31

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

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

4 回答 4

41

我将查询更改为使用 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)

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

可搜索的动态版本(SQL Server 2005+):

喜欢或讨厌它,动态 SQL 让您只需编写一次查询。请注意 sp_executesql 缓存查询计划,这与 SQL Server 中的 EXEC 不同。强烈建议在考虑 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
于 2010-12-20T00:46:58.397 回答
12

你可以把它写成

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 或在调用代码中使用不同的命令字符串,基于位?

于 2010-12-20T00:30:41.180 回答
4

您可以将您的CASE声明更改为此。查询规划器对此有不同的看法,但它可能并不比使用 OR 更有效:

(p.LocationTypeId = CASE @IncludeBelow WHEN 0 THEN p.LocationTypeId ELSE @LocationType END)
于 2014-07-23T15:18:53.177 回答
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 = '%'

限制查询返回的位置类型 Set @LocationType = '[A Specific Location Type]'

上面的 Set 语句假设 @LocationType 变量是字符数据类型

于 2020-02-06T00:41:56.660 回答