Achtergrond van de vraag:
Standaard SQL is oorspronkelijk ontworpen als een declaratieve taal zonder elementen van klassieke programmering (lussen, vertakkingen, directe sprongen), maar met uitbreidingen zoals T-SQL/PLSQL zijn constructies zoals WHILE, CASE, en zelfs een imitatie van GOTO beschikbaar gekomen.
Probleem:
Iteratieve bewerkingen (lussen door individuele records) leiden vaak tot traagheid, vooral bij het verwerken van grote hoeveelheden data, als je ze niet vervangt door een "set-based" benadering. Vertakkingen, CASE, IF — zijn erg handig, maar bij overmatige nesting lijdt de leesbaarheid en voorspelbaarheid van de code.
Oplossing:
Het gebruik van controleconstructies moet alleen gerechtvaardigd zijn wanneer batchverwerking (bulk/set) niet mogelijk is! Voor complexe berekeningen — een kleine lus, trigger of CASE is toegestaan. Voor massabewerking — is het beter om vensterfuncties of UPDATE met een subquery te gebruiken.
Voorbeeldcode (T-SQL):
DECLARE @i INT = 1 WHILE (@i <= 5) BEGIN IF @i % 2 = 0 PRINT CONCAT('Even: ', @i) ELSE PRINT CONCAT('Oneven: ', @i) SET @i = @i + 1 END
CASE-expressie:
SELECT num, CASE WHEN num % 2 = 0 THEN 'even' ELSE 'oneven' END AS parity FROM numbers
Belangrijke kenmerken:
Kan CASE worden gebruikt voor het filteren van rijen, net als WHERE?
Nee! CASE geeft verschillende waarden terug, maar filtert geen rijen. Een veelgemaakte fout is om via CASE te "filteren" in plaats van WHERE, het resultaat zal onjuist zijn.
Wat is het verschil tussen WHILE en CURSOR — is het niet hetzelfde?
WHILE is een basale lus, met handmatige controle van de variabele; CURSOR werkt op tabelrecords, houdt een verwijzing naar de rij vast. CURSOR is meer resource-intensief, werkt vaak veel langzamer voor grote datasets.
Welke benadering is sneller voor massaverwerking: WHILE-lus met UPDATE of één set-based UPDATE?
In 99% van de gevallen is één set-based UPDATE (of INSERT) veel sneller dan een lus over één record (ook al lijkt het flexibeler).
-- Onjuiste benadering DECLARE @id INT = 1 WHILE (@id <= 100000) BEGIN UPDATE t SET flag=1 WHERE id=@id SET @id = @id + 1 END -- Correct UPDATE t SET flag=1 WHERE id BETWEEN 1 AND 100000
In een project voor het bijwerken van de status van een miljoen bestellingen werd een lus met UPDATE voor elke id geschreven. De verwerking duurde ~8 uur. Bij een crash halverwege — verloren we alles, en moesten we handmatig herstellen.
Voordelen:
Nadelen:
Omgezet naar set-based UPDATE met één expressie. De uitvoeringstijd werd teruggebracht tot 6 minuten, de verwerking is atomair.
Voordelen:
Nadelen: