Dynamic WHERE clauses in SQL Query [closed] - sql

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I want to execute this query
select * from locations where region=1 or region=2
but in certain cases I don't want to include the second clause in the WHERE (or region=2)
Is there a way to conditionally run the or statement?

Check for the condition in the where statement
select * from locations
where region=1
or (region=2 and #checkRegion2=1)

If you want genralizable query, you can declare table variable for this (I assuming you're using SQL server):
declare #regions table (id int primary key)
and then fill it depending on condition:
insert into #regions select 1
if #use2 = 1
insert into #regions select 2
now you can run this query:
select *
from location as l
where exists (select * from #regions as r where r.id = l.region)
or
select *
from location as l
where l.region in (select r.id from #regions as r)

DECLARE #RUNAFTERBIT VARCHAR(10)
SET #RUNAFTERBIT = 'TRUE'
IF #RUNAFTERBIT = 'TRUE'
SELECT * FROM LOCATIONS WHERE REGION = 1 OR REGION = 2
ELSE
SELECT * FROM LOCATIONS WHERE REGION = 1
END IF

You can use dynamic SQL:
DECLARE #Query VARCHAR(MAX) = 'SELECT * FROM locations WHERE region=1 '
IF(#RUNAFTERBIT = 'TRUE')
BEGIN
SET #Query = #Query + ' OR REGION = 2'
END
EXEC(#Query)

A CASE expression can be used to perform short circuit evaluation under some circumstances:
declare #AlternateRegion as Int = 2;
select *
from Locations
where case
when Region = 1 then 1
when #AlternateRegion is not NULL then
case when Region = #AlternateRegion then 1 else 0 end
else 0
end = 1
CASE does not reliably provide short circuit evaluation in the presence of aggregation functions. See CASE / COALESCE won't always evaluate in textual order
and Aggregates Don't Follow the Semantics Of CASE
.

Related

SQL query for printing n rows [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I need a query which prints rows by the n number represented against it. example
You haven't specified your DBMS, the following solution will work for PostgreSQL:
select t.nr
from generate_series(1,4) as t(nr)
cross join generate_series(1,t.nr)
order by t.nr;
With 100% standard ANSI SQL, you can do this:
with recursive numbers (nr) as (
select * from (values (1) ) as t
union all
select p.nr + 1
from numbers p
where p.nr + 1 <= 4
)
select t.*
from numbers t
join numbers t2 on t2.nr <= t.nr
order by t.nr;
SQL Server
declare #n int = 5; // your n value
declare #i int = 1;
declare #j int = 1;
declare #tmp table (val int)
WHILE (#i <= #n)
BEGIN
WHILE (#j <= #i)
BEGIN
insert into #tmp(val) values(#i)
set #j = #j + 1;
END
set #j = 1;
set #i = #i + 1;
END
select * from #tmp

Adding dynamic condition in where clause

I want to add condition in 'where' clause depends upon 'if' condition like below
Declare #strSQLClause varchar(50)
If (#QMasterCompanyId='09' and #State='FL' and (#LOB='HO' or #LOB='HP'))
Begin
Declare #strMonthsOccupied char(1)
select Distinct #strMonthsOccupied=sm.MonthsOccupiedDesc from HOStructureRating sr
join HOSeleMonthsOccupied sm on sr.MonthsOccupied=sm.MonthsOccupiedCd
where AppId=#AppId
If(CONVERT(int,LTRIM(RTrim(#strMonthsOccupied))) > 9)
Begin
set #strSQLClause ='AND QuestionCd!=Q8'
End
Else
set #strSQLClause =''
End
so that in my Query will work as
select * from SHSeleQuestions where MasterCompanyId='09' + #strSQLClause
But this approach is not working fine, can anyone please help me on this.
There are two ways to do this one is use dynamic sql or other one is below mention :
select *
from SHSeleQuestions
where MasterCompanyId='09' AND
1 = CASE WHEN LEN(#strSQLClause) > 0 AND QuestionCd != 'Q8' THEN 1
WHEN LEN(#strSQLClause) = 0 THEN 1 END
Using Dynamic SQL
EXEC('select * from SHSeleQuestions where MasterCompanyId=''09''' + #strSQLClause ')
You would need to use dynamic SQL, but why not just have two statements that execute SQL, so rather than set #strSQLClause = 'AND ...', simply have a select statement here with the condition(s) you need
IF (#QMasterCompanyId='09' AND #State='FL' AND (#LOB='HO' OR #LOB='HP'))
BEGIN
DECLARE #strMonthsOccupied CHAR(1)
SELECT DISTINCT #strMonthsOccupied = sm.MonthsOccupiedDesc
FROM HOStructureRating sr
INNER JOIN HOSeleMonthsOccupied sm
ON sr.MonthsOccupied=sm.MonthsOccupiedCd
WHERE AppId=#AppId;
IF(CONVERT(INT,LTRIM(RTRIM(#strMonthsOccupied))) > 9)
BEGIN
SELECT *
FROM SHSeleQuestions
WHERE MasterCompanyId='09'
AND QuestionCd!='Q8';
RETURN;
END
END
SELECT *
FROM SHSeleQuestions
WHERE MasterCompanyId='09';
That being said, there are so many issues with the above I don't really know where to begin. You declare your variable then assign it an indeterminate value:
DECLARE #strMonthsOccupied CHAR(1)
SELECT DISTINCT #strMonthsOccupied = sm.MonthsOccupiedDesc
FROM HOStructureRating sr
INNER JOIN HOSeleMonthsOccupied sm
ON sr.MonthsOccupied=sm.MonthsOccupiedCd
WHERE AppId=#AppId;
If the query returns multiple rows then there is no clear logic for which value the variable should be assigned. The nex issue is that this CHAR(1) variable is clearly a number based on your attempted conversion:
IF(CONVERT(INT,LTRIM(RTRIM(#strMonthsOccupied))) > 9)
Why not just cut out the middle man and declare an INT to begin with. The next point is that it is a CHAR(1) so isn't actually big enough to store anything greater than 9, so your above condition will never be true.
Even if sm.MonthsOccupiedDesc, was 10, the variable would simply be truncated to 1, which is smaller than 9, so fails the condition, e.g.
DECLARE #strMonthsOccupied CHAR(1) = '10';
IF(CONVERT(INT,LTRIM(RTRIM(#strMonthsOccupied))) > 9)
PRINT 'TRUE';
ELSE
PRINT 'FALSE';

How to filter out a row from a query if the table contains another row with a certain value

We have a question and answer table. Tables and contents are below, answers are kept per user.
Answer table:
FKQID Answ UserID
1 Y 1
2 Y 2
Question table:
ID Question
1 Do you agree to x and y?
2 Do you agree to x, y and z?
We want to change the Q 1's question but we can't change the existing row's question because we wan't to keep historical data about which users answered the question before it's changed. We have a stored procedure that returns the questions and their answers given by the user:
CREATE PROCEDURE spGetUserQuestionAndAnswers
#UserID int
AS
BEGIN
DECLARE #temp Table
(
QID int,
Question nvarchar(200),
Answer nvarchar(200)
)
INSERT INTO #temp
SELECT * FROM
(
Select q.ID as QID,
Ques,
(
SELECT Answ FROM Answer
WHERE Answer.UserID = #UserID
AND q.ID = FKQID
) As Ans
FROM Question q
) qq
--WHERE 1 = CASE WHEN QID = 1 AND Ans IS NULL THEN 0 ELSE 1 END --Can't use this 'cos too slow
--If I filter #temp with the where clause above then I can do something like
--this below but it slows down the above query too much
--DECLARE #1WasAnsweredBefore
--SET #1WasAnsweredBefore = CAST(SELECT COUNT(*) FROM #temp WHERE QID = 1 AS bit)
--SELECT * FROM #temp t
--WHERE 1 = CASE WHEN QID = 2 AND #1WasAnsweredBefore THEN 0 ELSE 1 END
SELECT * FROM #temp t
WHERE 1 = CASE WHEN QID = 1 AND t.Answer IS NULL THEN 0 ELSE 1 END
--AND IF #temp has QID = 1, THEN Exclude QID = 2 ????
END
This returns the questions and answers for the user. If the user is answering the questions for the 1st time then answers are NULL, which is fine.
What I need to do is if QID = 1 was not answered by the user before (Ans IS NULL) then show Q 2 to the user, which I filtered out in the last WHERE. What I can't do is if the result set includes Q 1, meaning it was answered by the user before, then filter out Q 2. Commented out lines in the SP is what I tried but if I put the WHERE filter on the SELECT which does the insert, then the query is too slow. Another solution I can think of is run the filter on #temp, do another insert from #temp to a #temp2 and exclude Q 2 if Q 1 exists in #temp but it feels a bit too much, there's probably a solution without using 2 temp tables.
So how can I exclude Q 2 from the result set if result set includes Q 1?
Edit: I included sample data above, that's enough data to explain the issue.
calling
exec spGetUserQuestionAndAnswers 2
returns
QID Question Answer
2 Do you agree to x, y and z? Y
which is good. User 2 has not answered Q 1 before so the SP filters out Q1 and returns Q2
calling
exec spGetUserQuestionAndAnswers 1
returns
QID Question Answer
1 Do you agree to x and y? Y
2 Do you agree to x, y and z? NULL
This is not fine, it shouldn't include Q2 because user has previously answered Q 1.
This is how I solved it but if you have a solution that doesn't use #temp2, or an easier solution, please let me know.
ALTER PROCEDURE spGetUserQuestionAndAnswers
#UserID int
AS
BEGIN
DECLARE #temp Table
(
QID int,
Question nvarchar(200),
Answer nvarchar(200)
)
INSERT INTO #temp
SELECT * FROM
(
Select q.ID as QID,
Ques,
(
SELECT Answ FROM Answer
WHERE Answer.UserID = #UserID
AND q.ID = FKQID
) As Ans
FROM Question q
) qq
DECLARE #temp2 Table
(
QID int,
Question nvarchar(200),
Answer nvarchar(200)
)
Declare #Q1Exists bit
SELECT #Q1Exists = CAST((SELECT Count(*)
FROM #temp
WHERE QID = 1 AND Answer IS NOT NULL) as bit)
insert into #temp2
SELECT * FROM #temp t
WHERE 1 = CASE WHEN QID = 1 AND t.Answer IS NULL THEN 0
ELSE 1 END
SELECT * FROM #temp2
WHERE 1 = CASE WHEN QID = 2 THEN ~#Q1Exists
ELSE 1 END
END
select max(ID)
from question
left join answer
on answer.FKQID = question.ID
and answer.Answ <> null
and answer.UserID = #UserID
where answer.FKQID is null

Quicker way to update all rows in a SQL Server table

Is there a more efficient way to write this code? Or with less code?
SELECT *
INTO #Temp
FROM testtemplate
Declare #id INT
Declare #name VARCHAR(127)
WHILE (SELECT Count(*) FROM #Temp) > 0
BEGIN
SELECT TOP 1 #id = testtemplateid FROM #Temp
SELECT TOP 1 #name = name FROM #Temp
UPDATE testtemplate
SET testtemplate.vendortestcode = (SELECT test_code FROM test_code_lookup WHERE test_name = #name)
WHERE testtemplateid = #id
--finish processing
DELETE #Temp Where testtemplateid = #id
END
DROP TABLE #Temp
You can do this in a single UPDATE with no need to loop.
UPDATE tt
SET vendortestcode = tcl.test_code
FROM testtemplate tt
INNER JOIN test_code_lookup tcl
ON tt.name = tcl.test_name
You could try a single update like this:
UPDATE A
SET A.vendortestcode = B.test_code
FROM testtemplate A
INNER JOIN test_code_lookup B
ON A.name = B.test_name
Also, the way you are doing it now is wrong, since you are taking a TOP 1 Id and a TOP 1 name in two separate querys, without an ORDER BY, so its not sure that you are taking the right name for your ID.
You could write a function to update vendortestcode. Then your code reduces to one SQL statement:
update testtemplate set vendortestcode = dbo.get_test_code_from_name(name)

Stored procedure returns inaccurate data [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
For the [Submitted On] column, when #fiscalyear is selected, I want only data ending that fiscal year.
For example if #fiscalyear = 2011 I want the data with [Submitted On] column ending with only the 2011 or null and likewise for the other years which is 2010 and 2012. Right now the problem is when i select 2012 0r 2010, I still get data with dates ending in 2011. Any ideas? And thanks to Aaron for the hint - my script looks different now.
#FiscalYear int,
#SchoolID int,
#Status int
AS
BEGIN
SET NOCOUNT ON;
declare #intCount int
declare #sqlstr nvarchar(2000)
set #intCount = 0
Select #intCount = Count(*)
From EnrollmentDateSchool Ed Right Outer Join
(select FP.FiscalYear, PrivateSchool.* from PrivateSchool
INNER JOIN FiscalYearPrivateSchool FP ON PrivateSchool.PrivateSchoolID
= FP.PrivateSchoolID) PS ON Ed.PrivateSchoolID = PS.PrivateSchoolID
Left Outer Join
Finance.dbo.Person P ON Ed.CreatedBy = P.PersonID
WHERE FiscalYear=#FiscalYear AND PS.IsActive=1
AND (#SchoolID = -1 OR SchoolID=#SchoolID)
AND ( (#Status = -1)
OR (#Status=1 AND PS.PrivateSchoolID = Ed.PrivateSchoolID)
OR (Ed.PrivateSchoolID is null) )
IF #intCount > 0
BEGIN
Select
[SchoolName] As [School Name],
Status = CASE WHEN PS.PrivateSchoolID = Ed.PrivateSchoolID
THEN 'Submitted'
ELSE 'Not Submitted'
END,
[Submitted By] = CASE WHEN PS.PrivateSchoolID = Ed.PrivateSchoolID
THEN [FirstName] + ' ' + [LastName]
ELSE NULL
END,
[Submitted On] = CASE WHEN PS.PrivateSchoolID = Ed.PrivateSchoolID
THEN Convert( Varchar(10), Ed.CreatedDate, 101 )
ELSE NULL
END
From EnrollmentDateSchool Ed Right Outer Join
(select FP.FiscalYear, PrivateSchool.*
from PrivateSchool INNER JOIN
FiscalYearPrivateSchool FP ON
PrivateSchool.PrivateSchoolID = FP.PrivateSchoolID) PS ON
Ed.PrivateSchoolID = PS.PrivateSchoolID Left Outer Join
Finance.dbo.Person P ON Ed.CreatedBy = P.PersonID
END
Else Select 'No Data Found' as 'School Roster Certification Report'
END
Selecting COUNT(*) and then comparing that result against 0 means that the server has to do all of the work to retrieve the entire result set, even if it immediately found a matching row. You could replace this with an EXISTS test directly in your if statement, e.g.:
IF EXISTS (SELECT * FROM /* Current Query */)
BEGIN
END
Alternatively, depending on how your consuming the results from this stored proc (I'd imagine it already has to do some clever stuff, given that the two possible result sets from results available or not available have different shapes) would be to just have your inner query (appropriately modified), and then add this below it:
IF ##ROWCOUNT=0
BEGIN
Select 'No Data Found' as 'School Roster Certification Report'
END
##ROWCOUNT:
Returns the number of rows affected by the last statement.
You don't have any of your where clause from the #intCount query in your select statement after that. If you copy your predicates into that query as well (or at least the Year part), it should limit your results.