DECLARE @T TABLE
(
ID BIGINT IDENTITY PRIMARY KEY,
FaceBookID BIGINT NULL,
TwitterID BIGINT NULL,
LinkedInID VARCHAR(50) NULL
);
INSERT INTO @T (FaceBookID, TwitterID, LinkedInID)
VALUES (11111111, NULL, NULL)
INSERT INTO @T (FaceBookID, TwitterID, LinkedInID)
VALUES (NULL, 22222222, NULL)
INSERT INTO @T (FaceBookID, TwitterID, LinkedInID)
VALUES (NULL, NULL, '3333333')
DECLARE @UserType VARCHAR(10)
SET @UserType = 'LinkedIn'
DECLARE @oAuthID VARCHAR(50)
SET @oAuthID = 'aaaaaaa'
DECLARE @UserID BIGINT
SELECT @UserID = (
SELECT ID FROM @T
WHERE (@UserType = 'FaceBook' AND [FaceBookID] = CAST(@oAuthID AS BIGINT))
OR (@UserType = 'Twitter' AND [TwitterID] = CAST(@oAuthID AS BIGINT))
OR (@UserType = 'LinkedIn' AND [LinkedInID] = @oAuthID)
)
SELECT @UserID
Run Code Online (Sandbox Code Playgroud)
问题:
即使 UserType 是“LinkedIn”,第一个 WHERE 子句也会完全执行,并且 SQL 尝试将 @oAuthID 的值(在本例中为“aaaaaaa”)转换为 BIGINT。
问题:
我如何写一个 WHERE CLAUSE,如果第一部分不正确,第二部分不执行?
理想情况下,因为@UserType 不是'FaceBook',我们不应该尝试计算该行的第二部分(CAST)。
虽然 sql 确实会进行短路(该功能允许一种语言只评估条件的一部分),但它的做法与其他语言不同。
Sql Server 使用优化器为查询构建执行计划。优化器查看查询,并尝试以最有效的方式构建它。大多数情况下,它会针对估计的结果数或最佳索引使用进行优化:它希望一次锁定更少的记录,并且一次保留更少的内存记录。但是当来自两个条件的集合大小相同或匹配相似的索引时,它也可能意味着在非索引列之前检查 where 子句中的索引列,或者在更长的时间之前进行小(因此快速)比较,如位或整数(因此较慢)比较(如字符串匹配)。
在这种情况下,最好的机会是将双方视为文本类型(varchar、nchar 等)并将其用于匹配。它会更慢,但您必须始终首先编码正确性,然后才是性能。
这应该有效:
WHERE @oAuthID = CASE WHEN @UserType = 'Facebook' THEN Cast(FaceBookID as varchar(50))
WHEN @UserType = 'Twitter' THEN Cast(TwitterID as varchar(50))
WHEN @UserType = 'LinkedIn' THEN Cast(LinkedInID as varchar(50))
ELSE 'invalid user type' /* could use NULL here - as long as you'll never actually see a query with this value */
END
Run Code Online (Sandbox Code Playgroud)