背景
在尝试运行总计查询时,我注意到有时预估的计划只显示一个“获取查询”。
实际计划显示从聚集索引扫描中重复获取
在其他情况下(例如向查询添加TOP
时),估计的计划显示一个“Population Query”阶段,该阶段填充工作表。
实际计划显示使用聚集索引扫描来填充工作表,然后对该工作表进行重复查找。
问题
- SQL Server在选择方法时使用什么标准?
- 我认为第一种方法(不需要额外的工作表填充步骤)更有效,我的想法正确吗?
(奖励问题:如果有人能解释为什么第一个查询中的每个扫描都计为2个逻辑读取,那可能会很有启发性)
附加信息
我找到了这篇文章,其中解释了FAST_FORWARD
游标可以使用动态计划或静态计划。在本例中,第一个查询似乎使用动态计划,而第二个查询使用静态计划。
我还发现,如果我尝试
SET @C2 = CURSOR DYNAMIC TYPE_WARNING FOR SELECT TOP ...
光标会被隐式转换为一个keyset
光标,因此清楚地表明动态光标不支持TOP
构造,可能是出于Ruben回答中的原因 - 仍在寻找这方面的明确解释。
然而,我也读到过动态光标往往比其静态对应物慢(来源1, 来源2),这对我来说似乎令人惊讶,因为静态光标必须读取源数据,复制它,然后读取副本,而不仅仅是读取源数据。我之前引用的那篇文章提到动态光标使用markers
。有人能解释一下这些是什么吗?它只是RID或CI密钥,还是其他东西?
脚本
SET STATISTICS IO OFF
CREATE TABLE #T ( ord INT IDENTITY PRIMARY KEY, total INT, Filler char(8000))
INSERT INTO #T (total) VALUES (37),(80),(55),(31),(53)
DECLARE @running_total INT,
@ord INT,
@total INT
SET @running_total = 0
SET STATISTICS IO ON
DECLARE @C1 AS CURSOR;
SET @C1 = CURSOR FAST_FORWARD FOR SELECT ord, total FROM #T ORDER BY ord;
OPEN @C1;
PRINT 'Initial FETCH C1'
FETCH NEXT FROM @C1 INTO @ord, @total ;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @running_total = @running_total + @total
PRINT 'FETCH C1'
FETCH NEXT FROM @C1 INTO @ord, @total ;
END
SET @running_total = 0
SET STATISTICS IO ON
DECLARE @C2 AS CURSOR;
SET @C2 = CURSOR FAST_FORWARD FOR SELECT TOP 5 ord, total FROM #T ORDER BY ord;
OPEN @C2;
PRINT 'Initial FETCH C2'
FETCH NEXT FROM @C2 INTO @ord, @total ;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @running_total = @running_total + @total
PRINT 'FETCH C2'
FETCH NEXT FROM @C2 INTO @ord, @total ;
END
PRINT 'End C2'
DROP TABLE #T
top 5
在一个事务中检索,就像一个快照。如果没有工作表,你可能会得到一个包含从未同时存在于表中的行的top 5
。 - Andomar#temp
表,因此 SQL Server 可能会认识到它将始终保持一致,因为其他事务无法修改它。我也尝试了SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
,仍然看到相同的结果。(而且SET ROWCOUNT 5
也不会改变两个计划) - Martin Smith