背景
我注意到在尝试运行总查询时,有时估算的计划只是显示“获取查询”

并且实际计划显示了从聚集索引扫描中重复进行的提取

在其他情况下(例如,在查询中添加TOP时),估算的计划会显示一个“人口查询”阶段,该阶段会填充工作表

实际计划显示了聚集索引扫描以填充工作表,然后针对该工作表重复查找。


  • 在选择一种方法而不是另一种方法时,SQL Server使用什么标准?
  • 我是否认为第一种方法(不增加工作表填充步骤)效率更高?

  • (奖金问题:如果有人能解释为什么第一个查询中的每次扫描都算作2个逻辑读,这可能也很有启发性)
    附加信息
    我发现this article here可以解释FAST_FORWARD游标可以使用动态计划,也可以使用静态计划。在这种情况下,第一个查询似乎使用的是动态计划,第二个查询使用的是静态计划。
    我也发现如果我尝试
    SET @C2 = CURSOR DYNAMIC TYPE_WARNING FOR SELECT TOP ...
    
    游标被隐式转换为keyset游标,因此很明显,动态游标不支持TOP构造,这也许是因为Ruben的答案-仍在寻找对此的明确解释。
    但是我也读过,动态游标比静态游标(source 1source 2)慢,这使我感到惊讶,因为静态变量必须读取源数据,复制它,然后读取副本而不是仅仅读取源数据。 The article I referenced earlier提到动态游标使用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
    

    最佳答案

    在选择一种方法而不是另一种方法时,SQL Server使用什么标准?

    这主要是基于成本的决策。引用您所链接的文章,他说:“在动态计划看起来很有希望的情况下,可能会试探性地跳过成本比较。这主要发生在极其便宜的查询中,尽管细节深奥。”

    我是否认为第一种方法(不增加工作表填充步骤)效率更高?

    这取决于。动态和静态游标计划具有不同的优点和缺点。如果最终将触摸所有行,则静态计划可能会执行得更好。稍后再详细介绍。

    显然,动态游标不支持TOP构造

    这是真的。动态游标计划中的所有迭代器都必须能够保存和恢复状态,向前和向后扫描,为每个输出行处理一个输入行并且是非阻塞的。通常,Top不满足所有这些要求; CQScanTopNew类未实现必要的Set/Get/Goto/Marker()ReverseDirection()方法(以及其他方法)。

    我还读过动态游标往往比静态游标慢。

    对于Transact-SQL游标来说,通常是这样,其中大多数或所有游标集都被触摸了。保存和还原动态查询计划的状态会产生成本。在每次调用中处理一行,并且最终触及所有行的情况下,这种保存/恢复开销得以最大化。

    静态游标的开销是制作集合的副本(这可能是大集合的主要因素),但是每行检索的成本却很小。键集具有比静态更高的每行检索开销,因为键集必须外部联接回源表才能检索非键列。

    当访问集合的一小部分和/或一次不行检索时,动态游标是最佳的。在许多常见的游标方案中,这是一种典型的访问模式,但并非博客文章倾向于测试的模式:)

    如果有人能解释为什么第一个查询中的每次扫描都算作2个逻辑读取,这可能也很有启发性

    这取决于为扫描保存状态以及对读数进行计数的方式。

    我之前引用的文章提到动态游标使用标记。谁能解释这些是什么?仅仅是RID或CI密钥,还是其他?

    动态游标计划中的每个迭代器都存在标记,而不仅仅是访问方法。 “标记”是在中断点重新启动计划迭代器所需的所有状态信息。对于访问方法,RID或索引键(如有必要,带有唯一符)是其中的主要部分,但无论如何都不是全部。

    关于sql - FAST_FORWARD游标什么时候会有工作表(这是要避免的事情)?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7869172/

    10-11 03:38
    查看更多