22

我有下面运行非常缓慢的 sql 查询。我查看了执行计划,它声称 Files.OrderId 上的排序是成本最高的操作 (53%)。如果我不在任何地方按 OrderId 订购,为什么会发生这种情况?在 File.OrderId 上创建索引是我最好的选择吗?

如果有人感兴趣,执行计划。

with custOrders as
(
    SELECT c.firstName + ' ' + c.lastname as Customer, c.PartnerId , c.CustomerId,o.OrderId,o.CreateDate, c.IsPrimary
    FROM Customers c
    LEFT JOIN CustomerRelationships as cr
        ON c.CustomerId = cr.PrimaryCustomerId
    INNER JOIN Orders as o
       ON c.customerid = o.customerid 
           OR (cr.secondarycustomerid IS NOT NULL AND o.customerid = cr.secondarycustomerid)
    where c.createdate >= @FromDate + ' 00:00' 
       AND c.createdate <= @ToDate + ' 23:59' 
),
 temp as
(
SELECT Row_number() 
         OVER ( 
           ORDER BY c.createdate DESC)                    AS 'row_number', 
       c.customerid as customerId, 
       c.partnerid as partnerId, 
       c.Customer, 
       c.orderid as OrderId, 
       c.createdate as CreateDate, 
       Count(f.orderid)                                   AS FileCount, 
       dbo.Getparentcustomerid(c.isprimary, c.customerid) AS ParentCustomerId, 
       au.firstname + ' ' + au.lastname                   AS Admin, 
       '' as blank, 
       0  as zero
FROM   custOrders c 
       INNER JOIN files f 
               ON c.orderid = f.orderid 
       INNER JOIN admincustomers ac 
               ON c.customerid = ac.customerid 
       INNER JOIN adminusers au 
               ON ac.adminuserid = au.id 
       INNER JOIN filestatuses s 
               ON f.statusid = s.statusid 
WHERE  ac.adminuserid IS NOT NULL 
       AND f.statusid NOT IN ( 5, 6 ) 
GROUP  BY c.customerid, 
          c.partnerid, 
          c.Customer, 
          c.isprimary, 
          c.orderid, 
          c.createdate, 
          au.firstname, 
          au.lastname 
)
4

4 回答 4

16

当需要连接两个表时,SQL Server 有三种算法可供选择。Nested-Loops-Join、Hash-Join 和 Sort-Merge-Join。它根据成本估算选择哪一个。在这种情况下,它认为,根据可用的信息,Sort-Merge-Join 是正确的选择。

在 SQL Server 执行计划中,Sort-Merge 被拆分为两个操作符,Sort 和 Merge-Join,因为排序操作可能不是必需的,例如,如果数据已经排序。

有关连接的更多信息,请查看我的连接系列:http: //sqlity.net/en/1146/a-join-a-day-introduction/ 关于排序合并连接的文章在这里:http :// sqlity.net/en/1480/a-join-a-day-the-sort-merge-join/


为了使您的查询更快,我首先会查看索引。您在查询中有一堆聚集索引扫描。如果您可以用搜索替换其中的一些,您很可能会更好。还要检查 SQL Server 生成的估计值是否与实际执行计划中的实际行数相匹配。如果它们相距甚远,SQL Server 通常会做出错误的选择。因此,提供更好的统计数据也可以帮助您查询性能。

于 2013-01-08T16:41:34.417 回答
3

SQL Server 正在执行排序以启用该排序运算符右侧的数据集与Orders表中的记录之间的合并联接。Merge join 本身是一种连接数据集中所有记录的非常有效的方法,但它要求每个要连接的数据集按照连接键和相同的顺序进行排序。

由于PK_Orders键已按 排序OrderID,SQL Server 决定利用这一点,对连接的另一端(排序右侧的其他内容)进行排序,以便在计划中的该点将两个数据集合并在一起. 合并连接的常见替代方法是哈希连接,但这对您没有帮助,因为您将使用昂贵的哈希连接运算符而不是排序和合并。查询优化器已确定排序和合并在这种情况下更有效。

计划中代价高昂的步骤的根本原因是需要将订单表中的所有记录合并到数据集中。有没有办法限制来自files 表的记录?files.statusid如果不在 5,6 中的记录小于总表大小的 10%,则索引可能会有所帮助。

QO 认为大部分记录最后都会被过滤掉。尝试将尽可能多的过滤条件推送回记录源,以便在计划中间处理更少的记录。

编辑:我忘了提,有一个我们可以查看的执行计划非常有帮助。有没有什么方法可以得到一个实际的执行计划结果来查看通过这些操作符的真实记录数?有时估计的记录数可能会有所偏差。

编辑:深入研究倒数第二个过滤器运算符的谓词字段,总结:

c.CustomerId=o.CustomerId
OR o.CustomerId=cr.SecondaryCustomerId AND cr.SecondaryCustomerId IS NOT NULL

Orders看起来SQL Server在查询中的所有可能匹配记录之间产生交叉连接Customers(第二个到最后一个过滤运算符右侧的计划),然后查看具有该条件的每个记录以查看它是否确实匹配。注意进入过滤器的线是如何很粗的,而出来的线是很细的?这是因为在该运算符之后估计的行数从 21k 变为 4。忘记我之前说的,这可能是计划中的主要问题。即使这些列上有索引,SQL Server 也无法使用它们,因为连接条件太复杂。它导致计划将所有记录合并在一起,而不是只寻找您需要的记录,因为它不能立即使用完全连接谓词。

我的第一个想法是将 CTE 重新表述custOrders为两个数据集的联合:一个 usingCustomerId和一个 using SecondaryCustomerIdto join。这将复制 CTE 其余部分的工作,但如果它能够正确使用索引,它可能是一个巨大的胜利。

于 2013-01-08T16:49:02.437 回答
2

我知道这个问题已经很老了,但是我遇到了同样的问题,并意识到我的桌子突然变慢的原因完全不同。症状是相同的,更新以前闪电般快速的视图很慢。“排序”给出 40% 的成本。这个解决方案可能对某人有用,而且很简单。加入表格时,请确保您是在“点对点”的基础上加入的。我在 ID 上加入了两个表。但是在一个表中,我的 ID 设置为 int,而在另一个表中设置为 nvarchar。我更正了这一点,将它们都定义为相同的类型,并且视图恢复到闪电般的速度。

希望这将帮助其他人避免花费一周时间试图找出 SQL 出了什么问题,而这确实是一个 PEBKAC 时刻。

(键盘和椅子之间存在问题)

于 2016-12-30T15:10:46.387 回答
1

我认为这种连接正在发生这种情况:

FROM   custOrders c 
       INNER JOIN files f 
               ON c.orderid = f.orderid 

我会在包含 orderid 和 statusid 列的文件上创建一个索引,因为查询也使用 statusid 列。

您可能还需要考虑以下更改:

  1. 您不需要“ac.adminuserid IS NOT NULL”,因为这已包含在 adminusers 和 admincustomers 之间的内部连接中
  2. 将测试“f.statusid NOT IN (5, 6)”更改为肯定条件(例如 In),因为处理否定条件的成本更高。
于 2013-01-08T16:40:51.877 回答