SQL Server快进游标

时间:2008-08-31 18:56:42

标签: sql sql-server stored-procedures

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

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

由于

9 个答案:

答案 0 :(得分:17)

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

以下是一些参考文献,如果您研究这个问题,这只会是冰山一角:

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

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

答案 1 :(得分:2)

“如果你想要比FAST FORWARD更快的游标,那么使用STATIC游标。它们比FAST FORWARD更快。速度不是很快但可以有所作为。”

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

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

答案 2 :(得分: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

答案 3 :(得分:1)

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

使用一个只读快进的光标,数据保存在内存中,专门用于循环。

This article强调平均光标比while循环快50倍。

答案 4 :(得分:1)

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

1)如果可能的话,为查询使用基于集合的逻辑,即尝试仅使用SELECTINSERTUPDATEDELETE使用相应的{{ 1}}子句或嵌套查询 - 这些几乎总是更快。

2)如果无法实现上述目标,则在SQL Server 2005+ FROM中,游标效率高,性能良好,应优先使用while循环。

答案 5 :(得分:1)

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

WHILE循环 温度小叶 派生表 关联子查询 案例陈述 多重审讯 通常,也可以使用非光标技术来实现光标操作。

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

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

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

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

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

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

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

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

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

答案 6 :(得分:0)

回答Mile的原始问题......

快进,只读,静态光标(被亲切地称为“消防软管光标”)通常比等效的临时表和静态循环快或快,因为这样的光标只不过是临时表和虽然循环已经在幕后进行了优化。

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

  

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

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

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

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

答案 7 :(得分:-1)

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

你提到的'普遍接受'是有效,但现在已经过时且不正确 - 假设快进游标的行为与宣传和执行一样。根据SQL2005及更高版本的基础进行一些测试和研究

答案 8 :(得分:-2)

如果你想要比FAST FORWARD更快的游标,那么使用STATIC游标。它们比快速前进更快。不是非常快,但可以有所作为。