在此发现了几个类似的问题,但无法弄清楚如何应用于我的场景。
我的函数有一个名为 @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)
显然这不正确。
正确的语法是什么?
答案 0 :(得分:38)
我将查询更改为使用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允许您编写一次查询。请注意,与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变量为字符数据类型