SQL Server快进游标

SQL Server快进游标,sql,sql-server,stored-procedures,Sql,Sql Server,Stored Procedures,一般认为,应尽可能避免在存储过程中使用游标(替换为基于集合的逻辑等)。如果您需要迭代某些数据,并且可以以只读方式进行,那么快进(只读前进)游标的效率是否比while循环低?从我的调查来看,游标选项通常更快,并且使用更少的读取和cpu时间。我没有做过任何广泛的测试,但这是其他人发现的吗?这种类型的游标(快进)是否会带来额外的开销或资源,这些开销或资源可能会很昂贵,我不知道 所有关于不使用游标的讨论都是关于在基于集合的方法可用时避免使用游标,以及使用可更新的游标等吗 感谢人们避免使用游标,因为它们通

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

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


感谢人们避免使用游标,因为它们通常比简单的while循环更难编写。然而,while循环可能会很昂贵,因为您经常从表中选择临时或其他数据

使用只读快进游标,数据保存在内存中,并专门设计用于循环


突出显示平均游标运行速度比while循环快50倍。

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


您所指的“普遍接受”是有效的,但现在已经过时且不正确了-假设快进游标的行为与广告中的一样,并且执行。基于SQL2005和更高版本进行一些测试和研究虽然快进游标在SQLServer2005中确实有一些优化,但就性能而言,它们与基于集合的查询并不接近。很少有情况下游标逻辑不能被基于集合的查询所取代。游标总是天生较慢,部分原因是您必须不断中断执行以填充局部变量

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


如果您想要比快进更快的光标,请使用静态光标。他们比快进快。速度不是很快,但会有所不同

这一回答希望整合迄今为止给出的答复

1) 如果可能的话,在查询中使用基于集合的逻辑,即尝试使用适当的
FROM
子句或嵌套查询中的
来使用
选择
插入
更新
删除
——这些几乎总是会更快

2) 如果无法执行上述操作,则在SQL Server 2005+
快进中,游标效率高且性能良好,应优先使用while循环。

“如果您希望游标比快进更快,请使用静态游标。它们比快进快。速度不是非常快,但会有所不同。”

别那么快!据微软称: 通常,在进行这些转换时,光标类型会降级为“更昂贵”的光标类型。通常,(快速)只向前的光标性能最高,其次是动态、键集,最后是静态的,通常性能最低


发件人:

回答迈尔最初的问题

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

为了补充Eric Z.Beard在这条帖子上发表的内容,并进一步回答以下问题

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

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

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

尽管自SQLServer2005以来MS在游标的工作方式上做了一些改进,但与正确编写的基于集合的代码相比,“快速游标”这个术语仍然是一个矛盾的说法。即使在甲骨文中也是如此。过去我在Oracle工作了短短3年,但我的工作是改进现有代码的性能。当我将游标转换为基于集合的代码时,实现了大多数真正实质性的改进。许多工作都需要p
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