Home > front end >  Use WHERE AND CASE IN when crafting T-SQL
Use WHERE AND CASE IN when crafting T-SQL

Time:03-11

I have a stored procedure that has a few variables that may or may not be passed. they are a list of PKs from other tables, so FKs but formatted in as a string of CSVs.

here's what the query essentially looks like

DECLARE @SomeIds VARCHAR(MAX) = ''
CREATE TABLE #TempIds (Id INT)

IF (@SomeIds = '' OR @SomeIds = NULL) INSERT INTO #TempIds VALUES (NULL)
ELSE INSERT INTO #TempIds SELECT * FROM SplitString(@SomeIds,',') -- SplitString() is a user function

SELECT cont.varchar_LastName AS LastName
      ,cred.varchar_CredentialName AS CredentialName
  FROM [dbo].[tbl_Contacts] AS cont
  LEFT JOIN [dbo].[tbl_ContactsCredentials] AS cc ON cont.pk_int_Id = cc.fk_ContactId
  LEFT JOIN [dbo].[tbl_Credentials] AS cred ON cc.fk_CredentialId = cred.pk_int_Id

So this query basically gives me a full list of contacts both with and without a credential name. I don't have a WHERE clause, so not surprised.

I get data basically like:

LastName  |  CredentialName
---------------------------
Stevens   |  Admin
Arnolds   |  User
Bishop    |  NULL
Evans     |  NULL

So if I add a WHERE clause like this:

WHERE cred.pk_int_Id IN (SELECT * FROM #TempIds)

I get zero results.

When I run this:

SELECT * FROM #TempIds

I get this:

Id
-----------
NULL

When I run it with "real values" in @SomeIds like '1,2' then it works fine.

I presume this is because my WHERE clause is looking in the cred table and there are no NULL values in that table, so that's why I'm not getting anything.

But I'm not sure how I fix it?

I guess I really want to do something like this:

WHERE CredentialName IN (SELECT * FROM #TempIds)

But I believe to do that, I'd have to run the first query into another temp table, then run a second query on that table.

Any help is greatly appreciated.

CodePudding user response:

You can avoid a temp table

WHERE (NULLIF(@SomeIds,'') IS NULL OR cred.pk_int_Id IN (SELECT value FROM SplitString(@SomeIds,','))) 

Or if your Sql Server version supports STRING_SPLIT

WHERE (NULLIF(@SomeIds,'') IS NULL OR cred.pk_int_Id IN (SELECT value FROM STRING_SPLIT(@SomeIds,','))) 

And then don't initialize @SomeIds to make it get all records.

CodePudding user response:

I would consider using a UNION for this. Putting an OR in a WHERE clause can make for some bad execution plans, and often makes indexes unusable.

SELECT cont.varchar_LastName AS LastName
    ,cred.varchar_CredentialName AS CredentialName
FROM [dbo].[tbl_Contacts] AS cont
LEFT JOIN [dbo].[tbl_ContactsCredentials] AS cc
    ON cont.pk_int_Id = cc.fk_ContactId
LEFT JOIN [dbo].[tbl_Credentials] AS cred
    ON cc.fk_CredentialId = cred.pk_int_Id
WHERE cred.pk_int_Id IN (SELECT value FROM STRING_SPLIT(@SomeIds,','))

UNION ALL

SELECT cont.varchar_LastName AS LastName
    ,cred.varchar_CredentialName AS CredentialName
FROM [dbo].[tbl_Contacts] AS cont
LEFT JOIN [dbo].[tbl_ContactsCredentials] AS cc
    ON cont.pk_int_Id = cc.fk_ContactId
LEFT JOIN [dbo].[tbl_Credentials] AS cred
    ON cc.fk_CredentialId = cred.pk_int_Id
WHERE NULLIF(@SomeIds,'') IS NULL

I talk about using OR in UPDATES statements here. But the same logic applies to SELECT.

  • Related