Cursors in SQL ermöglichen es, Datensätze zeilenweise zu durchlaufen und bestimmte Aktionen für jeden Eintrag auszuführen, was einer Iteration in der Programmierung ähnelt. Dies ist nützlich für komplexe schrittweise Logik, die mit nur einer SQL-Abfrage nicht möglich ist (z. B. schrittweise Berechnungen mit Änderungen des äußeren Zustands).
Beispiel für einen Cursor:
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;
Vorteile:
Nachteile und Fallstricke:
Kann man einen Cursor innerhalb eines Triggers verwenden? Welche Auswirkungen kann das auf die Leistung der Datenbank haben?
Antwort und Beispiel: Die Verwendung eines Cursors innerhalb eines Triggers ist möglich, wird jedoch dringend davon abgeraten – jeder DML-Befehl kann einen Cursor für jede betroffene Zeile starten, was zu einer exponentiellen Zunahme von Abfragen und Sperren führt.
CREATE TRIGGER UpdateBalance ON Accounts AFTER INSERT AS DECLARE c CURSOR FOR SELECT id FROM inserted; -- schlecht! OPEN c; -- ...
Geschichte
Projekt: Zusammenfassung von Bestellungen im Einzelhandel. Die Aufgabe bestand darin, die Bestände im Lager zu zählen, wenn in einer Charge ein Defekt gefunden wurde – nachfolgende Chargen erforderten eine manuelle Neuberechnung über die Aktualisierung von Rabatten. Ein Cursor wurde zum Durchlaufen der Chargen verwendet. Später stellte sich heraus, dass bei mehrfachen Ausführungen der Thread stundenlang blockiert wurde, die Serverlast exponentiell anstieg und die Sperrkonkurrenz zu Ausfällen führte.
Geschichte
Projekt: ERP, Datenmigration. In das Verfahren zur Datenimportverarbeitung wurde ein Fehlerbehandlungsmechanismus mittels Cursor integriert. Dabei wurde nicht berücksichtigt, dass ein Cursor bei 5 Millionen Zeilen 40 Mal langsamer arbeitet als eine ähnliche Batch-Verarbeitung mittels set-basiertem UPDATE + CASE. Aufgrund der langsamen Migration verschob sich der Zeitrahmen.
Geschichte
Projekt: Abrechnung eines Finanzunternehmens. In den Trigger zum Update der Tabelle für Geldbewegungen wurde ein Cursor eingefügt, um den neuen aggregierten Restbetrag zu berechnen. In der Produktion führte dies zu "STOP THE WORLD" – das Einfügen einer einzigen Aufzeichnung verlangsamte den Betrieb des Dienstes aufgrund des Starts eines eingebetteten Cursors für viele Zeilen.