使用连接进行条件过滤
Posted
技术标签:
【中文标题】使用连接进行条件过滤【英文标题】:Conditional filtering with join 【发布时间】:2016-07-14 11:25:53 【问题描述】:在具有单个参数的存储过程中,我有一个类似于以下性质的查询:
SELECT
ID,
DepartmentID,
FileName
FROM
Document
-- conditional join from here
JOIN
AllowedDepartmentList ON DepartmentID = AllowedDepartmentList.ID
AND @IsAdmin = 'false'
参数为@IsAdmin
,数据类型位。
我使用的两个表是 Document
表(请参阅上面查询中的结构)和包含单个 int
列的 AllowedDepartmentList
。
我使用这个查询来过滤带有连接的Document
表的返回结果。我不使用 WHERE DepartmentID IN()
子句,因为 AllowedDepartmentList 可以长达 600-700 个项目(对于 IN()
来说太多了,无法在潜在的 1M 记录表中以良好的性能处理)
所以我使用连接进行过滤,但只有在@IsAdmin 参数为false
时才应执行过滤。就像 --conditional join from here 注释之后的行一样。
我尝试了上面的查询,但它没有产生任何记录。我怀疑我使用了错误的联接类型,但我被卡住了。
【问题讨论】:
【参考方案1】:您可以将左连接与需要管理员权限的地方结合使用,或者在连接上进行匹配
SELECT
ID,
DepartmentID,
FileName
FROM
Document
-- conditional join from here
LEFT JOIN
AllowedDepartmentList ON DepartmentID = AllowedDepartmentList.ID
WHERE
@IsAdmin = 'true' OR AllowedDepartmentList.ID IS NOT NULL
【讨论】:
请注意,jhilden 的建议更有效,因为您可以避免在不需要的情况下加入。不过,我确实理解您只保留一个查询的论点。 非常感谢,这似乎没问题,我得测试一下。是的,你是对的,我们还必须测试额外的连接是否会导致如此多的开销,以至于值得复制代码。 我接受了您的回答,因为我们已实施此解决方案以进行测试。它根据需要工作并完成工作,但应该衡量性能。还有许多其他适合我的答案,所以感谢所有花时间摆弄我的问题的人。【参考方案2】:作为一般规则,将参数放入 WHERE 子句 VS 连接中。一个稍微简单的解决方案是让 TVF 或 sproc 运行两个完全不同的查询。像这样:
IF (@isAdmin = 0) --notice I used a SQL bool vs a string of 'false'
BEGIN
SELECT
ID,
DepartmentID,
FileName
FROM
Document
JOIN
AllowedDepartmentList ON DepartmentID = AllowedDepartmentList.ID;
END;
ELSE
--@IsAdmin is not false, so don't join
SELECT
ID,
DepartmentID,
FileName
FROM
Document;
END;
【讨论】:
这是我试图避免的:重复选择。我们的 SP 中的精确选择要复杂得多(比如 150 行),如果整个 shebang 被复制,就会变得非常难看……更不用说未来的修改了。 @Daniel 如果整个 shebang 是 150 行,则更有理由进行有效的查询。复制粘贴和删除连接有多难? 你在这件事上是绝对正确的。我们将不得不对真实数据进行一些压力测试,以找出最佳答案。我只是在寻找代码重复的替代方法,但我从没想过会得到这么多好的建议(包括保持当前轨道,就像 jhilden - 和你 - 建议的那样)。【参考方案3】:你可以使用存在条件,像这样:
SELECT
ID,
DepartmentID,
FileName
FROM Document
WHERE exists(
SELECT 1 from AllowedDepartmentList
WHERE DepartmentID = AllowedDepartmentList.ID)
OR @IsAdmin = 'true'
【讨论】:
我认为它更快,特别是如果您使用附加条件,但您必须尝试。 join 是获取数据,exist 非常适合你:) 加入会导致记录相乘,不存在。 嗯。有趣的厘米。一定会检查这个建议。【参考方案4】:使用Dynamic query
的另一种方法。与JOIN/Exists
options 相比,这将具有良好的性能
DECLARE @sql NVARCHAR(max)='',
@IsAdmin BIT = 1
SET @sql = '
SELECT
ID,
DepartmentID,
FileName
FROM
Document D
' + CASE WHEN @IsAdmin = 'false' THEN ' where exists (select 1 from AllowedDepartmentList AD where D.DepartmentID = AD.ID ) ' ELSE '' END
--PRINT @sql
exec sp_executesql @sql
@IsAdmin = 0
时的动态框架查询
SELECT ID,
DepartmentID,
FileName
FROM Document D
WHERE EXISTS (SELECT 1
FROM AllowedDepartmentList AD
WHERE D.DepartmentID = AD.ID)
@IsAdmin = 0
时的动态框架查询
SELECT ID,
DepartmentID,
FileName
FROM Document D
【讨论】:
【参考方案5】:以下查询会对您有所帮助
SELECT ID
,DepartmentID
,FileName
FROM Document
LEFT JOIN AllowedDepartmentList ON DepartmentID = AllowedDepartmentList.ID
WHERE @isAdmin = 1
OR ( @isAdmin = 0 AND AllowedDepartmentList.ID IS NOT NULL)
【讨论】:
【参考方案6】:为管理员添加一个类似于 -1 的 AllowedDepartmentList.ID 为 @adminID 传递 null 或 -1
ON isnull(@adminID, DepartmentID) = AllowedDepartmentList.ID
一个 OR(或 @IsAdmin = 'true' )效率不高,这也只返回 1
【讨论】:
这会杀死DepartmentID
上的所有索引
@Prdp 所以,我假设 AllowedDepartmentList.ID 是一个 PK。它并不比代码更少的 EXISTS 差。以上是关于使用连接进行条件过滤的主要内容,如果未能解决你的问题,请参考以下文章