这是一个纯粹的学术问题,它不会导致问题,我只是想听听对这种行为的任何解释.
采取标准问题Itzik Ben-Gan交叉加入CTE计数表:
USE [master] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE FUNCTION [dbo].[TallyTable] ( @N INT ) RETURNS TABLE WITH SCHEMABINDING AS RETURN ( WITH E1(N) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 ) -- 1*10^1 or 10 rows,E2(N) AS (SELECT 1 FROM E1 a,E1 b) -- 1*10^2 or 100 rows,E4(N) AS (SELECT 1 FROM E2 a,E2 b) -- 1*10^4 or 10,000 rows,E8(N) AS (SELECT 1 FROM E4 a,E4 b) -- 1*10^8 or 100,000,000 rows SELECT TOP (@N) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS N FROM E8 ) GO
发出将创建100万行号表的查询:
SELECT COUNT(N) FROM dbo.TallyTable(1000000) tt
查看此查询的并行执行计划:
请注意,聚集流运算符之前的“实际”行计数为1,004,588.在收集流运算符之后,行计数是预期的1,000.更奇怪的是,价值不一致,并且因运行而异. COUNT的结果始终是正确的.
再次发出查询,强制执行非并行计划:
SELECT COUNT(N) FROM dbo.TallyTable(1000000) tt OPTION (MAXDOP 1)
这次所有运算符都显示正确的“实际”行数.
到目前为止,我在2005SP3和2008R2上尝试了这个,两者都有相同的结果.有什么可能导致这个问题的想法?
解决方法
行在内部从生产者传递到消费者线程的数据包(因此是CXPACKET – 类交换数据包),而不是一次一行.交易所内部有一定数量的缓冲.此外,从Gather Streams的消费者端关闭管道的调用必须在控制包中传递回生产者线程.调度和其他内部考虑意味着并行计划始终具有一定的“停止距离”.
因此,您经常会看到这种行计数差异,其中实际需要小于子树的整个潜在行集.在这种情况下,TOP将执行带到“早期结束”.
更多信息:
Parallel Execution Plans Suck
Understanding and Using Parallelism in SQL Server.