带有子行的 T-SQL SELECT 存在优化

Posted

技术标签:

【中文标题】带有子行的 T-SQL SELECT 存在优化【英文标题】:T-SQL SELECT with child rows exists optimise 【发布时间】:2011-08-29 09:32:34 【问题描述】:

我正在尝试优化我的查询。基本上我有一个部门表和一个文档表。每个文档属于一个部门,每个文档可以是特定类型。

目前 mu 查询看起来像这样

SELECT  DepartmentID,
        [Description] = DepartmentNo + ' (' + DepartmentName + ')',
        hasInvoice = CASE WHEN EXISTS(SELECT DocID FROM Document WHERE DepartmentID = Department.DepartmentID AND TypeID = 1) > 0 THEN 1 ELSE 0 END,
        hasCreditNote = CASE WHEN EXISTS(SELECT DocID FROM Document WHERE DepartmentID = Department.DepartmentID AND TypeID = 2) > 0 THEN 1 ELSE 0 END,
        hasQuote = CASE WHEN EXISTS(SELECT DocID FROM Document WHERE DepartmentID = Department.DepartmentID AND TypeID = 3) > 0 THEN 1 ELSE 0 END
FROM  Department
ORDER BY DepartmentName

我遇到的问题是,每次按类型查找与部门相关的文档都会导致查询重新扫描文档表,这使得事情变得非常缓慢。有没有更优化的方法来做到这一点?我尝试使用 CTE,为每种类型进行左连接。但它似乎对加快查询速度没有影响。

我们有大约 200000 个文档,因此优化此查询非常重要。

谢谢

【问题讨论】:

你在桌子上设置了哪些索引? 是的。它不应该“重新扫描”。执行计划是否真的显示了这一点?理想情况下,您希望在 Document(DepartmentID,TypeID) 上有一个复合索引 【参考方案1】:

无论索引是什么,您对 Document 表的“触摸”太多:Department 表中每行 3 次。所以它的扩展性会很差,优化器可能不会将相关的子查询更改为 JOIN。

您可以像这样使用 JOIN 对 Document 表进行一次“触摸”

SELECT
    D.DepartmentID,
    [Description] = D.DepartmentNo + ' (' + D.DepartmentName + ')',
    hasInvoice = SIGN(COUNT(CASE WHEN doc.TypeID = 1 THEN 1 END)),
    hasCreditNote = SIGN(COUNT(CASE WHEN doc.TypeID = 2 THEN 1 END)),
    hasQuote = SIGN(COUNT(CASE WHEN doc.TypeID = 3 THEN 1 END))
FROM 
    Department D
    LEFT JOIN
    Document doc ON D.DepartmentID = doc.DepartmentID
ORDER BY
    D.DepartmentName, D.DepartmentID, D.DepartmentNo

您也可以在过滤+聚合的 Document 派生表上 LEFT JOIN 3 次或使用 CROSS APPLY 3 次:但这些仍然是 Document 表的 3 次使用

对于任何解决方案,您都需要在文档中的 DepartmentID, TypeID 上建立索引。没有这个,我只有re-arranged the deck chairs可以这么说

我假设 Department.DepartmentID 也是一个聚集索引。

【讨论】:

以上是关于带有子行的 T-SQL SELECT 存在优化的主要内容,如果未能解决你的问题,请参考以下文章

C# SQL Row 删除带有子行的父级

使用 ajax 的带有子行的闪亮数据表

SQL从每个父行的子行返回最大值

SELECT COUNT 对具有 > 100M 行的表进行优化

数据库优化(数据索引)

关于T-SQL中exists或者not exists子查询的“伪优化”的做法