Case Statement in Where Clause NULL case [duplicate] - sql

This question already has answers here:
CASE in WHERE, SQL Server
(5 answers)
Closed 4 years ago.
I'm creating a query (to eventually be used in a stored procedure) with multiple variations on what criteria is entered in a form. Sometimes there can be an entry, sometimes not. Sometimes the data field has a value, sometimes it's NULL.
The fields in my form are NAME, SSN, and DRLICENSE.
DECLARE #name VARCHAR(30);
DECLARE #ssn VARCHAR(10);
DECLARE #drlic VARCHAR(10);
--(if for example, someone enters data in two of the fields like this...)
SET #name = 'SMITH'
SET #drlic = 'D'
(In stored procedure)
SET #name = #name + '%'
SET #ssn = #ssn + '%'
SET #drlic = #drlic + '%'
SELECT
NAME,
SSN,
DRLICENSE
FROM
TABLE
WHERE
NAME LIKE CASE WHEN LEN(#name) > 1 THEN #name ELSE NAME END
AND SSN LIKE CASE WHEN len(#ssn) > 1 THEN #ssn ELSE SSN END
AND DRLICENSE LIKE CASE WHEN LEN(#drlic) > 1 THEN #drlic ELSE DRLICENSE END
The idea behind my case statement is to check the variable for usage and perform a like if the name, ssn, or drlicense are partial entries.
My question is: how do I account for the case of NULL in the table column (i.e. SSN LIKE SSN does not work when SSN is NULL because SSN IS NULL needs to be there).

This isn't going to perform well, especially since you are using LIKE but parentheses are important here
where ((#name is null or name like '%' + #name + '%') or ( name is null))
and ((#ssn is null or ssn like '%' + #ssn + '%') or (ssn is null))
and ((#drlic is null or DRLICENSE like '%' + #drlic + '%') or (DRLICENSE is null))
This will return the NULL values in those columns regardless if a parameter is passed in or not. If you don't want the NULL then just remove the or ( name is null)) section from each line. I wasn't certain from your post.

if length(#foo) <= 1
set #foo = NULL
if length(#bar) <= 1
set #bar = NULL
...
WHERE (#foo is NULL or t.foo = #foo)
AND (#bar is NULL or t.bar like #bar)
OPTION (RECOMPILE)

This should do it:
WHERE (#name IS NULL OR name LIKE #name + '%')
AND (#ssn IS NULL OR ssn LIKE #ssn + '%')
AND (#drlic IS NULL OR drlicense LIKE #drlic + '%')
Note that if, for example, user searched for name = foo then it will return rows that match foo% and not rows that have NULL name.

You can inline adding the wild card to the parameter value and add your null checks inline as well. There is no need for a CASE statement either. For each of the parameter/column values the result returns true if the parameter is null or if the column value is null or if the column value starts with the passed in parameter value.
Where statement:
WHERE (Name IS NULL OR #name IS NULL OR Name LIKE #name + '%')
AND (SSN IS NULL OR #ssn IS NULL OR SSN LIKE #ssn + '%')
AND (DRLICENSE IS NULL OR #drlic IS NULL OR DRLICENSE LIKE #drlic + '%')
The key here is to not do any filtering if the incoming value to use is null or if the column value is null which I assume you do not want filtered on if it is null.

You could use ISNULL() it's very useful in cases like yours.
SELECT
NAME,
SSN,
DRLICENSE
FROM
TABLE
WHERE
ISNULL(NAME,0) LIKE ISNULL(ISNULL(#name, NAME),0)
AND ISNULL(SSN,0) LIKE ISNULL(ISNULL(#ssn, SSN),0)
AND ISNULL(DRLICENSE,0) LIKE ISNULL(ISNULL(#drlic, DRLICENSE),0)
This method will replace the NULL values with the column value if the column value is NULL then both will be set to 0 (which will be 0=0, which will always be true).

If I understand correctly, you want a NULL-safe equality. That is, you want NULL to match NULL and values to match values. So:
where (name = #name or (name is null and #name is null)) and
(ssn = #ssn or (ssn is null and #ssn is null)) and
(drlicense = #drlicense or (drlicense is null and #drlicense is null))
or, an intriguing variant:
where (select count(*)
from (select distinct *
from (values (name, 'name'), (#name, 'name'), (ssn, 'ssn'), (#ssn, 'ssn'), (drlicense, 'drlicense'), (#drlicense, 'drlicense')
v(val, which)
) x
) = 3
This is using group by semantics to combine NULL values.
Or, if you want some obfuscation:
where nullif(name, #name) is null and nullif(#name, name) is null and
nullif(ssn, #ssn) is null and nullif(#ssn, ssn) is null and
nullif(drlicense, #drlicense) is null and nullif(#drlicense, drlicense) is null

Related

Stored procedure ignoring Nulls and fetch result for only entered parameters

I am creating a stored procedure to do a search through a table. I have many different search columns, all of which are optional. Is there a way to create a stored procedure that will handle this? Let's say I have a table with four columns ID, N1, N2 and N3. I could do something like this:
Table:
INSERT INTO [dbo].[Sample]
VALUES ('1', 'A,B,C', 'A,B,C', 'A,B,C'),
('2', 'B,D,N', 'B,D,N', 'B,D,N'),
('3', 'A,N,S', 'A,N,S', 'A,N,S'),
('4', 'S,F,G', 'S,F,G', 'S,F,G'),
('5', 'D,F,K', 'D,F,K', 'D,F,K'),
('6', 'S,H,Y', 'S,H,Y', 'S,H,Y'),
('7', 'Z,B,C', 'Z,B,C', 'Z,B,C')
Stored procedure:
CREATE PROCEDURE dbo.Sample2
#n11 varchar(max) = null,
#n12 varchar(max) = null,
#n21 varchar(max) = null,
#n22 varchar(max) = null,
#n31 varchar(max) = null,
#n32 varchar(max) = null
AS
BEGIN
SELECT COUNT(*)
FROM Sample
WHERE
(#n11 IS NULL OR Sample.N1 LIKE '%' + #n11 + '%'
OR #n12 IS NULL OR Sample.N1 LIKE '%' + #n12 + '%')
AND (#n21 IS NULL OR Sample.N2 LIKE '%' + #n21 + '%'
OR #n22 IS NULL OR Sample.N2 LIKE '%' + #n22 + '%')
AND (#n31 IS NULL OR Sample.N3 LIKE '%' + #n31 + '%'
OR #n32 IS NULL OR Sample.N3 LIKE '%' + #n32 + '%')
END
If user enters #n11 as A and leave the rest, since N1 contains A in 2 rows, output should be 2 but above query is providing 7. If a parameter is not specified, I need that to be ignored and pass the rest to where condition.
I think you need to AND every separate input condition e.g.
SELECT count(*)
FROM [Sample] S
WHERE (#n11 IS NULL OR S.N1 LIKE '%'+#n11+'%')
AND (#n12 IS NULL OR S.N1 LIKE '%'+#n12+'%')
AND (#n21 IS NULL OR S.N2 LIKE '%'+#n21+'%')
AND (#n22 IS NULL OR S.N2 LIKE '%'+#n22+'%')
AND (#n31 IS NULL OR S.N3 LIKE '%'+#n31+'%')
AND (#n32 IS NULL OR S.N3 LIKE '%'+#n32+'%')
Note the alias, which is the best practice way to avoid having to repeat a long table name all over the query.
Try to remove the next conditions from your stored procedure query:
#n12 IS NULL
#n22 IS NULL
#n32 IS NULL
As the above conditions are returning always true while there were sent when calling the procedure.
Stored procedure code after modified:
Alter Procedure dbo.Sample2
#n11 varchar(max) = null,
#n12 varchar(max) = null,
#n21 varchar(max) = null,
#n22 varchar(max) = null,
#n31 varchar(max) = null,
#n32 varchar(max) = null
AS
BEGIN
select count(*)
from Sample
where (#n11 IS NULL OR Sample.N1 LIKE '%'+#n11+'%' OR Sample.N1 LIKE '%'+#n12+'%')
AND (#n21 IS NULL OR Sample.N2 LIKE '%'+#n21+'%' OR Sample.N2 LIKE '%'+#n22+'%')
AND (#n31 IS NULL OR Sample.N3 LIKE '%'+#n31+'%' OR Sample.N3 LIKE '%'+#n32+'%')
END
Now while executing exec dbo.Sample2 #n11 = 'A' the output will be 2 instead of 7
select
sum(iif(N1 like '%'+#N11+'%',1,0)
+ iif(N1 like '%'+#N12+'%',1,0)
+ iif(N2 like '%'+#N21+'%',1,0)
+ iif(N2 like '%'+#N22+'%',1,0)
+ iif(N3 like '%'+#N31+'%',1,0)
+ iif(N3 like '%'+#N32+'%',1,0)
)
from Sample
Any NULL variables will be ignored.

SQL variable into diffrent column

I need to show a set of string into new column based on some columns value.
I tried to use isnull (name, variable = ''null) but does not work
DECLARE #NAME VARCHAR (MAX)
DECLARE #TEL VARCHAR (MAX)
SET #NAME = '- inNAME '
SET #TEL = '- inTEL '
SELECT IIF(NAME IS NULL, #NAME = '- notinNAME'), IIF(TEL IS NULL, #TEL = '- notinTEL;), (#NAME+ #TEL) AS RESULT FROM ADDRESS
NAME TEL RESULT
NULL 07 - notinNAME - inTEL
ARK NULL - inNAME - notinTEL
ARK 07 - inNAME - inTEL
NULL NULL - notinNAME - notinTEL
Is this what you are trying to do?
SELECT CONCAT(CASE WHEN NAME IS NULL THEN '- notinNAME' ELSE '' END,
CASE WHEN TEL IS NULL THEN '- notinTEL' ELSE '' END
)
FROM ADDRESS;
You cannot use variables for a few reasons. First, SQL Server does not allow you to assign variables in a SELECT and also to return values.
More importantly, SQL (in general) does not guarantee the order of evaluation of expressions in the SELECT. Your formulation assumes a particular order of evaluation, so it is not going to work.
EDIT:
If you want to use the variables:
SELECT CONCAT(CASE WHEN NAME IS NULL THEN #NAME ELSE '' END,
CASE WHEN TEL IS NULL THEN #TEL ELSE '' END
)
FROM ADDRESS;

SQL Server - Using CASE statement

I have a SELECT statement with a WHERE clause that I want to dynamically change depending if a parameter is supplied or not.
I can't seem to understand how to use CASE statement in a WHERE clause but this is how I want it to look like using an IF statement.
DECLARE #Gender NVARCHAR(100) = NULL --this is an INPUT parameter and may or may not be NULL
DECLARE #Status NVARCHAR(100) = NULL --this is an INPUT parameter and may or may not be NULL
SELECT Name
FROM Person
WHERE
-- first WHERE clause
IF #Gender IS NULL
BEGIN
Gender IS NULL
END
ELSE
BEGIN
Gender = #Gender
END
AND
-- second WHERE clause
IF #Status IS NULL
BEGIN
Status IS NULL
END
ELSE
BEGIN
Status LIKE '%' + #Status + '%'
END
Is it possible to transform this code into a CASE statement?
I think you want:
select p.name
from person p
where ( (#gender is null and gender is null) or gender = #gender) and
( (#status is null and status is null) or status = #status);
Note that this does "null-matching". Often, people want to use NULL to select all records, not just the NULL ones. If that is what you intend, then:
select p.name
from person p
where ( #gender is null or gender = #gender) and
( #status is null or status = #status);
In either situation, case is not needed in the where. As a general rule, don't use case in where -- unless you really need it to control the order of evaluation of expressions.
You can do this:
SELECT Name
FROM Person
WHERE Gender = COALESCE(#gender, Gender)
AND (#Status is null or Status like '%' + #status + '%')
DECLARE #Gender NVARCHAR(100) = NULL --this is an INPUT parameter and may or may not be NULL
DECLARE #Status NVARCHAR(100) = NULL --this is an INPUT parameter and may or may not be NULL
SELECT Name
FROM Person
WHERE CASE WHEN #Gender IS NULL THEN 1
WHEN #Gender = ISNULL(Gender, '') THEN 1
ELSE 0
END = 1
AND CASE WHEN #Status IS NULL THEN 1
WHEN ISNULL(Status, '') LIKE '%' + #Status + '%' THEN 1
ELSE 0
END = 1

SQL OR CONDITION ON TWO DIFFERENT COLUMNS

I have two fields (#EmployeeId,#SSOId) out of which one value can come or both can come, but when i am applying OR condition it is not giving me correct output. What i am doing wrong ?
ALTER PROCEDURE [dbo].[usp_User_GetDetails] (
#UserId INT = NULL
,#ADSId NVARCHAR(32) = NULL
,#EmployeeId NVARCHAR(32) = NULL
,#SSOId NVARCHAR(32) = NULL
,#UserName NVARCHAR(100) = NULL
)
AS
*/
SET NOCOUNT ON;
BEGIN
SELECT [USER_ID] AS UserId
,[FIRST_NM] AS FirstName
,[LST_NM] AS LastName
,[FULL_NM] AS FullName
,[ADS_USER_ID] AS ADSId
,[SEG_ID] AS SegmentId
,[PHONE_NO] AS PhoneNo
,[FAX_NO] AS FaxNo
,[EMP_ID] AS EmployeeId
,[EMAIL_AD_TX] AS Email
,[SSO_ID] AS SSOId
,[SFDC_IN] AS IsSFDC
,[USER_SFDC_ID] AS UserSFDCId
,[MGR_SFDC_ID] AS ManagerSFDCId
,[ACT_IN] AS IsActive
,[SYS_USER_IN] AS IsSystemUser
,[PORFOLIO_OWN_IN] AS CanHavePortfolio
,[MGR_ID] AS ManagerId
,[LST_LOG_IN_TS] AS LastLoginDate
,[EMP_BAND_TX] AS Band
,[CREAT_TS] AS CreatedDate
,[CREAT_BY_USER_ID] AS CreatedBy
,[LST_UPDT_TS] AS UpdatedDate
,[LST_UPDT_BY_USER_ID] AS UpdatedBy
FROM [dbo].[USER] WITH (NOLOCK)
WHERE ([EMP_ID] = ISNULL(#EmployeeId, [EMP_ID])OR [SSO_ID] = ISNULL(#SSOId, [SSO_ID])
AND [ADS_USER_ID] = ISNULL(#ADSId, [ADS_USER_ID])
AND [USER_ID] = ISNULL(#UserId, [USER_ID])
AND [FULL_NM] LIKE CASE
WHEN #UserName IS NOT NULL
THEN '%' + #UserName + '%'
ELSE [FULL_NM]
END
END
I don't think the parentheses are balanced correctly. In any case, I would write this without the ISNULL():
WHERE ((#EmployeeId IS NULL OR EMP_ID = #EmployeeId) OR
(#SSOId IS NULL OR SSO_ID = #SSOId)
) AND
(#ADSId IS NULL OR ADS_USER_ID = #ADSId) AND
(#UserId IS NULL OR USER_ID = #UserId) AND
(#UserName IS NULL OR FULL_NM LIKE '%' + #UserName + '%')
I am guessing that the OR is for the first two conditions. This is where the parens don't seem to line up in the query in the question.
I prefer this construct for two reasons. First, it handles NULL values in the column values as well as the parameter values. And second -- because it is more general -- it is one of the standard two ways I use to handle optional parameters (the other is to use dynamic SQL which can make use of indexes).
Query seems to be okay .Are you passing DBNull from you C# code or empty text
WHERE (#EmployeeId IS NULL OR (EMP_ID = #EmployeeId))
AND (#SSOId IS NULL OR (SSO_ID = #SSOId))
AND [ADS_USER_ID] = ISNULL(#ADSId, [ADS_USER_ID])
AND [USER_ID] = ISNULL(#UserId, [USER_ID])
AND [FULL_NM] LIKE CASE
WHEN #UserName IS NOT NULL
THEN '%' + #UserName + '%'
ELSE [FULL_NM]
Used this script
WHERE EMP_ID = CASE WHEN ISNULL(#EmployeeId,0) > 0 THEN #EmployeeId ELSE EMP_ID END AND SSO_ID = CASE WHEN ISNULL(#SSOId,0) > 0 THEN #SSOId ELSE SSO_ID END

how to take all values from sql table column on condition?

I want to select all column values irrespective of NULL as column value
my problem is when i try to select values using IS NULL i'm getting such records also thise having NULL as a value :(
declare #status varchar(50)=NULL
declare #path varchar(50)= 'India'
select
*
from [vwMYDATA]
where
Path like '%' + #path + '%' and
(Status = #status or #status IS NULL)
vwMYDATA
path status
INDIA1 NULL
INDIA2 close
INDIA3 open
If i execute above query i'm getting all records whose status column has NULL value
expected is if no status value specified show all status records
path status
INDIA1 NULL
INDIA2 close
INDIA3 open
it is showing
path status
INDIA1 NULL
I'm passing these 2 parameters to stored procedure which is having this select statment.
please help me to filtered out and show all the records if status is not given as input
select
*
from [vwMYDATA]
where
Path like '%' + #path + '%' and
((#status IS NULL )OR (Status = #status or status IS NULL))
You need to change the WHERE clause to a Case statement if you want different actions based on whether the #status varchar is NULL.
Select *
From [vwMYDATA]
Where
Path Like Concat('%', #path, '%') And
1 = (
Case When #status Is Null Then 1
When Status = #status Then 1
Else 0
)
This might be inefficient and nonperformant. For performance, it would be better to have a completely different query that matches based on NULL or not, so that proper indexing can be applied.
If you're happy to always filter out records with a null status (which it seems you are) then this should work:
declare #status varchar(50)=NULL
declare #path varchar(50)= 'India'
select
*
from [vwMYDATA]
where
Path like '%' + #path + '%' and
(Status = #status or #status IS NULL)
AND Status IS NOT NULL
Just try remove or #status IS NULL condition
select
*
from [vwMYDATA]
where
Path like '%' + #path + '%' and
(Status = #status)
You need to change your condition like below:
declare #status varchar(50)=NULL
declare #path varchar(50)= 'India'
select
*
from [vwMYDATA]
where
Path like '%' + #path + '%' and
(#status IS NULL OR Status = #status)
you just need to change the place of condition because the conditions check from first, so now if the #status will be null, query don't check next condition.
Get NUll Value Records on Status
select * from vwMYDATA where status is null
Get Not NUll Value Records on Status
select * from vwMYDATA where status is not null