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
问题:
即使UserType为'LinkedIn',第一个WHERE子句也会完全执行,并且SQL会尝试将@oAuthID的值CAST(在这种情况下为'aaaaaaa')转换为BIGINT。
问题:
如何编写WHERE CLAUSE,如果第一部分如果不是,则第二部分不执行?
理想情况下,因为@UserType不是'FaceBook',我们不应该尝试计算该行的第二部分(CAST)?
先谢谢。
Filu
答案 0 :(得分:4)
虽然sql确实做短路(允许语言仅评估条件的一部分的功能),但它不会像其他语言一样进行。
Sql Server使用优化器为查询构建执行计划。优化器查看查询,并尝试以最有效的方式构建它。大多数情况下,它会优化估计的结果数量:它希望一次锁定的记录更少,一次内存中的记录更少。但是当来自两个条件的集合大小相同时,它也可能意味着在非索引列之前检查where子句中的索引列,或者在>之前进行小的(因而快速的)比较,例如位或整数 em>更长(因此更慢)的比较(如字符串匹配)。
在这种情况下,您最好的机会是将双方视为文本类型(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
答案 1 :(得分:2)
将您的查询更改为:
SELECT @UserID = (
SELECT ID FROM @T
WHERE (@UserType = 'FaceBook' AND [FaceBookID] = CASE WHEN ISNUMERIC(@oAuthID) = 1 THEN CAST(@oAuthID AS BIGINT) ELSE NULL END)
OR (@UserType = 'Twitter' AND [TwitterID] = CASE WHEN ISNUMERIC(@oAuthID) = 1 THEN CAST(@oAuthID AS BIGINT) ELSE NULL END)
OR (@UserType = 'LinkedIn' AND [LinkedInID] = @oAuthID)
)
注意,我在else脚上放置null,指定您选择的默认值。
答案 2 :(得分:1)
您可以在where子句中添加CASE
语句。
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] = case when @UserType = 'Facebook' then CAST(@oAuthID AS BIGINT) else [FaceBookID] end)
OR (@UserType = 'Twitter' AND [TwitterID] = case when @UserType = 'Twitter' then CAST(@oAuthID AS BIGINT) else [TwitterID] end)
OR (@UserType = 'LinkedIn' AND [LinkedInID] = @oAuthID)
)
SELECT @UserID
编辑:通过修改这段代码进行测试,就像进行健全性检查一样。
INSERT INTO @T (FaceBookID, TwitterID, LinkedInID)
VALUES (NULL, NULL, 'aaaaaaa')
答案 3 :(得分:1)
尝试不同的方法
SELECT @UserID = (
SELECT ID FROM @T
WHERE (@UserType = 'FaceBook' AND LTRIM(STR([FaceBookID])) = @oAuthID)
OR (@UserType = 'Twitter' AND LTRIM(STR([TwitterID])) = @oAuthID)
OR (@UserType = 'LinkedIn' AND [LinkedInID] = @oAuthID)
答案 4 :(得分:0)
CASE语句是唯一可以依赖TSQL短路评估的时间。 (例如,参见:http://www.sqlservercentral.com/articles/t-sql/71950,关于CASE陈述的部分)。
所以这样的事情应该是安全的:
SELECT @UserID = (
SELECT ID FROM @T
WHERE 1 =
(CASE WHEN @UserType = 'FaceBook' THEN CASE WHEN [FaceBookID] = CAST(@oAuthID AS BIGINT) THEN 1 END
WHEN @UserType = 'Twitter' THEN CASE WHEN [TwitterID] = CAST(@oAuthID AS BIGINT) THEN 1 END
WHEN @UserType = 'LinkedIn' THEN CASE WHEN [LinkedInID] = @oAuthID THEN 1 END
END)
)