Десять лет спустя я полагаю, что должен добавить некоторую информацию к этому конкретному вопросу.
Есть два основных решения вашей проблемы. Сначала используйте объявление курсора LOCAL
:
DECLARE --Operation
Cursor1 -- Name
CURSOR -- Type
LOCAL READ_ONLY FORWARD_ONLY -- Modifiers
FOR -- Specify Iterations
SELECT Col1, Col2 FROM INSERTED;
Это ограничивает ваш конкретный курсор только вашим активным сеансом, а не глобальным контекстом сервера, при условии, что никакое другое действие не вызывает этот курсор. Аналогично в принципе использовать переменную Cursor, которая будет выглядеть следующим образом:
DECLARE @Cursor1 CURSOR;
SET @Cursor1 = CURSOR LOCAL READ_ONLY FORWARD_ONLY FOR SELECT Col1, Col2 FROM INSERTED;
Используя переменную курсора, вы всегда можете перезаписать ее в любое время, используя синтаксис SET
, в дополнение к управлению областью, чтобы она находилась в вашей конкретной сессии, как в предыдущем примере. Перезаписывая контекст курсора, вы фактически освобождаете любую прошлую ссылку, которая у него была. Тем не менее, оба эти подхода реализуют ваше первоначальное намерение, связывая состояние курсора с активностью текущего соединения.
Это может оставить длительную блокировку, если контекст вашего приложения использует пул соединений, и в этом случае вы должны использовать шаблон Try-Catch
следующим образом:
CREATE TRIGGER trigger1
ON [dbo].[table1]
AFTER UPDATE
AS
BEGIN
--declare some vars
DECLARE @Col1 SMALLINT;
DECLARE @Col2 TINYINT;
--declare cursor
DECLARE
Cursor1
CURSOR
LOCAL READ_ONLY FORWARD_ONLY
FOR
SELECT
Col1,
Col2
FROM
INSERTED;
--do the job
OPEN Cursor1;
BEGIN TRY
FETCH
NEXT
FROM
Cursor1
INTO
@Col1,
@Col2;
WHILE @@FETCH_STATUS = 0
BEGIN
IF -- my condition
EXEC myProc1 @param1 = @Col1, @Param2 = @Col2;
ELSE IF -- additional condition
EXEC myProc2 @param1 = @Col1, @Param2 = @Col2;
FETCH
NEXT
FROM
Cursor1
INTO
@Col1,
@Col2;
END;
END TRY
BEGIN CATCH
-- Error Handling
END CATCH
--clean it up
CLOSE Cursor1;
DEALLOCATE Cursor1;
END;
Использование шаблона таким образом уменьшает дублирование кода или необходимость проверки состояния курсора. По сути, инициализация курсора должна быть безопасной, как и оператор open. Как только курсор открыт, вы захотите всегда закрывать-освобождать его из сеанса, и это всегда должно быть безопасным действием при условии, что курсор был открыт (который мы только что установили, всегда должен быть безопасной операцией). Таким образом, выход за пределы Try-Catch
означает, что все может быть аккуратно закрыто в конце, после блока Catch
.
Стоит отметить, что я указал атрибут READ_ONLY
для курсора, а также FORWARD_ONLY
, поскольку ваш пример кода не прокручивался назад и вперед между записями в наборе. Если вы изменяете базовые строки в этих процедурах, вам, вероятно, лучше использовать курсор STATIC
, чтобы случайно не вызвать бесконечный цикл. Это не должно быть проблемой, поскольку вы используете таблицу INSERTED
для управления контекстом курсора, но все же стоит упомянуть о других возможных случаях использования.
Если вы хотите больше узнать о курсорах в SQL Server, я настоятельно рекомендую прочитать эту запись в блоге на эту тему, поскольку он очень подробно объясняет, каковы различные модификаторы курсора, и как эффекты, которые они имеют в Database Engine.