programowanieProgramista SQL

Opisz cechy, zalety i pułapki przetwarzania danych w trybie seryjnym przy użyciu kursorów (CURSOR) w SQL. Kiedy warto stosować kursory, a kiedy należy ich unikać na rzecz operacji opartych na zbiorach (set-based)? Podaj przykład użycia kursora.

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

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:

  • Pozwalają realizować złożone procedury, w których każdy wiersz wymaga osobnego przetwarzania lub sprawdzenia warunków zewnętrznych.
  • Wygodne do integracji z przestarzałymi systemami lub migracjami, gdzie potrzebna jest kontrola krok po kroku.

Wady i pułapki:

  • Kursory są zazwyczaj BARDZO wolne: przetwarzanie odbywa się po jednym rekordzie, a nie w trybie zbiorczym;
  • Zajmują dużo zasobów, mogą blokować tabele;
  • Nie skalują się przy dużej ilości danych;
  • Lepiej przepisać na zapytania set-based SQL lub użyć przetwarzania wsadowego.

Pytanie podchwytliwe.

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.