在此找到了两个类似的问题,但无法弄清楚如何应用于我的方案。
我的函数有一个名为@@ 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)
显然那是不正确的。
正确的语法是什么?
我将查询更改为使用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允许您编写一次查询。请注意,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
您可以将其写为
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或在调用代码中使用不同的命令字符串?
您可以将您的CASE
语句更改为此。查询计划者对此有不同的看法,但它可能不比使用OR更有效:
(p.LocationTypeId = CASE @IncludeBelow WHEN 0 THEN p.LocationTypeId ELSE @LocationType END)
如下编辑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变量是字符数据类型