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

所有关于不使用游标的讨论实际上都是在基于集合的方法可用时避免使用游标,以及使用可更新游标等。

谢谢

有帮助吗?

解决方案

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

您提到的“普遍接受” 曾是 有效,但现在已经过时且不正确 - 继续假设快进光标的行为如广告中所宣传和执行的那样。根据 SQL2005 及更高版本的发现进行一些测试和研究

其他提示

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

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

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

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

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

1)如果可能的话,使用基于集合的逻辑进行查询,即尝试使用 SELECT, INSERT, UPDATE 或者 DELETE 与适当的 FROM 子句或嵌套查询 - 这些几乎总是更快。

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

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

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

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

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

请记住,FAST_FORWARD 是动态的......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

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

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

本文 强调平均光标的运行速度比 while 循环快 50 倍。

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

虽然循环临时曲面衍生表相关的子查询案例语句经常进行多个询问,但也可以通过非cursor技术来实现光标操作。

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

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

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

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

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

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

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

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

笔记:如果未指定cursore,则默认为FORWARD_ONLY。

为了回答迈尔最初的问题......

快进、只读、静态游标(亲切地称为“消防水带游标”)通常与等效的临时表和 While 循环一样快或更快,因为此类游标只不过是临时表和 While 循环,已经在幕后进行了一些优化。

添加到 Eric Z.胡子张贴在这个帖子上并进一步回答...的问题

“所有谈论都没有真正关于在可用的基于设置的方法以及使用可更新光标等的使用时真正避免使用光标的使用。”

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

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

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

如果您想要比 FAST FORWARD 更快的游标,请使用 STATIC 游标。它们比 FAST FORWARD 更快。虽然速度不是特别快,但可以有所作为。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top