Использование курсоров: обработка набора данных построчно
Что такое курсоры и зачем они нужны? 🔍
Представьте, что у вас есть таблица с тысячами строк, и вам нужно обработать каждую запись индивидуально — например, проверить условия, модифицировать данные или вызвать хранимую процедуру. Вот здесь на помощь приходят курсоры!
Курсор — это объект SQL, который позволяет последовательно перебирать строки в результате запроса и выполнять операции над каждой из них отдельно. Это как "указатель" на текущую строку в наборе данных.
Когда использовать:
- Когда нужно построчно обработать результаты запроса
- Для сложных бизнес-логик, которые сложно реализовать одним запросом
- При интеграции с прикладным кодом (например, Python или Java)
Анатомия курсора: 4 ключевых шага ⚙️
Работа с курсорами всегда следует определенному паттерну:
1. Объявление курсора
DECLARE cursor_name CURSOR FOR
SELECT column1, column2 FROM table_name WHERE condition;
2. Открытие курсора
OPEN cursor_name;
3. Чтение данных
FETCH NEXT FROM cursor_name INTO @variable1, @variable2;
4. Закрытие и освобождение
CLOSE cursor_name;
DEALLOCATE cursor_name;
Практический пример: обработка заказов 🛒
Давайте рассмотрим реальный сценарий — нам нужно обработать все необработанные заказы старше 7 дней:
-- Создаем временные переменные
DECLARE @OrderId INT;
DECLARE @OrderDate DATETIME;
DECLARE @CustomerId INT;
-- Объявляем курсор
DECLARE order_cursor CURSOR FOR
SELECT Id, OrderDate, CustomerId
FROM Orders
WHERE Status = 'Pending' AND OrderDate < DATEADD(day, -7, GETDATE());
-- Открываем курсор
OPEN order_cursor;
-- Начинаем обработку
FETCH NEXT FROM order_cursor INTO @OrderId, @OrderDate, @CustomerId;
WHILE @@FETCH_STATUS = 0
BEGIN
-- Логика обработки для каждого заказа
PRINT 'Обрабатывается заказ #' + CAST(@OrderId AS VARCHAR);
-- Например, можно обновить статус
UPDATE Orders SET Status = 'Expired' WHERE Id = @OrderId;
-- Или вызвать хранимую процедуру
EXEC sp_send_order_notification @CustomerId, @OrderId;
-- Переходим к следующей записи
FETCH NEXT FROM order_cursor INTO @OrderId, @OrderDate, @CustomerId;
END
-- Не забываем освободить ресурсы!
CLOSE order_cursor;
DEALLOCATE order_cursor;
Важные тонкости работы с курсорами 🧠
Производительность
Курсоры могут быть ресурсоемкими. Всегда предпочитайте операцию над множеством строк, если это возможно. Курсоры — это инструмент "последней мили".
Типы курсоров
- STATIC: Создает копию данных в tempdb (менее производительно, но стабильно)
- DYNAMIC: Отслеживает изменения в базовых таблицах
- FAST_FORWARD: Оптимизирован для чтения только вперед (самый быстрый вариант)
Пример объявления с параметрами:
DECLARE fast_cursor CURSOR FAST_FORWARD FOR
SELECT * FROM LargeTable;
Альтернативы курсорам: когда что использовать 🔄
| Ситуация | Лучший подход |
|---|---|
| Обработка каждой строки с бизнес-логикой | Курсор |
| Массовое обновление/удаление | Один UPDATE/DELETE |
| Сложные вычисления по группам | Оконные функции |
| Агрегация данных | GROUP BY |
Тест на понимание 💡
Попробуйте решить задачу: нужно пройтись по таблице Products и для товаров с ценой выше 1000 увеличить цену на 10%, а для остальных — на 5%. Напишите код с курсором, который это делает (ответ в следующем уроке!)
-- Ваше решение здесь
Заключительные мысли 🌟
Курсоры — мощный инструмент в руках SQL-разработчика, но как любой мощный инструмент, они требуют аккуратного обращения. Используйте их там, где действительно нужна построчная обработка, и всегда помните о производительности.