8

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

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

谢谢

4

9 回答 9

19

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

以下是一些参考资料,如果您研究此问题,这只是冰山一角:

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

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

于 2008-09-01T21:20:24.893 回答
2

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

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

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

于 2009-01-20T13:57:13.610 回答
2

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

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

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

于 2010-07-13T17:06:42.770 回答
2

您可以在大多数情况下避免使用游标,但有时这是必要的。

请记住,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
于 2014-12-08T13:31:55.107 回答
1

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

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

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

于 2008-08-31T22:16:31.567 回答
1

要回答 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 小时才能执行的作业现在减少到几分钟,有时甚至几秒钟。

于 2012-12-30T22:47:28.837 回答
1

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

WHILE 循环 Temp tablolar 派生表 关联子查询 CASE 语句 多次询问 通常,游标操作也可以通过非游标技术实现。

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

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

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

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

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

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

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

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

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

于 2018-07-12T09:01:33.670 回答
-1

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

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

于 2008-09-01T21:05:26.830 回答
-2

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

于 2008-09-18T09:11:24.377 回答