T-SQL条件WHERE子句

问题描述 投票:28回答:4

在此找到了两个类似的问题,但无法弄清楚如何应用于我的方案。

我的函数有一个名为@@ 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)

显然那是不正确的。

正确的语法是什么?

sql sql-server tsql where-clause case-statement
4个回答
39
投票

我将查询更改为使用EXISTS,因为如果与POST相关联的位置不止一个,那么将存在重复的POST记录,这些记录需要DISTINCT或GROUP BY子句才能摆脱...

不可燃的

这将执行最坏的解决方案:

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)

可升级的非动态版本

自我解释。...

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

可升级的动态版本(SQL Server 2005 +):

喜欢或讨厌它,动态SQL允许您编写一次查询。请注意,sp_executesql缓存查询计划,这与SQL Server中的EXEC不同。强烈建议在考虑在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

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

这是您经常看到的一种模式,例如用于可选的搜索参数。但是IIRC可能会弄乱查询执行计划,因此可能会有更好的方法来执行此操作。

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


3
投票

您可以将您的CASE语句更改为此。查询计划者对此有不同的看法,但它可能不比使用OR更有效:

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

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变量是字符数据类型

© www.soinside.com 2019 - 2024. All rights reserved.