Geschichte der Frage:
Standard-SQL wurde ursprünglich als deklarative Sprache ohne Elemente der klassischen Programmierung (Schleifen, Verzweigungen, direkter Übergang) konzipiert, jedoch wurden mit Erweiterungen wie T-SQL/PLSQL Konstrukte wie WHILE, CASE und sogar eine Nachbildung von GOTO verfügbar.
Problem:
Iterative Operationen (Schleifen zur Verarbeitung von Einzelaufzeichnungen) führen häufig zu Leistungseinbußen, insbesondere bei großen Datenmengen, wenn man sie nicht durch einen "set-basierten" Ansatz ersetzt. Verzweigungen, CASE, IF sind sehr praktisch, aber bei übermäßiger Verschachtelung leidet die Lesbarkeit und Vorhersagbarkeit des Codes.
Lösung:
Die Verwendung von Kontrollstrukturen sollte nur gerechtfertigt sein, wenn man nicht auf eine batch (bulk/set) Verarbeitung verzichten kann! Bei komplexen Berechnungen ist eine kleine Schleife, ein Trigger oder CASE akzeptabel. Für Massenverarbeitung ist es besser, Fensterfunktionen oder UPDATE mit einer Unterabfrage zu verwenden.
Beispielcode (T-SQL):
DECLARE @i INT = 1 WHILE (@i <= 5) BEGIN IF @i % 2 = 0 PRINT CONCAT('Gerade: ', @i) ELSE PRINT CONCAT('Ungerade: ', @i) SET @i = @i + 1 END
CASE-Ausdruck:
SELECT num, CASE WHEN num % 2 = 0 THEN 'gerade' ELSE 'ungerade' END AS Parität FROM numbers
Wesentliche Merkmale:
Kann CASE zur Filterung von Zeilen verwendet werden, wie WHERE?
Nein! CASE gibt unterschiedliche Werte zurück, filtert aber keine Zeilen. Ein häufiger Fehler ist, durch CASE statt WHERE zu "filtern", das Ergebnis wird falsch sein.
Was unterscheidet WHILE von CURSOR — ist das nicht das Gleiche?
WHILE ist eine grundlegende Schleife mit benutzerdefinierter Steuerung der Variablen; CURSOR arbeitet mit Tabellenzeilen und hält einen Verweis auf die Zeile. CURSOR ist ressourcenintensiver und oft bei großen Datenmengen erheblich langsamer.
Welcher Ansatz ist schneller für die Massenverarbeitung: WHILE mit UPDATE oder ein set-basiertes UPDATE?
In 99% der Fälle ist ein set-basiertes UPDATE (oder INSERT) viel schneller als eine Schleife über jede Einzelzeile (selbst wenn es scheint, dass es "flexibler" ist).
-- Falscher Ansatz DECLARE @id INT = 1 WHILE (@id <= 100000) BEGIN UPDATE t SET flag=1 WHERE id=@id SET @id = @id + 1 END -- Richtig UPDATE t SET flag=1 WHERE id BETWEEN 1 AND 100000
In einem Projekt wurde ein Schleifen-UPDATE für den Status von einer Million Bestellungen geschrieben. Die Verarbeitung dauerte ~8 Stunden. Bei einem Absturz zur Hälfte — verloren wir alles und mussten manuell reparieren.
Vorteile:
Nachteile:
Umgestellt auf ein set-basiertes UPDATE mit einem einzigen Ausdruck. Die Ausführungszeit wurde auf 6 Minuten reduziert, die Verarbeitung ist atomar.
Vorteile:
Nachteile: