T-SQL条件WHERE子句

时间:2010-12-20 00:27:15

标签: sql sql-server tsql where-clause case-statement

在此发现了几个类似的问题,但无法弄清楚如何应用于我的场景。

我的函数有一个名为 @IncludeBelow 的参数。值为0或1(BIT)。

我有这个问题:

SELECT p.*
FROM Locations l
INNER JOIN Posts p
on l.LocationId = p.LocationId
WHERE l.Condition1 = @Value1
AND   l.SomeOtherCondition = @SomeOtherValue

如果@IncludeBelow为0,我需要查询:

SELECT p.*
FROM Locations l
INNER JOIN Posts p
on l.LocationId = p.LocationId
WHERE l.Condition1 = @Value1
AND   l.SomeOtherCondition = @SomeOtherValue
AND   p.LocationType = @LocationType -- additional filter to only include level.

如果@IncludeBelow为1,则需要排除最后一行。 (即不要应用过滤器)。

我猜它需要是CASE语句,但无法弄清楚语法。

这是我尝试过的:

SELECT p.*
FROM Locations l
INNER JOIN Posts p
on l.LocationId = p.LocationId
WHERE l.Condition1 = @Value1
AND   l.SomeOtherCondition = @SomeOtherValue
AND (CASE @IncludeBelow WHEN 0 THEN p.LocationTypeId = @LocationType ELSE 1 = 1)

显然这不正确。

正确的语法是什么?

4 个答案:

答案 0 :(得分:38)

我将查询更改为使用EXISTS,因为如果有多个POST位置关联,则会有重复的POST记录需要DISTINCT或GROUP BY子句来摆脱...

非sargable

这将执行最糟糕的解决方案:

SELECT p.*
  FROM POSTS p
 WHERE EXISTS(SELECT NULL
                FROM LOCATIONS l
               WHERE l.LocationId = p.LocationId
                 AND l.Condition1 = @Value1
                 AND l.SomeOtherCondition = @SomeOtherValue)
   AND (@IncludeBelow = 1 OR p.LocationTypeId = @LocationType)

sargable,非动态版本

自我解释......

BEGIN
  IF @IncludeBelow = 0 THEN
    SELECT p.*
      FROM POSTS p
     WHERE EXISTS(SELECT NULL
                    FROM LOCATIONS l
                   WHERE l.LocationId = p.LocationId
                     AND l.Condition1 = @Value1
                     AND l.SomeOtherCondition = @SomeOtherValue)
       AND p.LocationTypeId = @LocationType
  ELSE
    SELECT p.*
      FROM POSTS p
     WHERE EXISTS(SELECT NULL
                    FROM LOCATIONS l
                   WHERE l.LocationId = p.LocationId
                     AND l.Condition1 = @Value1
                     AND l.SomeOtherCondition = @SomeOtherValue) 
END

sargable,动态版本(SQL Server 2005 +):

爱或恨,动态SQL允许您编写一次查询。请注意,与SQL Server中的EXEC不同,sp_executesql会缓存查询计划。强烈建议在考虑SQL Server上的动态SQL之前阅读The Curse and Blessings of Dynamic SQL ...

DECLARE @SQL VARCHAR(MAX)
    SET @SQL = 'SELECT p.*
                  FROM POSTS p
                 WHERE EXISTS(SELECT NULL
                                FROM LOCATIONS l
                               WHERE l.LocationId = p.LocationId
                                 AND l.Condition1 = @Value1
                                 AND l.SomeOtherCondition = @SomeOtherValue)'

    SET @SQL = @SQL + CASE 
                        WHEN @IncludeBelow = 0 THEN
                         ' AND p.LocationTypeId = @LocationType '
                        ELSE ''
                      END   

BEGIN 

  EXEC sp_executesql @SQL, 
                     N'@Value1 INT, @SomeOtherValue VARCHAR(40), @LocationType INT',
                     @Value1, @SomeOtherValue, @LocationType

END

答案 1 :(得分:10)

你可以把它写成

SELECT  p.*
  FROM  Locations l
INNER JOIN Posts p
    ON  l.LocationId = p.LocationId
  WHERE l.Condition1 = @Value1
    AND l.SomeOtherCondition = @SomeOtherValue
    AND ((@IncludeBelow = 1) OR (p.LocationTypeId = @LocationType))

这是一种你看得很多的模式,例如用于可选的搜索参数。但IIRC可能会破坏查询执行计划,因此可能有更好的方法来执行此操作。

由于它只是一点点,几乎可能值得在两个SQL块之间进行判断,有或没有检查,例如在存储过程中使用IF或在调用代码中使用不同的命令字符串,基于位?

答案 2 :(得分:3)

您可以将CASE语句更改为此。查询规划器以不同的方式看待它,但它可能不比使用OR更有效:

(p.LocationTypeId = CASE @IncludeBelow WHEN 0 THEN p.LocationTypeId ELSE @LocationType END)

答案 3 :(得分:0)

按如下所示编辑sql语句:

SELECT p.*
FROM Locations l
    INNER JOIN Posts p
    on l.LocationId = p.LocationId
WHERE l.Condition1 = @Value1
    AND l.SomeOtherCondition = @SomeOtherValue
    AND l.LocationType like @LocationType

不需要@IncludeBelow变量

包括所有位置类型设置@LocationType ='%'

要限制查询返回的位置类型 设置@LocationType ='[特定位置类型]'

以上Set语句假定@LocationType变量为字符数据类型