Abe*_*ler 20 sql-server performance sql-server-2008
我有下面的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
)
Run Code Online (Sandbox Code Playgroud)
Seb*_*ine 13
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/ 有关排序-MERG-加入的文章是在这里:HTTP:// sqlity.net/en/1480/a-join-a-day-the-sort-merge-join/
为了使您的查询更快,我首先看看索引.您在查询中有一堆聚簇索引扫描.如果你可以用寻求替换它们中的一些,那么你很可能会更好.还要检查SQL Server生成的估计值是否与实际执行计划中的实际行计数相匹配.如果距离很远,SQL Server通常会做出错误的选择.因此提供更好的统计信息也可以帮助您查询性能.
SQL Server 执行排序是为了启用该排序运算符右侧的数据集与表中的记录之间的合并联接Orders。合并连接本身是一种连接数据集中所有记录的非常有效的方法,但它要求要连接的每个数据集根据连接键并以相同的顺序进行排序。
由于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
Run Code Online (Sandbox Code Playgroud)
看起来 SQL Server 正在查询中的所有可能匹配记录之间生成交叉联接Orders(Customers倒数第二个过滤器运算符右侧的计划),然后查看具有该条件的每个记录以查看是否满足确实匹配。请注意,进入过滤器的管线非常粗,而出来的管线却非常细?这是因为在该运算符之后估计的行数从 21k 变为 4。忘记我之前说的吧,这可能是计划中的主要问题。即使这些列上有索引,SQL Server也无法使用它们,因为连接条件太复杂。它导致计划将所有记录合并在一起,而不是只寻求您需要的记录,因为它无法立即使用完整的连接谓词。
我的第一个想法是将 CTE 重新表述custOrders为两个数据集的并集:一个使用数据集CustomerId,一个使用SecondaryCustomerId连接数据集。这将重复 CTE 其余部分的工作,但如果它能够正确使用索引,那么这可能是一个巨大的胜利。