【问题标题】:SQL Server Fast Forward CursorsSQL Server 快进游标
【发布时间】:2010-09-07 09:54:16
【问题描述】:

普遍认为,应尽可能避免在存储过程中使用游标(替换为基于集合的逻辑等)。如果您考虑需要迭代某些数据并且可以以只读方式进行的情况,那么快进(只读转发)游标是否比说 while 循环效率低下?从我的调查来看,游标选项通常更快并且使用更少的读取和 cpu 时间。我没有进行任何广泛的测试,但这是其他人发现的吗?这种类型的游标(快进)是否会带来额外的开销或资源,这些开销或资源可能很昂贵,我不知道。

所有关于不使用游标的讨论真的是在基于集合的方法可用时避免使用游标,以及使用可更新游标等吗?

谢谢

【问题讨论】:

    标签: sql sql-server stored-procedures


    【解决方案1】:

    虽然快进游标在 Sql Server 2005 中确实有一些优化,但它们在性能方面接近于基于集合的查询。很少有游标逻辑不能被基于集合的查询替换的情况。游标本质上总是较慢,部分原因是您必须不断中断执行以填充局部变量。

    这里有一些参考资料,如果你研究这个问题,这只是冰山一角:

    http://www.code-magazine.com/Article.aspx?quickid=060113

    http://dataeducation.com/re-inventing-the-recursive-cte/

    【讨论】:

      【解决方案2】:

      这个答案希望能巩固迄今为止给出的答复。

      1) 如果可能的话,为您的查询使用基于集合的逻辑,即尝试仅使用 SELECTINSERTUPDATEDELETE 以及适当的 FROM 子句或嵌套查询 - 这些将几乎总是更快。

      2) 如果上述情况不可行,那么在 SQL Server 2005+ 中,FAST FORWARD 游标效率高且性能良好,应优先使用 while 循环。

      【讨论】:

        【解决方案3】:

        “如果您想要一个比 FAST FORWARD 更快的游标,那么请使用 STATIC 游标。它们比 FAST FORWARD 更快。不是特别快,但可以有所作为。”

        没那么快!据微软称: “通常,当这些转换发生时,游标类型会降级为‘更昂贵’的游标类型。通常,(FAST)FORWARD-ONLY 游标性能最高,其次是 DYNAMIC、KEYSET,最后是 STATIC,通常是最低的高性能。”

        来自:http://blogs.msdn.com/b/mssqlisv/archive/2006/06/23/644493.aspx

        【讨论】:

          【解决方案4】:

          大多数时候你可以避免使用光标,但有时这是必要的。

          请记住,FAST_FORWARD 是 DYNAMIC ... FORWARD_ONLY 您可以与 STATIC 游标一起使用。

          尝试在万圣节问题上使用它,看看会发生什么!!!

          IF OBJECT_ID('Funcionarios') IS NOT NULL
          DROP TABLE Funcionarios
          GO
          
          CREATE TABLE Funcionarios(ID          Int IDENTITY(1,1) PRIMARY KEY,
                                    ContactName Char(7000),
                                    Salario     Numeric(18,2));
          GO
          
          INSERT INTO Funcionarios(ContactName, Salario) VALUES('Fabiano', 1900)
          INSERT INTO Funcionarios(ContactName, Salario) VALUES('Luciano',2050)
          INSERT INTO Funcionarios(ContactName, Salario) VALUES('Gilberto', 2070)
          INSERT INTO Funcionarios(ContactName, Salario) VALUES('Ivan', 2090)
          GO
          
          CREATE NONCLUSTERED INDEX ix_Salario ON Funcionarios(Salario)
          GO
          
          -- Halloween problem, will update all rows until then reach 3000 !!!
          UPDATE Funcionarios SET Salario = Salario * 1.1
            FROM Funcionarios WITH(index=ix_Salario)
           WHERE Salario < 3000
          GO
          
          -- Simulate here with all different CURSOR declarations
          -- DYNAMIC update the rows until all of then reach 3000
          -- FAST_FORWARD update the rows until all of then reach 3000
          -- STATIC update the rows only one time. 
          
          BEGIN TRAN
          DECLARE @ID INT
          DECLARE TMP_Cursor CURSOR DYNAMIC 
          --DECLARE TMP_Cursor CURSOR FAST_FORWARD
          --DECLARE TMP_Cursor CURSOR STATIC READ_ONLY FORWARD_ONLY
              FOR SELECT ID 
                    FROM Funcionarios WITH(index=ix_Salario)
                   WHERE Salario < 3000
          
          OPEN TMP_Cursor
          
          FETCH NEXT FROM TMP_Cursor INTO @ID
          
          WHILE @@FETCH_STATUS = 0
          BEGIN
            SELECT * FROM Funcionarios WITH(index=ix_Salario)
          
            UPDATE Funcionarios SET Salario = Salario * 1.1 
             WHERE ID = @ID
          
            FETCH NEXT FROM TMP_Cursor INTO @ID
          END
          
          CLOSE TMP_Cursor
          DEALLOCATE TMP_Cursor
          
          SELECT * FROM Funcionarios
          
          ROLLBACK TRAN
          GO
          

          【讨论】:

            【解决方案5】:

            人们避免使用游标,因为它们通常比简单的 while 循环更难编写,但是,while 循环可能会很昂贵,因为您不断地从表中选择数据,无论是临时的还是其他的。

            使用只读快进的游标,数据保存在内存中,并且专为循环而设计。

            This article 强调,游标的平均运行速度比 while 循环快 50 倍。

            【讨论】:

              【解决方案6】:

              回答 Mile 最初的问题...

              快进、只读、静态游标(被亲切地称为“消防软管游标”)通常与等效的 Temp Table 和 While 循环一样快或更快,因为这样的游标只不过是一个 Temp Table 和一个在幕后进行了一些优化的 While 循环。

              添加 Eric Z. Beard 在此线程上发布的内容并进一步回答...的问题

              “所有关于不使用游标的讨论真的是为了避免使用 当基于集合的方法可用时,游标的数量,以及使用 可更新游标等”

              是的。除了极少数例外,编写适当的基于集合的代码来执行与大多数游标相同的事情所需的时间和代码更少,并且具有使用更少资源的额外好处,并且通常比游标或 While 循环运行得快得多。一般来说,除了某些管理任务之外,确实应该避免它们,以支持正确编写的基于集合的代码。当然,每个“规则”都有例外,但对于游标、While 循环和其他形式的 RBAR,大多数人可以用一只手计算例外情况,而无需动用所有手指。 ;-)

              还有“隐藏的 RBAR”的概念。这是看起来基于集合但实际上不是的代码。这种类型的“基于集合”的代码是某些人接受 RBAR 方法并说他们“OK”的原因。例如,在我的书中,使用聚合(SUM)相关子查询和不等式来解决运行总数问题来构建运行总数并不是真正基于集合的。相反,它是类固醇上的 RBAR,因为对于计算的每一行,它必须以 N*(N+1)/2 的速率重复“接触”许多其他行。这被称为“三角连接”,至少是完全笛卡尔连接(交叉连接或“方形连接”)的一半。

              尽管自 SQL Server 2005 以来 MS 对光标的工作方式进行了一些改进,但与正确编写的基于集合的代码相比,“快速光标”一词仍然是一个矛盾的说法。即使在 Oracle 中也是如此。过去我在 Oracle 工作了短短 3 年,但我的工作是改进现有代码的性能。当我将游标转换为基于集合的代码时,实现了大多数真正实质性的改进。许多以前需要 4 到 8 小时才能执行的作业现在减少到几分钟,有时甚至几秒钟。

              【讨论】:

                【解决方案7】:

                使用光标的一些替代方法:

                WHILE 循环 温度表 派生表 关联子查询 案例陈述 多次审讯 通常,游标操作也可以通过非游标技术实现。

                如果您确定需要使用游标,则应尽可能减少要处理的记录数。这样做的一种方法是首先将要处理的记录放入临时表中,而不是原始表,而是将使用临时表中的记录的游标。使用此路径时,假设临时表中的记录数与原始表相比已大大减少。记录越少,游标完成越快。

                一些影响性能的游标属性包括:

                FORWARD_ONLY:支持仅使用 FETCH NEXT 将游标从第一行转发到末尾。除非设置为 KEYSET 或 STATIC,否则每次调用 fetch 时都会重新评估 SELECT 子句。

                STATIC:创建已创建数据的临时副本并由游标使用。这可以防止每次调用游标时重新计算游标,从而提高性能。这不允许修改游标类型,并且在调用 fetch 时不会反映对表的更改。

                KEYSET:被游标的行放置在 tempdb 下的表中,并且在调用 fetch 时反映对非键列的更改。但是,不会反映添加到表中的新记录。使用键集游标,不会再次评估 SELECT 语句。

                动态:对表的所有更改都反映在游标中。每次调用 fetch 时都会重新评估游标。它使用大量资源并对性能产生不利影响。

                FAST_FORWARD:游标是单向的,如FORWARD_ONLY,但指定游标为只读。 FORWARD_ONLY 是一种性能提升,并且不会在每次提取时重新评估游标。如果它适合编程,它会提供最佳性能。

                OPTIMISTIC:此选项可用于更新游标中的行。如果获取并更新了一行,并且在获取和更新操作之间更新了另一行,则游标更新操作将失败。如果使用了可以执行行更新的 OPTIMISTIC 游标,则不应由其他进程对其进行更新。

                注意:如果未指定 cursore,则默认为 FORWARD_ONLY。

                【讨论】:

                  【解决方案8】:

                  在 SQL Server 中避免使用游标的“最佳实践”可以追溯到 SQL Server 2000 和更早的版本。 SQL 2005 中引擎的重写解决了与游标问题相关的大部分问题,特别是在引入快进选项时。游标并不一定比基于集合的差,并且在 Oracle PL/SQL (LOOP) 中被广泛且成功地使用。

                  您提到的“普遍接受”有效的,但现在已过时且不正确 - 继续假设快进光标的行为与宣传和执行的一样。做一些测试和研究,基于 SQL2005 及更高版本的发现

                  【讨论】:

                  • 虽然游标在 Oracle PL/SQL 中被成功使用是事实,但这与它们在 SQL Server 中的使用没有任何关系。上次我使用 Oracle 时,您无法编写 INSERT ... FROM 或 UPDATE ... FROM 语句,因此您需要使用游标。锁定模型也完全不同,所以我认为比较不公平。
                  • 不,西蒙。它既不是“过时的”也不是“不正确的”。尽管 MS 可能改进了游标的处理方式,并且除了某些管理任务之外,游标必然比正确编写的基于集合的代码更糟糕......即使在 Oracle 中也是如此。
                  【解决方案9】:

                  如果您想要比 FAST FORWARD 更快的游标,请使用 STATIC 游标。它们比 FAST FORWARD 更快。不是非常快,但可以有所作为。

                  【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多