SQL Where Clause with CASE & NOT IN Condition - sql-server

I have a select query with where Clause. Now I have to add additional condition in where clause based on user access. If user does not have access then need to include additional condition in where clause, else if user have access then there is no additional logic.
For example:
Select * from TableA where ID > 100
Additional Logic:
If user does not have access to Admin Page then #X= 0 and if does not have access to External Page then #Y = 0.
I need to include the below logic in where clause:
if (#X = 0 and #y=0) then
pagecode not in ('admin','external')
else if(#x=0 and #y=1) then
pagecode not in ('admin')
else if(#x=1 and #y=0) then
pagecode not in ('external')
else
no additional logic in where clause
How to implement this using case in where clause.

You can use CASE in WHERE as Shown below:
WHERE 1=(
CASE WHEN #X = 0 and #y = 0 and pagecode not in ('admin','external') THEN 1
WHEN #x = 0 and #y = 1 and pagecode not in ('admin') THEN 1
WHEN #x = 1 and #y = 0 and pagecode not in ('external') THEN 1
ELSE 0 END
)
this will not return any row if #x=1 and #y=1.
If you want to return all rows if #x=1 and #y=1
WHERE 1=(
CASE WHEN #X = 0 and #y = 0 and pagecode not in ('admin','external') THEN 1
WHEN #x = 0 and #y = 1 and pagecode not in ('admin') THEN 1
WHEN #x = 1 and #y = 0 and pagecode not in ('external') THEN 1
WHEN #x = 1 and #y = 1 THEN 1
ELSE 0 END
)

Pretty sparse on details here. The ELSE is pretty vague but I was assuming that both variables would equal 1. But we don't even know the datatype of those so not totally sure. My guess is something like this.
where
(
#X = 0
and
#y = 0
and pagecode not in ('admin','external')
)
OR
(
#x = 0
and
#y = 1
and
pagecode not in ('admin')
)
OR
(
#x = 1
and
#y = 0
and
pagecode not in ('external')
)
OR
(
#x = 1
and
#y = 1
)
Be warned. This approach can have some serious performance problems. Gail Shaw has written about this here and a follow up here. You can also read Erland Sommarskog's article here

Maybe this:
WHERE ID > 100
AND ((pagecode != 'admin' and X = 0) or X= 1)
AND ((pagecode != 'external' and Y = 0) or Y= 1)

Since the question asks
How to implement this using case in where clause.
I thought I'd do a little experimentation. After discovering some interesting limitations, I found a way.
You should be able to adapt this successful experiment:
SELECT 'boom'
WHERE 'a' NOT IN (
SELECT CASE WHEN 1=0 THEN 'a' ELSE '' END
UNION ALL
SELECT CASE WHEN 1=1 THEN 'b' ELSE '' END
)
Note the ELSE '' is important. Without an ELSE the CASE will supply a NULL for that row, and that will mess up your IN() condition.
By the way, this isn't how I would look to solve your actual problem, but it does answer the question, so if you really need to use a CASE expression for some reason, this is how it can be done.

Related

Visual Studio debugging stored procedure , focus on T-SQL CASE WHEN line by line

Discovering Visual Studio's ability to step debug on SQL stored procedures and UDF's I want to ask the following: is there any way to step debug between an inner query of a function (line by line) and specifically between a case when of a T-SQL, in order to be sure which one of all conditions is executed with a specific parameter of #Customercode ?
I know that 99% will be "no!" and I suppose that an answer to this would be "Remove the cases from the inner of the query and put them in terms of if ... then of the function... but it seems hard for me to believe that there is no way to do something like this even if an alternative method..
Sometimes you have to debug something that is already written in terms of CASE WHEN of the inner query..
The following is a very small example but what if the was a huge query ? There is no way to debug an SQL query line by line and get SQL's definitions of what is SQL doing with your statement ?
CREATE FUNCTION [dbo].[Fn_SalutSmallName]
(#CustomerCode varchar(50))
RETURNS varchar(100)
AS
BEGIN
DECLARE #res Varchar(100)
BEGIN
SELECT
#res = CASE
WHEN ESGOPERSON.PersonKind = 0 THEN 'Mr/Mrs'
WHEN ESGOPERSON.PersonKind = 1 AND ESGOPerson.SEX = 1
THEN 'Dear Mr ' + ISNULL(ESGOZCASES.Vocative, '')
WHEN ESGOPERSON.PersonKind = 1 AND ESGOPerson.SEX = 2
THEN 'Dear Mrs ' + ISNULL(ESGOZCASES.Vocative, '')
WHEN ESGOPERSON.PersonKind = 1 AND ESGOPerson.SEX = 0 AND ESGOPERSON.fTitleCode = 'Κος' AND ESGOZCASES.Vocative IS NULL
THEN 'Mr'
WHEN ESGOPERSON.PersonKind = 1 AND ESGOPerson.SEX = 0 AND ESGOPERSON.fTitleCode = 'Κα' AND ESGOZCASES.Vocative IS NULL
THEN 'Mrs'
WHEN ESGOPERSON.PersonKind = 1 AND ESGOPerson.SEX = 0 AND ESGOPERSON.fTitleCode = 'Κος' AND ESGOZCASES.Vocative IS NOT NULL
THEN 'Dear Mr ' + ISNULL(ESGOZCASES.Vocative, '')
WHEN ESGOPERSON.PersonKind = 1 AND ESGOPerson.SEX = 0 AND ESGOPERSON.fTitleCode = 'Κα' AND ESGOZCASES.Vocative IS NOT NULL
THEN 'Dear Mrs ' + ISNULL(ESGOZCASES.Vocative, '')
ELSE 'Dear'
END
FROM
ESFITradeAccount
LEFT OUTER JOIN
ESGOPerson ON ESFITradeAccount.fPersonCodeGID = ESGOPerson.GID
LEFT OUTER JOIN
ESGOZCases ON ESGOPerson.FirstName = ESGOZCASES.KeyID
AND ESGOPerson.Sex = ESGOZCases.Gender
WHERE
ESFITradeAccount.Code = #CustomerCode
AND ISNULL(ESGOZCases.KeyType, 1) = 1
RETURN #res
END
END

SQL Server - WHERE clause with CASE

ALTER PROCEDURE GetVendor_RMA_CreditMemo
(#HasCreditMemoNo INT)
BEGIN
SELECT
*
FROM
(SELECT
CreditMemoNumber,
CASE WHEN CreditMemoNumber != ''
THEN 1
ELSE 0
END AS HasCreditMemoNo
FROM
XYZ) as C
WHERE
(C.HasCreditMemoNo = #HasCreditMemoNo OR #HasCreditMemoNo = -1)
END
CreditMemoNumber is a varchar column
I want to achieve this:
CASE
WHEN #HasCreditMemoNo = 0
THEN -- select all rows with no value in CreditMemoNumber Column,
WHEN #HasCreditMemoNo = 1
THEN -- all rows that has some data,
WHEN #HasCreditMemoNo = -1
THEN -- everything regardless..
You can't do this kind of thing with a CASE.
The correct way to do it is with OR:
WHERE (#HasCreditMemoNo = 0 AND {no value in CreditMemoNumber Column})
OR
(#HasCreditMemoNo = 1 AND {all rows that has some data})
OR
(#HasCreditMemoNo = -1)
Would this work for you? I'm not sure if it would improve your performance. You may be better off writing an if else if else statement and three separate select statements with an index on the CreditMemoNumber column.
ALTER PROCEDURE GetVendor_RMA_CreditMemo(#HasCreditMemoNo int)
BEGIN
select
CreditMemoNumber,
case when CreditMemoNumber != '' then 1 else 0 end as HasCreditMemoNo
from XYZ
where
(#HasCreditMemoNo = 0 and (CreditMemoNumber is null or CreditMemoNumber = ''))
or (#HasCreditMemoNo = 1 and CreditMemoNumber != '')
or (#HasCreditMemoNo = -1)
END

Case Statement in where clase in sql server 2014

I am creating a store procedure and in which am I stuck in a problem. I want to query two columns based on condition. If parameter is numeric then query to one column and if it is nonnumeric then query to other column. Following is the procedure.
$
declare #result AS varchar(50)
DECLARE #peopleId AS varchar(50)
if('232332' NOT LIKE '%[^0-9]%')
BEGIN
SET #result='Numeric'
PRINT #result
END
ELSE
BEGIN
set #result='nonNumeric'
print #result
END
select isnull(class.grade,'') as grade,ISNULL(class.room,'') as room,student.prefix as prefix,student.student_id as student_id,(person.first_name+' '+person.last_name) as name,
person.dob as dob,person.people_id as people_id,quit_on,
case when student.student_status='30' then
N'พักการเรียน'
when student.student_status='31' then
N'น.ร.ไปเรียนโครงการฯ'
else ''
end
as quit_reason from school_student student
inner join people_person person on student.person_id=person.id
left join school_classroom_students classStudent on classStudent.student_id=student.id
left join school_classroom class on class.id =classStudent.classroom_id
where student.student_status in('30','31') and student.system_status = 'DC' and student.school_id=#schoolId
AND case
WHEN
#result='nonNumeric' then-- this should execure
person.people_id=#peopleId
else---- this should work
person.first_name+' '+ person.last_name LIKE '%'+#peopleId+'%'
Please help me out on this
Why would use use a separate variable? You can do:
WHEN (person.people_id = try_convert(int, #peopleId) or
try_convert(int, #peopleId) is null and
person.first_name + ' ' + person.last_name LIKE '%' + #peopleId + '%'
)
I question why you are passing a value that is used for both a string and numeric comparison. If I were using a variable, I would do:
declare #personid int;
declare #personname varchar(255);
if #peopleid not like '%[^0-9]%'
set #personname = #peopleid;
else
set #personid = convert(int, #peopleid);
where (personid = #personid or
person.first_name + ' ' + person.last_name LIKE '%' + #personname + '%'
)
The code just seems easier to follow.
Since SQL Server doesn't treat results of CASE expressions as booleans, you have to add an extra comparison. The way to do that is like this:
WHERE 1 = CASE WHEN x THEN 1 WHEN y THEN 0 ELSE 1 END
Conditions which result in rows being included in the result must evaluate to 1, and conditions which don't, must evaluate to something other than 1 (like 0). So the whole CASE expression returns either 0 or 1, and that is compared to 1.
In your code, it would look like this:
AND 1 = case
WHEN
#result='nonNumeric' then case when person.person_id = #peopleId then 1 else 0 end
else when person.first_name+' '+person.last_name LIKE '%'+#peopleId+'%' then 1 else 0 end
end
I added the END.
Just do like that
DECLARE #IsNumeric INT = NULL
DECLARE #IsNotNumeric INT = NULL
DECLARE #peopleId Varchar(50)
SET #peopleId = '123'
IF ISNUMERIC(#peopleId) = 1
BEGIN
SET #IsNumeric = 1
END
ELSE
BEGIN
SET #IsNotNumeric = 1
END
IN WHERE Condition Just Check
AND (#IsNumeric IS NULL OR CONVERT(VARCHAR(500),person.people_id)=#peopleId)
AND (#IsNotNumeric IS NULL OR person.first_name+' '+ person.last_name LIKE '%'+#peopleId+'%')

SQL Server TSQL - How to achieve "NOT IN" statements within a CASE

I have the following working code:
INSERT INTO #resultado
SELECT 1,
v.idReqVoucher,
v.dtSolicitacao,
v.idFuncionario_solicitante,
v.idFuncionario_beneficiario,
v.idStatus,
NULL as valor
FROM reqVoucher v
WHERE
v.idReqVoucher =
CASE WHEN #idRequisicao = 0 THEN v.idReqVoucher
ELSE #idRequisicao END
AND v.idStatus =
CASE WHEN #status = 0 THEN v.idStatus
ELSE #status END
AND v.dtSolicitacao >= CASE WHEN #dtSolicitacaoIni IS NULL THEN v.dtSolicitacao ELSE #dtSolicitacaoIni END
AND v.dtSolicitacao <= CASE WHEN #dtSolicitacaoFim IS NULL THEN v.dtSolicitacao ELSE #dtSolicitacaoFim+' 23:59:59' END
But what I need to achieve is something like that:
AND v.idStatus
CASE WHEN #status = 99 THEN NOT IN (5,1,4,20)
ELSE WHEN #status != 0 THEN = #status END
And I have no idea on how achieve that in my code. I'm fairly new in TSQL and SQL Server, so please be gentle.
Or use a CASE expression:
and case
when #status = 99 and v.idStatus not in ( 5, 1, 4, 20 ) then 1
when #status !=0 and v.idStatus = #status then 1
else 0
end = 1
The CASE expression returns a value which you must then use, e.g. by comparing it to 1. It is generally good practice to include an ELSE clause to supply a default value should the unexpected arise.
The CASE statement returns a value, it does not act as an IF statement by changing the SQL query (if this, then do that). You would need to modify your where statement to something like the following:
AND (
(#status = 99 AND v.idStatus NOT IN (5, 1, 4, 20))
OR (#status NOT IN (0, 99) AND v.idStatus = #status)
)
Edit: Commenter is correct, the 2nd check needs to ensure the #status is not 99.
This should be equivalent to what you want:
AND (
(#status = 99) AND (v.idStatus NOT IN (5,1,4,20))
OR
(#status <> 99) AND (#status <> 0) AND (v.idStatus = #status)
)
CASE expression can only be used to return a scalar value, so it cannot be used to return a predicate like NOT IN (5,1,4,20).

CASE statement inside a WHERE clause in SQL Server

I am trying to add a CASE statement, but I need an OR within it and I am having a little trouble.
I'm not sure how I can do this, but I was hoping someone could help. Here is what I am trying to do:
SELECT *
FROM Table1
WHERE IsUpdate = CASE WHEN #Type = 'Yes' THEN 1 ELSE (0 OR 1) END
So basically, I want to select only the rows that have IsUpdate set to 1 when #Type = 'Yes', otherwise I want to select the rows where IsUpdate = 0 OR IsUpdate = 1. Any ideas?
You don't need a CASE, i assume that the value can only be 0 or 1:
SELECT * FROM Table1
WHERE #Type <> 'Yes' OR IsUpdate = 1
If this is sitting in a stored-procedure it's probably better to use a If-Else instead of the parameter-check since above query is non-sargable so it might be inefficient on a large table.
The full where clause that matches your logic is:
where (#Type = 'Yes' and IsUpdate = 1) or
(#Type <> 'Yes' and IsUpdate in (0, 1))
You can simplify this, if you know something about the values in the columns. For instance, if IsUpdate only takes on the values 0 and 1 (and not NULL):
where #Type <> 'Yes' or IsUpdate = 1

Resources