Kursory w SQL pozwalają na iteracyjne przetwarzanie zbioru danych, wykonując określone operacje na każdym wierszu, co przypomina iterację w programowaniu. Jest to wygodne dla skomplikowanej logiki krok po kroku, niemożliwej do zrealizowania za pomocą jednego zapytania SQL (na przykład obliczenia krokowe z modyfikacją zewnętrznego stanu).
Przykład kursora:
DECLARE my_cursor CURSOR FOR SELECT id, balance FROM Accounts WHERE isActive = 1; OPEN my_cursor; DECLARE @id INT, @bal DECIMAL(10,2); FETCH NEXT FROM my_cursor INTO @id, @bal; WHILE @@FETCH_STATUS = 0 BEGIN UPDATE Accounts SET balance = @bal * 1.05 WHERE id = @id; FETCH NEXT FROM my_cursor INTO @id, @bal; END CLOSE my_cursor; DEALLOCATE my_cursor;
Zalety:
Wady i pułapki:
Czy można używać kursora wewnątrz wyzwalacza? Jakie mogą być konsekwencje dla wydajności bazy danych?
Odpowiedź i przykład: Użycie kursora wewnątrz wyzwalacza jest możliwe, ale zdecydowanie odradzane — każdy operator DML może uruchamiać kursor dla każdego dotkniętego wiersza, co prowadzi do lawinowego wzrostu zapytań i blokad.
CREATE TRIGGER UpdateBalance ON Accounts AFTER INSERT AS DECLARE c CURSOR FOR SELECT id FROM inserted; -- źle! OPEN c; -- ...
Historia
Projekt: Podsumowanie zamówień w handlu detalicznym. Zadaniem było policzenie stanów magazynowych, jeśli w partii znaleziono defekt — kolejne partie wymagały ręcznego przeliczenia poprzez aktualizację rabatów. Użyto kursora do iteracji po partiach. Później okazało się, że przy wielokrotnych uruchomieniach wątek był blokowany na godziny, obciążenie serwera rosło wykładniczo, a kolizje blokad prowadziły do awarii.
Historia
Projekt: ERP, migracja danych. W procedurze przetwarzania importu dodano przetwarzanie błędów przez kursor. Nie wzięto pod uwagę, że przy 5 mln wierszy kursor działa 40 razy wolniej niż analogiczne przetwarzanie wsadowe za pomocą instrukcji set-based UPDATE + CASE. Z powodu wolnej migracji przesunięto terminy.
Historia
Projekt: Billing firmy finansowej. Do wyzwalacza aktualizacji tabeli ruchów środków dodano kursor, aby obliczyć nowy agregatowy stan. W produkcji prowadziło to do "STOP THE WORLD" — wstawienie nawet jednego rekordu spowolniło działanie usługi z powodu uruchomienia zagnieżdżonego kursora dla wielu wierszy.