Быстрые курсоры SQL Server

Общепризнано, что использование курсоров в хранимых процедурах следует избегать, где это возможно (заменяется логикой на основе набора и т.д.). Если вы принимаете те случаи, когда вам нужно перебирать некоторые данные и можете делать это только в режиме чтения, ускоренная перемотка вперед (только чтение вперед) курсор более или менее неэффективна, чем во время циклов? Из моих исследований кажется, что опция курсора, как правило, быстрее и использует меньше чтений и время процессора. Я не проводил тщательного тестирования, но это то, что другие находят? У курсоров такого типа (ускоренная перемотка вперед) несут дополнительные накладные расходы или ресурсы, которые могут быть дорогими, о которых я не знаю.

Все разговоры о том, что не используют курсоры, действительно об избегании использования курсоров, когда доступны основанные на наборе подходы, и использование обновляемых курсоров и т.д.

Спасибо

Ответ 1

"Лучшая практика" для избежания курсоров в SQL Server датируется SQL Server 2000 и более ранними версиями. Пересмотр движка в SQL 2005 касался большинства проблем, связанных с проблемами курсоров, особенно с введением опции быстрой перемотки вперед. Курсоры не обязательно хуже, чем на основе набора, и широко используются и используются в Oracle PL/SQL (LOOP).

"Общепринятый", который вы называете , был действительным, но в настоящее время устарел и некорректен - полагайте, что быстрые курсоры работают так, как рекламируются и выполняются. Проведите несколько тестов и исследований, основывая свои выводы на SQL2005 и более поздних версиях

Ответ 2

В то время как быстрый курсор вперед имеет некоторые оптимизации в Sql Server 2005, неверно, что они находятся где-то близко к набору основанных запросов с точки зрения производительности. Очень мало ситуаций, когда логика курсора не может быть заменена на основе набора запросов. Курсоры всегда будут по своей сути медленнее, отчасти из-за того, что вам нужно продолжать прерывать выполнение, чтобы заполнить ваши локальные переменные.

Вот несколько ссылок, которые будут только верхушкой айсберга, если вы исследуете эту проблему:

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

"Если вам нужен еще более быстрый курсор, чем FAST FORWARD, тогда используйте курсор STATIC. Они быстрее, чем FAST FORWARD. Не очень быстро, но могут иметь значение.

Не так быстро! По словам Microsoft: "Обычно, когда эти преобразования происходили, тип курсора ухудшался до" более дорогого типа курсора ". Обычно наиболее удобным является курсор (FAST) FORWARD-ONLY, за которым следуют DYNAMIC, KEYSET и, наконец, STATIC, который, как правило, является наименее результативным."

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

Ответ 4

Вы можете часто избегать курсоров, но иногда это необходимо.

Просто имейте в виду, что 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

Ответ 5

Люди избегают курсора, потому что их обычно сложнее писать, чем простые циклы while, однако цикл while может быть дорогим, потому что вы постоянно выбираете данные из таблицы, временные или другие.

С помощью курсора, который с готовностью переводится в прямом направлении, данные хранятся в памяти и специально предназначены для циклирования.

В этой статье подчеркивается, что средний курсор работает в 50 раз быстрее, чем цикл while.

Ответ 6

Этот ответ надеется объединить ответы, полученные на сегодняшний день.

1) Если это вообще возможно, используйте логику, основанную на множестве, для ваших запросов, т.е. пытайтесь использовать только SELECT, INSERT, UPDATE или DELETE с помощью соответствующих предложений FROM или вложенных запросов - это будет почти всегда быстрее.

2) Если вышеизложенное невозможно, то в курсах SQL Server 2005+ FAST FORWARD эффективны и хорошо работают и должны использоваться в предпочтении для циклов while.

Ответ 7

Чтобы ответить на оригинальные вопросы Майл...

Ускоренная перемотка вперед, только чтение, статические курсоры (ласково называемые "курсором пожарного шланга" ), как правило, такие же быстрые или быстрые, как эквивалентная таблица Temp и цикл While, поскольку такой указатель представляет собой не что иное, как таблицу Temp и Хотя цикл, который был оптимизирован немного за кулисами.

Чтобы добавить к тому, что Эрик З. Борода опубликовал в этой теме и далее ответить на вопрос...

"Все разговоры о том, что не используют курсоры, действительно об избегании использования курсоров, когда доступны основанные на наборе подходы, и использование обновляемые курсоры и т.д."

Да. За очень немногими исключениями требуется меньше времени и меньше кода для написания правильного кода на основе набора, чтобы делать то же самое, что и большинство курсоров, и имеет дополнительное преимущество при использовании гораздо меньшего количества ресурсов и обычно выполняется намного быстрее, чем курсор или цикл While. Вообще говоря, и за исключением некоторых административных задач, их действительно следует избегать в пользу правильно написанного кода на основе набора. Конечно, есть исключения для каждого "правила", но, в случае курсоров, петель и других форм RBAR, большинство людей могут считать исключения одной рукой, не используя все пальцы.; -)

Там также понятие "Скрытый RBAR". Это код, который выглядит на основе набора, но на самом деле его нет. Этот тип кода на основе набора является причиной того, что некоторые люди используют методы RBAR и говорят, что они "ОК". Например, решение текущей общей проблемы с использованием агрегированного (SUM) коррелированного подзапроса с неравенством в нем для построения текущей суммы на самом деле не основано на моей книге. Вместо этого он RBAR на стероидах, потому что для каждой вычисленной строки он должен многократно "касаться" многих других строк со скоростью N * (N + 1)/2. Это известно как "Треугольная регистрация" и по крайней мере вдвое хуже, чем полный Cartesian Join (Cross Join или "Square Join" ).

Хотя MS сделала некоторые улучшения в работе курсоров с SQL Server 2005, термин "Fast Cursor" по-прежнему является оксюмороном по сравнению с правильно написанным кодом на основе набора. Это справедливо и в Oracle. Я работал с Oracle в течение 3 лет в прошлом, но моя работа заключалась в том, чтобы улучшить производительность существующего кода. Большинство действительно существенных улучшений было реализовано, когда я преобразовал курсоры в код на основе набора. Многие задания, которые раньше выполнялись от 4 до 8 часов, сокращались до минут, а иногда и секунд.

Ответ 8

Если вам нужен еще более быстрый курсор, чем FAST FORWARD, используйте курсор STATIC. Они быстрее, чем FAST FORWARD. Не очень быстро, но может иметь значение.