В SQL мы обычно оперируем выборками, но иногда надо выполнить некие действия для каждой строки в выборке. В обычных языках такая задача решается обходом последовательности в цикле, но в Transact SQL клссические циклы плохо работают, вместо них лучше использовать специальный обьект - курсор. Его синтаксис весьма непривычен для новичков, так что в данной статье я приведу простой и работающий пример, который можно взять за основу. Общая схема действий такова:
- Создаем курсор, описывая выборку, со строками которой мы будем работать. Запрос может быть сколь угодно сложным, с кучей JOIN
- Открываем курсор
- Объявляем переменные, в которые будем выбирать поля каждой строки, количество переменных должно совпадать с количеством столбцов в запросе
- Делаем выборку первой строки
- Прокручиваем в цикле наш код, завершая его выборкой следующей строки
- Закрываем курсор
-- обьявляем курсор declare some_cursor cursor --- sql запрос любой сложности, формирующий набор данных для курсора for select SOME_INT_FIELD, SOME_VARCHAR_FIELD from SOME_TABLE -- открываем курсор open some_cursor -- курсор создан, обьявляем переменные и обходим набор строк в цикле declare @counter int declare @int_var int, @string_var varchar(100) set @counter = 0 -- выборка первой строки fetch next from some_cursor INTO @int_var, @string_var -- цикл с логикой и выборкой всех последующих строк после первой while @@FETCH_STATUS = 0 begin --- логика внутри цикла set @counter = @counter + 1 if @counter >= 5 break -- возможный код для проверки работы, прерываем после пятой итерации -- отладочный select, на большом количестве строк выборка данных в sql server management studio может привести к ошибке переполнения памяти SELECT @int_var, @string_var INSERT INTO OTHER_TABLE (SOME_FIELD1, SOME_FIELD2) VALUES (@string_var, 'Мегастрока') DELETE FROM OTHER_TABLE2 WHERE ID_FIELD = @int_var exec some_stored_procedure -- выборка следующей строки fetch next from some_cursor INTO @int_var, @string_var -- завершение логики внутри цикла end select @counter as final_count -- закрываем курсор close some_cursor deallocate some_cursor
Курсоры (Transact-SQL)
FETCH (Transact-SQL)
@@FETCH_STATUS (Transact-SQL)
Спасибо. Очень понятно и доходчиво.