SQL Server快进游标

wlwcrazw  于 2023-01-16  发布在  SQL Server
关注(0)|答案(9)|浏览(183)

一般认为,应尽可能避免在存储过程中使用游标(替换为基于集合的逻辑等)。如果你需要迭代一些数据,并且可以以只读的方式进行,快进(只读向前)游标比say while循环效率更低还是更低?从我的调查来看,游标选项似乎通常更快,使用的读取和cpu时间更少。但这是别人发现的吗?这种类型的游标(快进)是否会带来额外的开销或资源,这可能是昂贵的,我不知道。
所有关于不使用游标的讨论实际上都是在基于集合的方法可用时避免使用游标,以及使用可更新游标等吗?

ztigrdn8

ztigrdn81#

虽然在Sql Server 2005中快进游标确实有一些优化,但就性能而言,它们与基于集合的查询相差无几。很少有基于集合的查询无法替换游标逻辑的情况。游标总是天生较慢,部分原因是您必须不断中断执行以填充局部变量。
以下是一些参考资料,如果你研究这个问题,这些资料只会是冰山一角:
http://www.code-magazine.com/Article.aspx?quickid=060113
http://dataeducation.com/re-inventing-the-recursive-cte/

x4shl7ld

x4shl7ld2#

本答复希望综合迄今为止所作的答复。
1)如果可能的话,使用基于集合的逻辑进行查询,即尝试只使用SELECTINSERTUPDATEDELETE以及适当的FROM子句或嵌套查询-这些几乎总是更快。
2)如果上述方法不可行,则在SQL Server 2005中,+FAST FORWARD游标是高效且性能良好的,应优先于while循环使用。

ao218c7q

ao218c7q3#

大多数情况下可以避免使用游标,但有时也是必要的。
只要记住FAST_FORWARD是动态的... FORWARD_ONLY可以与静态游标一起使用。
试着把它用在万圣节的问题上,看看会发生什么!

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
z4bn682m

z4bn682m4#

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

WHILE循环临时表派生表关联子查询CASE语句多个查询通常,游标操作也可以通过非游标技术实现。
如果确定需要使用游标,则应尽可能减少要处理的记录数,方法之一是先将要处理的记录放入临时表,而不是原始表,而是将使用临时表中记录的游标,使用此路径时,假设临时表中的记录数与原始表相比已经大大减少。2记录越少,游标完成的速度越快。
影响性能的一些游标属性包括:
仅向前:使用FETCH NEXT仅支持将游标从第一行前移到末尾。除非设置为KEYSET或STATIC,否则每次调用提取时都会重新计算SELECT子句。
STATIC:创建已创建数据的临时副本,并由游标使用。这可以防止每次调用游标时都重新计算游标,从而提高性能。这不允许修改游标类型,并且调用提取时不会反映对表所做的更改。
KEYSET:游标行放置在tempdb下的表中,调用提取时会反映对非键列的更改。但是,不会反映添加到表中的新记录。使用键集游标时,不会再次计算SELECT语句。
动态:所有对表的更改都反映在游标中。每次调用提取时都会重新计算游标。这会占用大量资源,并对性能产生负面影响。
快进:游标是单向的,如FORWARD_ONLY,但将游标指定为只读。FORWARD_ONLY提高了性能,并且不会在每次提取时重新计算游标。如果它适合编程,则可提供最佳性能。
OPTIMISTIC:此选项可用于更新游标中的行。如果提取并更新了一行,而在提取和更新操作之间更新了另一行,则游标更新操作将失败。如果使用了可以执行行更新的OPTIMISTIC游标,则其他进程不应更新该游标。
注:如果未指定cursore,则缺省值为FORWARD_ONLY。

bwleehnv

bwleehnv5#

“如果您需要比FAST FORWARD更快的游标,请使用STATIC游标。它们比FAST FORWARD更快。虽然不是特别快,但可以发挥作用。”
别这么快!根据微软的说法:通常,当发生这些转换时,游标类型会降级为“开销更大”的游标类型。通常,(FAST)FORWARD-ONLY游标的性能最高,其次是DYNAMIC、KEYSET,最后是STATIC,后者的性能通常最低。
来自:Link

kzmpq1sx

kzmpq1sx6#

人们避免使用游标,因为游标通常比简单的while循环更难编写,然而,while循环可能会很昂贵,因为您需要不断地从表中选择数据,无论是临时的还是其他的。
使用只读快进游标时,数据保留在内存中,并且是专门为循环而设计的。
This article强调了游标的平均运行速度比while循环快50倍。

iezvtpos

iezvtpos7#

为了回答米尔最初的问题...
快进、只读、静态游标(亲切地称为"消防水管游标")通常与等效的临时表和While循环一样快或更快,因为这样的游标只不过是一个临时表和一个While循环,在后台进行了一些优化。
添加到埃里克Z比尔德张贴在这个线程,并进一步回答的问题...
"所有关于不使用游标的讨论实际上都是在基于集合的方法可用时避免使用游标,以及使用可更新游标等吗?"
是的。除了极少数例外,编写正确的基于集合的代码来完成与大多数游标相同的任务所花费的时间和代码更少,而且还有使用更少资源的额外好处,通常比游标或While循环运行得更快。一般来说,除了某些管理任务外,确实应该避免使用这些任务,而应该使用正确编写的基于集合的代码。当然,每个"规则"都有例外,但在Cursors、While循环和其他形式的RBAR的情况下,大多数人可以用一只手来计算例外,而不用用所有的手指。-)
还有一个"隐藏RBAR"的概念。这是看起来基于集合但实际上不是的代码。这种"基于集合"的代码是为什么某些人接受RBAR方法并说它们"可以"的原因。例如,使用聚合的在我的书中,使用包含不等式的(SUM)相关子查询来构建运行总和并不是真正基于集合的,而是RBAR的强化,因为对于计算的每一行,它必须以N *(N +1)/2的速率重复"接触"许多其他行。这就是所谓的"三角形连接",它至少比完全笛卡尔连接(交叉连接或"正方形连接")差一半。
尽管MS自SQLServer2005以来对游标的工作方式做了一些改进,与正确编写的基于集合的代码相比,术语"快速游标"仍然是一个矛盾修饰法。即使在Oracle中也是如此。过去我在Oracle工作了短短3年,但我的工作是改进现有代码的性能。大多数真正实质性的改进是在我将游标转换为集合时实现的。许多以前需要4到8个小时执行的作业减少到几分钟,有时甚至几秒。

0aydgbwb

0aydgbwb8#

在SQL Server中避免使用游标的“最佳实践”可以追溯到SQL Server 2000及更早版本。SQL 2005中对引擎的重写解决了与游标问题相关的大多数问题,特别是引入了快进选项。游标并不一定比基于集的游标差,并且在Oracle PL/SQL(LOOP)中得到了广泛而成功的使用。
您提到的“普遍接受”有效的,但现在已经过时且不正确-继续假设快进游标的行为和性能与宣传的一致。请根据SQL 2005及更高版本的结果进行一些测试和研究

u0sqgete

u0sqgete9#

如果你想要一个比快进更快的游标,那么使用静态游标。它们比快进更快。不是特别快,但是可以有所不同。

相关问题