SQL - Different WHERE clause dependant upon variable - sql

I'm using a variable to get my SELECT to run in 2 different "modes":
DECLARE #Mode as Varchar(1) = 'A'
SELECT
CASE
WHEN #Mode = 'A'
THEN FieldABC * 2
ELSE FieldABC * 3
END AS FieldABC,
CASE
WHEN #Mode = 'A'
THEN FieldDEF
ELSE FieldGHI
END AS FieldJKL
WHERE
FieldABC BETWEEN 0 AND 100
When I want to run the "B" version of the SELECT, I just have to change the variable value. This all works fine.
I now want to change the WHERE clause so it too is dependent upon the variable:
IF #Mode = 'A' THEN
WHERE FieldABC > 0
ELSE
WHERE FieldABC < 0
END IF
Except, this syntax is totally nonsense in SQL!
How can I achieve the 2 different flavours of my WHERE clause dependent upon the same variable - or some other simple way of switching the SELECT between the 2 different modes?

You can do this with a combination of AND and OR :
WHERE (#Mode='A' AND FieldABC > 0)
OR (#Mode='B' AND FieldABC < 0);
Or with a CASE expression:
WHERE CASE
WHEN #Mode = 'A' AND FieldABC > 0 THEN 1
WHEN #Mode = 'B' AND FieldABC < 0 THEN 1
ELSE 0
END = 1;

Related

Dynamic Case Expression Based on Dates

I have a SQL query that has multiple conditions that will change the WHERE clause. I have three case expressions that dynamically update this WHERE clause. The last two case expressions are working well. However, the first one is more dynamic than the last two and I am having issues with it.
Here is the portion of the table that is applicable to this exercise
(Image of Table)
I need it to work as follows:
When my variable #selection = 1, I need it to sort my return based on two dates being passed in.
When my variable #selection = {any other int}, I need it to return all dates.
Query with Description
...
WHERE
{Start Date} = (case when #selection = 1 then {return all values where Start Date is between two dates that are passed in} else {return all Start Dates})
...
Entire Query
DECLARE #selection integer ;
DECLARE #items integer ;
DECLARE #washTypes integer ;
SET #selection = {Root Container.Selection Group.Selection Checkbox.controlValue} ;
SET #items = {Root Container.Items Group.Items Checkbox.controlValue} ;
SET #washTypes = {Root Container.Types of Wash Group.Wash Types Checkbox.controlValue} ;
SELECT
RAW_CIP_records_ndx as 'Index',
start as 'Start',
stop as 'End',
total_duration as 'Total Duration',
item as 'Item',
wash_type as 'Type of Wash',
operator as 'CIP Operator',
program_complete as 'Program Fully Completed?'
FROM RAW_CIP_records
WHERE
start = (case when #selection = 1 then (BETWEEN '{Root Container.Start Date.formattedDate}' and '{Root Container.End Date.formattedDate}') else start end)
and
item = (case when #items = 0 then 'Receiving Bay 1' when #items = 1 then 'Receiving Bay 2' when #items = 2 then 'Receiving Bay 3' else item end)
and
wash_type = (case when #washTypes = 0 then 'Regular' when #washTypes = 1 then 'Sanitize' when #washTypes = 2 then 'Acid' else wash_type end)
I can get a simple WHERE clause to work with these two dates and the BETWEEN function. However, I can't figure out how to pass all of this into a CASE expression.
You could do it a WHERE condition in one query
WHERE (
#selection = 1 AND (start BETWEEN #startDate and #endDate)
OR #selection <> 1
)
But I would suggest you are better off with two separate queries as it will be more likely to use an index.
IF #selection = 1
SELECT ...
ELSE
SELECT ...
Maybe something like this:
WHERE
1 = (case when #selection = 1 then
case when start BETWEEN '{Root Container.Start Date.formattedDate}' and '{Root Container.End Date.formattedDate}' then 1 else 0 end
else 1
end)
and
item = ...
Warning: this will most likely not make use of any indexes, so only use if performance is acceptable in your case.

input parameter used in case statement in where clause

I have the following simplified stored procedure where based on on the input parameter, I need to then do a case in the where clause. It will not execute as it says: Incorrect syntax near '='
PROCEDURE [dbo].[DataInfo]
#Allowactive BIT = 1
AS
BEGIN
Select * from tbl1 1
where (CASE #Allowactive
WHEN 0 then (t.Isactive = 1) END
AND isSubmitted = 1
END
your where clause will be like below
where
CASE #Allowactive
WHEN 0 then t.Isactive END =1
AND isSubmitted = 1
You shouldn't use parameters in a query like this, as it messes up the query plan. When the right plan to use changes depending on the parameter, you need separate queries, or to force SQL to always recompile.
So do this instead:
create or alter procedure [dbo].[DataInfo] #Allowactive bit = 1
as
begin
if #Allowactive = 0
begin
Select * from tbl1 1
where Isactive = 1
AND isSubmitted = 1
end
else
begin
select * from tbl1 1
where isSubmitted = 1
end
end
Instead run separate queries.
Try to run the following and see the results:
SELECT *
FROM Tbl1 AS T
WHERE CASE #Allowactive
WHEN 0 THEN 1 ELSE #Allowactive END = T.Isactive
AND
isSubmitted = 1;
If you have 2012+ version then you could also do:
SELECT *
FROM Tbl1 AS T
WHERE IIF(#Allowactive = 0, 1, #Allowactive) = T.Isactive;
It seems Zaynul Abadin Tuhin directly answers your question.
But, I believe a case statement complicates what you want to achieve.
I think a query like this satisfies your desired outcome:
PROCEDURE [dbo].[DataInfo]
#Allowactive BIT = 1
AS
BEGIN
SELECT * FROM tbl1 t
WHERE (#Allowactive = 1 OR (#Allowactive = 0 AND t.Isactive = 1))
AND t.isSubmitted = 1
END

Choose multiple cases condition

I want to get multipule choises after then in case statment as
#value
select * from [dbo].[Currency_Tbl]
WHERE [Currency_Active_YN]=
CASE WHEN #value = 1 THEN
( 1 or 0)
ELSE
#Value = 0 then 0
END
it didn't accept the first line in col1 but accept the col2
how can I select multiple numbers after THEN?
You don't use case in where clauses. Use boolean logic
select * from [dbo].[Currency_Tbl]
WHERE (#value = 1 and [Currency_Active_YN] in (0,1))
OR (#value = 0 and [Currency_Active_YN] = 0)
You dont need a case to do what you're trying to do. Assuming Currency_Active_YN is a not null bit field the following logic should suffice.
select * from [dbo].[Currency_Tbl]
WHERE (#value=1 OR [Currency_Active_YN]=#Value)

T-SQL exactly 1 condition is true out of a set of conditions

What is the simplest way to express in T-SQL that only 1 and exactly 1 of a number of boolean conditions is true (needs to be usable in a CHECK constraint)?
XOR works for 2 conditons, eg A XOR B will insure that exactly 1 is set but it does not work for 3 conditions:
One solution would be to get some kind of collection out of them, filter on the conditions being true, perform and aggregate/sum and check that the result is equal to 1.
I would structure your CHECK along the lines of:
CHECK (
CASE WHEN <condition 1> THEN 1 ELSE 0 END +
CASE WHEN <condition 2> THEN 1 ELSE 0 END +
CASE WHEN <condition 3> THEN 1 ELSE 0 END
= 1
)
It's slightly verbose but it is hopefully readable to see what your intention was. It also extends to other similar requirements more easily than XOR (e.g. "exactly 2 out of 5 conditions must be matched" can follow the same structure)
For SQL Server 2012 or later, you can be slight more concise with IIF:
CHECK (
IIF(<condition 1>,1,0) +
IIF(<condition 2>,1,0) +
IIF(<condition 3>,1,0)
= 1
)
Shamelessly taken from this SO question, a general formula for an exclusive OR between three variables can be written as:
(a ^ b ^ c) && !(a && b && c)
We can express this in SQL Server as:
(A XOR B XOR C) AND NOT (A AND B AND C)
Note that this only works for three variables, and does not generalize to higher numbers. If you have more than three variables, you'll have to do more work.
Assuming that all your conditions are represented as BIT columns, you can have a constraint with the format:
alter table [table_name] add constraint [constraint_name]
check ( ( a ^ b ^ c ) = 1 AND NOT ( a & b & c ) = 1 )
Doing this, you can also then use the same conditions in a case statement, like:
select a, b, c,
case when (( a ^ b ^ c ) = 1 AND NOT ( a & b & c ) = 1) then 1
else 0 end
as true_or_false
from [table_name]
Putting this together, we can demo it with a script like:
create table #bits (a bit, b bit, c bit)
create table #bits2 (a bit, b bit, c bit)
alter table #bits2 add constraint ck_xor
check ( ( a ^ b ^ c ) = 1 AND NOT ( a & b & c ) = 1 )
insert into #bits
values
( 0, 0, 0 ), ( 0, 0, 1 ), ( 0, 1, 0 ), ( 0, 1, 1 ), ( 1, 0, 0 ), ( 1, 0, 1 ), ( 1, 1, 0 ), ( 1, 1, 1 )
select a, b, c,
case when ( a ^ b ^ c ) = 1 AND NOT ( a & b & c ) = 1 then 1
else 0 end
as true_or_false
from #bits
insert into #bits2
select * from #bits
where ( a ^ b ^ c ) = 1 AND NOT ( a & b & c ) = 1
-- the below line will fail because of the check constraint
insert into #bits2 (a,b,c) values (1,1,0)
select * from #bits2
drop table #bits
drop table #bits2
Declare three variable #a, #b , #c and it's bit type. Where 1 is true and 0 is false. Example with all possible of bit is here.
declare #a bit, #b bit, #c bit;
set #a=1; set #b=1; set #c=1; select #a as a,#b as b,#c as c, case when #a=#b then (case when #c=1 then 1 else 0 end) else (case when #c=0 then 1 else 0 end) end as xor;
set #a=1; set #b=1; set #c=0; select #a as a,#b as b,#c as c, case when #a=#b then (case when #c=1 then 1 else 0 end) else (case when #c=0 then 1 else 0 end) end as xor;
set #a=1; set #b=0; set #c=0; select #a as a,#b as b,#c as c, case when #a=#b then (case when #c=1 then 1 else 0 end) else (case when #c=0 then 1 else 0 end) end as xor;
set #a=1; set #b=0; set #c=0; select #a as a,#b as b,#c as c, case when #a=#b then (case when #c=1 then 1 else 0 end) else (case when #c=0 then 1 else 0 end) end as xor;
set #a=0; set #b=1; set #c=0; select #a as a,#b as b,#c as c, case when #a=#b then (case when #c=1 then 1 else 0 end) else (case when #c=0 then 1 else 0 end) end as xor;
set #a=0; set #b=1; set #c=0; select #a as a,#b as b,#c as c, case when #a=#b then (case when #c=1 then 1 else 0 end) else (case when #c=0 then 1 else 0 end) end as xor;
set #a=0; set #b=0; set #c=0; select #a as a,#b as b,#c as c, case when #a=#b then (case when #c=1 then 1 else 0 end) else (case when #c=0 then 1 else 0 end) end as xor;
set #a=0; set #b=0; set #c=0; select #a as a,#b as b,#c as c, case when #a=#b then (case when #c=1 then 1 else 0 end) else (case when #c=0 then 1 else 0 end) end as xor;
To reduce even more from the code duplication and make it a bit more readable something like this could be used:
(SELECT SUM(ExpressionValue)
FROM
(VALUES
(CASE WHEN 42 = 42 THEN 1 ELSE 0 END),
(CASE WHEN 42 = 42 THEN 1 ELSE 0 END),
(CASE WHEN 42 = 1 THEN 1 ELSE 0 END),
(CASE WHEN 42 = 42 THEN 1 ELSE 0 END)
) AS conditions(ExpressionValue))
=
1
Example usage:
DECLARE #say as VARCHAR(MAX) =
CASE
WHEN (
(SELECT SUM(ExpressionValue)
FROM
(VALUES
(CASE WHEN 42 = 42 THEN 1 ELSE 0 END),
(CASE WHEN 42 = 42 THEN 1 ELSE 0 END),
(CASE WHEN 42 = 1 THEN 1 ELSE 0 END),
(CASE WHEN 42 = 42 THEN 1 ELSE 0 END)
) AS conditions(ExpressionValue))
=
1
) THEN 'Only one set'
ELSE 'Non only one set'
END
PRINT #say
It could still be improved if it is somehow possible to elegantly just apply the case operation similar to a map.
declare #tt table (i int, b1 bit, b2 bit, b3 bit);
insert into #tt values (1,0,0,0), (2,1,1,1), (3,1,0,0)
select i, b1, b2, b3
from #tt
where cast(b1 as tinyint) + cast(b2 as tinyint) + cast(b3 as tinyint) = 1

Use comparison signs inside a sql case statement

I'm looking for a way to build case statements in a sql select query using less than and greater than signs. For example, I want to select a ranking based on a variable:
DECLARE #a INT
SET #a = 0
SELECT CASE
WHEN #a < 3 THEN 0
WHEN #a = 3 THEN 1
WHEN #a > 3 THEN 2
END
I'd like to write it as:
DECLARE #a INT
SET #a = 0
SELECT CASE #a
WHEN < 3 THEN 0
WHEN 3 THEN 1
WHEN > 3 THEN 2
END
...but SQL doesn't let me use the < and > signs in this way. Is there a way that I can do this is SQL 2005, or do I need to use the code like in the first one.
The reason for only wanting the code there once is because it would make the code a lot more readable/maintainable and also because I'm not sure if SQL server will have to run the calculation for each CASE statement.
I'm looking for a VB.NET case statement equivelent:
Select Case i
Case Is < 100
p = 1
Case Is >= 100
p = 2
End Select
Maybe it's not possible in SQL and that's ok, I just want to confirm that.
You can use the SIGN function as
DECLARE #a INT
SET #a = 0
SELECT CASE SIGN(#a - 3)
WHEN -1 THEN 0
WHEN 0 THEN 1
WHEN 1 THEN 2
END
If #a is smaller than 3, then #a - 3 results in a negative int, in which SIGN returns -1.
If #a is 3 or greater, then SIGN returns 0 or 1, respectively.
If the output you want is 0, 1 and 2, then you can simplify even more:
DECLARE #a INT
SET #a = 0
SELECT SIGN(#a - 3) + 1
Using SIGN as suggested by #Jose Rui Santos seems a nice workaround. An alternative could be to assign the expression an alias, use a subselect and test the expression (using its alias) in the outer select:
SELECT
…,
CASE
WHEN expr < 3 THEN …
WHEN expr > 3 THEN …
END AS …
FROM (
SELECT
…,
a complex expression AS expr
FROM …
…
)
SELECT
CASE
WHEN ColumnName >=1 and ColumnName <=1 THEN 'Fail'
WHEN ColumnName >=6 THEN 'Pass'
ELSE 'Test'
END
FROM TableName