制作 T-SQL 时使用 WHERE + AND + CASE + IN

Use WHERE + AND + CASE + IN when crafting T-SQL

我有一个存储过程,其中包含一些可能传递也可能不传递的变量。它们是来自其他 tables 的 PK 列表,所以 FKs 但格式为 CSV 字符串。

下面是查询的基本内容

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

所以这个查询基本上给了我一个完整的联系人列表,有和没有凭据名称。我没有 WHERE 子句,所以不要感到惊讶。

我得到的数据基本上是这样的:

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

所以如果我像这样添加一个 WHERE 子句:

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

我得到零结果。

当我运行这个:

SELECT * FROM #TempIds

我明白了:

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

当我 运行 它与 @SomeIds 中的“实际值”类似 '1,2' 时,它工作正常。

我认为这是因为我的 WHERE 子句在 cred table 中查找并且 table 中没有 NULL 值,所以这就是我的原因我什么也没得到。

但我不确定如何修复它?

我想我真的很想做这样的事情:

WHERE CredentialName IN (SELECT * FROM #TempIds)

但我相信要做到这一点,我必须 运行 第一个查询进入另一个临时 table,然后 运行 第二个查询 table .

非常感谢任何帮助。

你可以避免临时table

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

或者如果您的 Sql 服务器版本支持 STRING_SPLIT

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

然后不初始化@SomeIds使其获取所有记录。

我会考虑为此使用 UNION。将 OR 放在 WHERE 子句中可能会产生一些错误的执行计划,并且通常会使索引无法使用。

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

我谈到在 UPDATES 语句 here 中使用 OR。但同样的逻辑适用于 SELECT.