SQL Where子句 - 仅在某些@Parameters等于特定值时执行

时间:2011-11-10 19:19:17

标签: sql tsql

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

5 个答案:

答案 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)  
)