使用相同计划执行查询的巨大差异
Vast Difference In Query Execution Using Identical Plan
我不是 DBA,所以我会尽力解释这一点。我们正试图弄清楚为什么有些查询似乎执行得非常快而其他时候却不是。查询本身没有变化,计划详细信息在下面的两个计划 ID 之间也没有变化,直到每个节点上的成本百分比。我们过去曾尝试强制执行更好的计划,但由于统计数据更新等 "schema" 认为它发生了变化,并且经常抛出以前有效的强制计划,尽管我们仍在调查强制这些计划是否正在解决短期内有没有问题
这看起来很奇怪,就像 SQL 服务器在一天中随机地在 2 个计划之间来回切换,而你会认为它会停止使用更长的 运行 计划,即使我们除了执行时间之外,无法弄清楚这 2 个计划实际上有什么不同。
Link 到 Query Plan,WinMerge XML 比较两个计划 XML 显示它们确实相同。
我们正在使用 SQL Server 2017 RTM CU14,可以根据要求或需要提供有关版本或配置的更多详细信息。
这不是答案 "per se",更像是扩展评论。
计划不是 "complex",它很简单而且似乎是你能得到的最好的(不包括游标)。
两个集群 tables,寻求并合并连接,聚合为 sum()s 并转储到 tempdb 中的集群 table 以供游标遍历。
两个要点(性能方面)似乎是:
- the obvious clustered index insert (writing 1.3mil rows)
- the sort operator, before the stream aggregate. this most likely spills in tempdb for sorting (1.3 mil rows) . You could verify that by
checking an actual execution plan.
A "not so direct" 观察是行数:聚合前 130 万,聚合后 130 万。一个猜测是聚合实际上不是用于计算总和的聚合(sum() 是无意义的{?}),而更像是聚合的 "distinct type"(使用 sum 因为 inventsum colums 是数字)。此处甚至可能不需要不同的聚合,您可以通过检查 ITEMID(INVENTSUM table 的)的值是否实际上是唯一的来验证这一点。如果值不唯一,则检查 ITEMID 是否在每个 AREAID&INVENTDIMID 中是唯一的(即,一个项目在一个区域和发明{ory}中只能出现一次)。如果任何一个为真,那么对于特定查询,ITEMID 可以被认为是唯一的,因此根本不需要聚合 (GROUP BY cols...)。
理论上,如果 ITEMID 是 "unique" 并且特定查询的微优化是目标,那么 "better" 执行可以通过 AREAID、INVENTDIMID、ITEMID
[create unique clustered index inventsumclidx on INVENTSUM(AREAID,INVENTDIMID, ITEMID)]
并按 ITEMID、INVENTDIMID 选择组
GROUP BY A.ITEMID, A.INVENTDIMID, B.CONFIGID, B.INVENTSIZEID ....
ORDER BY A.ITEMID, A.INVENTDIMID, B.CONFIGID,..
这将消除合并前的排序运算符(所有信息已按 INVENTDIMID 排序)和聚合运算符(所有信息都是唯一的)。如果不溢出到 tempdb 以在 SEGMENT 之前进行排序,则执行时间可能会减少 50%。
如果你有测试用例,你可以试试看是否有什么不同:
--drop existing clustered index
--drop index [I_174ITEMDIMIDX] on dbo.[INVENTSUM]
create unique clustered index inventsumclidx on INVENTSUM(AREAID,INVENTDIMID, ITEMID);
go
--adjust these values
declare @P1 int = 20, @P2 int = 50, @P3 int = 15, @P4 int = 500;
declare cur cursor FAST_FORWARD for
SELECT
SUM(A.POSTEDQTY),SUM(A.POSTEDVALUE),SUM(A.PHYSICALVALUE),SUM(A.DEDUCTED),SUM(A.REGISTERED),SUM(A.RECEIVED),SUM(A.PICKED),SUM(A.RESERVPHYSICAL),SUM(A.RESERVORDERED),SUM(A.ONORDER),SUM(A.ORDERED),SUM(A.ARRIVED),SUM(A.QUOTATIONRECEIPT),SUM(A.QUOTATIONISSUE),SUM(A.PHYSICALINVENT),SUM(A.JSPOTENCIES),SUM(A.JSPOTENCIES2_),SUM(A.JSPOSTEDDUALUNITQTY),SUM(A.JSPICKEDDUALUNITQTY),SUM(A.JSRECEIVEDDUALUNITQTY),SUM(A.JSDEDUCTEDDUALUNITQTY),SUM(A.JSREGISTEREDDUALUNITQTY),SUM(A.AVAILPHYSICAL),SUM(A.AVAILORDERED),A.ITEMID,B.CONFIGID,B.INVENTSIZEID,B.INVENTCOLORID,B.INVENTLOCATIONID,B.WMSLOCATIONID
FROM INVENTSUM A, INVENTDIM B WITH(INDEX(I_698DIMIDIDX))
WHERE ((A.DATAAREAID=@P1) AND ((A.CLOSED=@P2) AND (A.CLOSEDQTY=@P3))) AND ((B.DATAAREAID=@P4) AND (A.INVENTDIMID=B.INVENTDIMID))
GROUP BY A.ITEMID, A.INVENTDIMID, B.CONFIGID,B.INVENTSIZEID,B.INVENTCOLORID,B.INVENTLOCATIONID,B.WMSLOCATIONID
ORDER BY A.ITEMID, A.INVENTDIMID, B.CONFIGID,B.INVENTSIZEID,B.INVENTCOLORID,B.INVENTLOCATIONID,B.WMSLOCATIONID
OPTION(FAST 1);
open cur;
fetch next from cur;
close cur;
deallocate cur;
我不是 DBA,所以我会尽力解释这一点。我们正试图弄清楚为什么有些查询似乎执行得非常快而其他时候却不是。查询本身没有变化,计划详细信息在下面的两个计划 ID 之间也没有变化,直到每个节点上的成本百分比。我们过去曾尝试强制执行更好的计划,但由于统计数据更新等 "schema" 认为它发生了变化,并且经常抛出以前有效的强制计划,尽管我们仍在调查强制这些计划是否正在解决短期内有没有问题
这看起来很奇怪,就像 SQL 服务器在一天中随机地在 2 个计划之间来回切换,而你会认为它会停止使用更长的 运行 计划,即使我们除了执行时间之外,无法弄清楚这 2 个计划实际上有什么不同。
Link 到 Query Plan,WinMerge XML 比较两个计划 XML 显示它们确实相同。
我们正在使用 SQL Server 2017 RTM CU14,可以根据要求或需要提供有关版本或配置的更多详细信息。
这不是答案 "per se",更像是扩展评论。
计划不是 "complex",它很简单而且似乎是你能得到的最好的(不包括游标)。 两个集群 tables,寻求并合并连接,聚合为 sum()s 并转储到 tempdb 中的集群 table 以供游标遍历。 两个要点(性能方面)似乎是:
- the obvious clustered index insert (writing 1.3mil rows)
- the sort operator, before the stream aggregate. this most likely spills in tempdb for sorting (1.3 mil rows) . You could verify that by checking an actual execution plan.
A "not so direct" 观察是行数:聚合前 130 万,聚合后 130 万。一个猜测是聚合实际上不是用于计算总和的聚合(sum() 是无意义的{?}),而更像是聚合的 "distinct type"(使用 sum 因为 inventsum colums 是数字)。此处甚至可能不需要不同的聚合,您可以通过检查 ITEMID(INVENTSUM table 的)的值是否实际上是唯一的来验证这一点。如果值不唯一,则检查 ITEMID 是否在每个 AREAID&INVENTDIMID 中是唯一的(即,一个项目在一个区域和发明{ory}中只能出现一次)。如果任何一个为真,那么对于特定查询,ITEMID 可以被认为是唯一的,因此根本不需要聚合 (GROUP BY cols...)。
理论上,如果 ITEMID 是 "unique" 并且特定查询的微优化是目标,那么 "better" 执行可以通过 AREAID、INVENTDIMID、ITEMID
[create unique clustered index inventsumclidx on INVENTSUM(AREAID,INVENTDIMID, ITEMID)]
并按 ITEMID、INVENTDIMID 选择组
GROUP BY A.ITEMID, A.INVENTDIMID, B.CONFIGID, B.INVENTSIZEID ....
ORDER BY A.ITEMID, A.INVENTDIMID, B.CONFIGID,..
这将消除合并前的排序运算符(所有信息已按 INVENTDIMID 排序)和聚合运算符(所有信息都是唯一的)。如果不溢出到 tempdb 以在 SEGMENT 之前进行排序,则执行时间可能会减少 50%。
如果你有测试用例,你可以试试看是否有什么不同:
--drop existing clustered index
--drop index [I_174ITEMDIMIDX] on dbo.[INVENTSUM]
create unique clustered index inventsumclidx on INVENTSUM(AREAID,INVENTDIMID, ITEMID);
go
--adjust these values
declare @P1 int = 20, @P2 int = 50, @P3 int = 15, @P4 int = 500;
declare cur cursor FAST_FORWARD for
SELECT
SUM(A.POSTEDQTY),SUM(A.POSTEDVALUE),SUM(A.PHYSICALVALUE),SUM(A.DEDUCTED),SUM(A.REGISTERED),SUM(A.RECEIVED),SUM(A.PICKED),SUM(A.RESERVPHYSICAL),SUM(A.RESERVORDERED),SUM(A.ONORDER),SUM(A.ORDERED),SUM(A.ARRIVED),SUM(A.QUOTATIONRECEIPT),SUM(A.QUOTATIONISSUE),SUM(A.PHYSICALINVENT),SUM(A.JSPOTENCIES),SUM(A.JSPOTENCIES2_),SUM(A.JSPOSTEDDUALUNITQTY),SUM(A.JSPICKEDDUALUNITQTY),SUM(A.JSRECEIVEDDUALUNITQTY),SUM(A.JSDEDUCTEDDUALUNITQTY),SUM(A.JSREGISTEREDDUALUNITQTY),SUM(A.AVAILPHYSICAL),SUM(A.AVAILORDERED),A.ITEMID,B.CONFIGID,B.INVENTSIZEID,B.INVENTCOLORID,B.INVENTLOCATIONID,B.WMSLOCATIONID
FROM INVENTSUM A, INVENTDIM B WITH(INDEX(I_698DIMIDIDX))
WHERE ((A.DATAAREAID=@P1) AND ((A.CLOSED=@P2) AND (A.CLOSEDQTY=@P3))) AND ((B.DATAAREAID=@P4) AND (A.INVENTDIMID=B.INVENTDIMID))
GROUP BY A.ITEMID, A.INVENTDIMID, B.CONFIGID,B.INVENTSIZEID,B.INVENTCOLORID,B.INVENTLOCATIONID,B.WMSLOCATIONID
ORDER BY A.ITEMID, A.INVENTDIMID, B.CONFIGID,B.INVENTSIZEID,B.INVENTCOLORID,B.INVENTLOCATIONID,B.WMSLOCATIONID
OPTION(FAST 1);
open cur;
fetch next from cur;
close cur;
deallocate cur;