我有一个查询在某些情况下运行过度简化'执行计划实际上变得非常慢(3-5秒)。查询是:
SELECT DISTINCT Salesperson.*
FROM Salesperson
INNER JOIN SalesOrder on Salesperson.Id = SalesOrder.SalespersonId
INNER JOIN PrelimOrder on SalesOrder.Id = PrelimOrder.OrderId
INNER JOIN PrelimOrderStatus on PrelimOrder.CurrentStatusId = PrelimOrderStatus.Id
INNER JOIN PrelimOrderStatusType on PrelimOrderStatus.StatusTypeId = PrelimOrderStatusType.Id
WHERE
PrelimOrderStatusType.StatusTypeCode = 'Draft'
AND Salesperson.EndDate IS NULL
慢速执行计划如下:
直接突出的是,实际的行数/执行次数明显高于各自的估算值:
如果我删除Salesperson.EndDate IS NULL
子句,则运行更快,并行化的执行计划:
如果删除DISTINCT关键字,类似的执行计划也会运行得非常快。
根据我可以收集的信息,优化人员似乎根据其不正确的估算确定查询的运行成本不高,因此不会选择并行计划。但我不能为我的生活弄清楚为什么选择错误的计划。我检查了我的统计数据,他们都是应有的。我已经在SQL Server 2008到2016中进行了测试,结果相同。
答案 0 :(得分:1)
SELECT DISTINCT
很贵。所以,最好避免它。像这样:
SELECT sp.*
FROM Salesperson sp
WHERE EXISTS (SELECT 1
FROM SalesOrder so INNER JOIN
PrelimOrder po
ON so.Id = po.OrderId INNER JOIN
PrelimOrderStatus pos
ON po.CurrentStatusId = pos.Id INNER JOIN
PrelimOrderStatusType post
ON pos.StatusTypeId = post.Id
WHERE sp.Id = so.SalespersonId AND
post.StatusTypeCode = 'Draft'
) AND
sp.EndDate IS NULL;
注意:SalesPerson(EndDate, Id)
上的索引会有所帮助。
答案 1 :(得分:0)
正如@Gordon Linoff所说,DISTINCT
通常是表现的坏消息。通常它意味着你正在积累太多的数据,然后在一个更紧凑的集合中将它重新压缩。如果可能的话,最好在整个过程中尽量保持小。
此外,反直觉的是,带索引扫描的查询计划比索引搜索的查询计划更快;似乎(在这种情况下)并行性弥补了它。您可以尝试使用 Cost Threshold For Parallelism Option,但要注意这是服务器范围的设置! (然而,在我看来,对于我个人遇到的大多数用例来说,默认值为5相当高;这些天CPU很多,时间仍然不是=。)
有点长距离,但我想知道你是否可以在2中“拆分”查询,从而消除(一小部分)服务器的猜测。我在这里假设StatusTypeCode
是唯一的。 (也验证变量的数据类型!)
DECLARE @StatusTypeId int
SELECT @StatusTypeId = Id
FROM PrelimOrderStatusType
WHERE StatusTypeCode = 'Draft'
SELECT Salesperson.*
FROM Salesperson
WHERE Salesperson.EndDate IS NULL
AND EXISTS ( SELECT *
FROM SalesOrder
ON SalesOrder.SalespersonId = Salesperson.Id
JOIN PrelimOrder
ON PrelimOrder.OrderId = SalesOrder.Id
JOIN PrelimOrderStatus
ON PrelimOrderStatus.Id = PrelimOrder.CurrentStatusId
AND PrelimOrderStatus.StatusTypeId = @StatusTypeId)
如果它没有帮助,你能给出正在使用的索引的定义吗?