T-SQL条件WHERE子句

RPM*_*984 28 sql t-sql sql-server case-statement where-clause

在这里找到了几个类似的问题,但无法弄清楚如何应用于我的场景.

我的函数有一个名为@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
Run Code Online (Sandbox Code Playgroud)

如果@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.
Run Code Online (Sandbox Code Playgroud)

如果@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)
Run Code Online (Sandbox Code Playgroud)

显然这不正确.

什么是正确的语法?

OMG*_*ies 39

我将查询更改为使用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)
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

喜欢或讨厌它,动态SQL允许您编写一次查询.请注意,与SQL Server中的EXEC不同,sp_executesql会缓存查询计划.强烈建议在考虑SQL Server上的动态SQL之前阅读动态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
Run Code Online (Sandbox Code Playgroud)

  • @ RPM1984:诅咒你和你抽象的代码:P :) (4认同)

Rup*_*Rup 11

你可以把它写成

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))
Run Code Online (Sandbox Code Playgroud)

这是您看到很多例如可选搜索参数的模式.但IIRC可能会破坏查询执行计划,因此可能有更好的方法来执行此操作.

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