SQL Server Fast Forward Cursors

匿名 (未验证) 提交于 2019-12-03 08:46:08

问题:

It is generally accepted that the use of cursors in stored procedures should be avoided where possible (replaced with set based logic etc). If you take the cases where you need to iterate over some data, and can do in a read only manner, are fast forward (read only forward) cursor more or less inefficient than say while loops? From my investigations it looks as though the cursor option is generally faster and uses less reads and cpu time. I haven't done any extensive testing, but is this what others find? Do cursors of this type (fast forward) carry additional overhead or resource that could be expensive that I don't know about.

Is all the talk about not using cursors really about avoiding the use of cursors when set-based approaches are available, and the use of updatable cursors etc.

Thanks

回答1:

The 'Best Practice' of avoiding cursors in SQL Server dates back to SQL Server 2000 and earlier versions. The rewrite of the engine in SQL 2005 addressed most of the issues related to the problems of cursors, particularly with the introduction of the fast forward option. Cursors are not neccessarily worse than set-based and are used extensively and successfully in Oracle PL/SQL (LOOP).

The 'generally accepted' that you refer to was valid, but is now outdated and incorrect - go on the assumption that fast forward cursors behave as advertised and perform. Do some tests and research, basing your findings on SQL2005 and later



回答2:

While a fast forward cursor does have some optimizations in Sql Server 2005, it is not true that they are anywhere close to a set based query in terms of performance. There are very few situations where cursor logic cannot be replaced by a set-based query. Cursors will always be inherently slower, due in part to the fact that you have to keep interrupting the execution in order to fill your local variables.

Here are few references, which would only be the tip of the iceberg if you research this issue:

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

http://sqlblog.com/blogs/adam_machanic/archive/2007/10/13/cursors-run-just-fine.aspx



回答3:

"If You want a even faster cursor than FAST FORWARD then use a STATIC cursor. They are faster than FAST FORWARD. Not extremely faster but can make a difference."

Not so fast! According to Microsoft: "Typically, when these conversions occurred, the cursor type degraded to a ‘more expensive’ cursor type. Generally, a (FAST) FORWARD-ONLY cursor is the most performant, followed by DYNAMIC, KEYSET, and finally STATIC which is generally the least performant."

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



回答4:

You can avoid cursors most of the time, but sometimes it's necessary.

Just keep in mind that FAST_FORWARD is DYNAMIC ... FORWARD_ONLY you can use with a STATIC cursor.

Try using it on the Halloween problem to see what happens !!!

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 


回答5:

People avoid cursor because they generally are more difficult to write than a simple while loops, however, a while loop can be expensive because your constantly selecting data from a table, temporary or otherwise.

With a cursor, which is readonly fast forward, the data is kept in memory and has been specifically designed for looping.

This article highlights that an average cursor runs 50 times faster than a while loop.



回答6:

This answer hopes to consolidate the replies given to date.

1) If at all possible, used set based logic for your queries i.e. try and use just SELECT, INSERT, UPDATE or DELETE with the appropriate FROM clauses or nested queries - these will almost always be faster.

2) If the above is not possible, then in SQL Server 2005+ FAST FORWARD cursors are efficient and perform well and should be used in preference to while loops.



回答7:

To answer Mile's original questions...

Fast Forward, Read Only, Static cursors (affectionately known as a "Fire Hose Cursor") are typically as fast or faster than a equivalent Temp Table and a While loop because such a cursor is nothing more than a Temp Table and a While loop that has been optimized a bit behind the scenes.

To add to what Eric Z. Beard posted on this thread and to further answer the question of...

"Is all the talk about not using cursors really about avoiding the use of cursors when set-based approaches are available, and the use of updatable cursors etc."

Yes. With very few exceptions, it takes less time and less code to write proper set-based code to do the same thing as most cursors and has the added benefit of using much fewer resources and usually runs MUCH faster than a cursor or While loop. Generally speaking and with the exception of certain administrative tasks, they really should be avoided in favor of properly written set-based code. There are, of course, exceptions to every "rule" but, in the case of Cursors, While loops, and other forms of RBAR, most people can count the exceptions on one hand without using all of the fingers. ;-)

There's also the notion of "Hidden RBAR". This is code that looks set-based but actually isn't. This type of "set-based" code is the reason why certain people have embraced RBAR methods and say they're "OK". For example, solving the running total problem using an aggregated (SUM) correlated sub-query with an inequality in it to build the running total isn't really set-based in my book. Instead, it's RBAR on steroids because ,for each row calculated, it has to repeatedly "touch" many other rows at a rate of N*(N+1)/2. That's known as a "Triangular Join" and is at least half as bad as a full Cartesian Join (Cross Join or "Square Join").

Although MS has made some improvements in how Cursors work since SQL Server 2005, the term "Fast Cursor" is still an oxymoron compared to properly written set-based code. That also holds true even in Oracle. I worked with Oracle for a short 3 years in the past but my job was to make performance improvements in existing code. Most of the really substantial improvements were realized when I converted Cursors to set-based code. Many jobs that previously took 4 to 8 hours to execute were reduced to minutes and, sometimes, seconds.



回答8:

If You want a even faster cursor than FAST FORWARD then use a STATIC cursor. They are faster than FAST FORWARD. Not extremely faster but can make a difference.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!