制作 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
.
我有一个存储过程,其中包含一些可能传递也可能不传递的变量。它们是来自其他 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
.