LOOPS

MySQL ondersteunt ook loops in opgeslagen routines, waardoor routines een reeks acties te herhalen totdat er aan een vooraf bepaalde voorwaarde wordt voldaan. Drie verschillende lusconstructies worden momenteel ondersteund: de LOOP construct, de REPEAT construct en de WHILE constructie. In de volgende paragrafen bespreken elk van deze onderwerpen in meer detail.

De LOOP Constructie

De LOOP constructie is de meest eenvoudigste soort lus in MySQL en waar er voor  MSSQL geen rechtstreeks equivalent voor bestaat, wordt er met deze constructie een reeks opdrachten herhaaldelijk uitgevoerd. Het ziet er als volgt uit:

loop-name: LOOP
statement 1;
statement 2;
...
statement n;
END LOOP loop-name;

De opdrachten die omsloten zijn door het LOOP … END LOOP blok worden herhaaldelijk uitgevoerd totdat het wordt onderbroken door een LEAVE opdracht. In combinatie met het IF constructie is het mogelijk om lussen uit te voeren totdat aan een bepaalde voorwaarde wordt voldaan. Beschouw het volgende voorbeeld, dat door het bouwen van een faculteit calculator dit illustreert. In deze functie wordt het door de gebruiker ingevoerde getal met elke iteratie met één verminderd en vermenigvuldigd met het eerder berekende product. Dit gaat door totdat het door de gebruiker ingevoerde waarde 0 heeft bereikt. De LEAVE instructie wordt gebruikt om de lus te verlaten. Het eindresultaat is de faculteit van het ingevoerde nummer.

Let op het gebruik van het attribuut UNSIGNED gegevenstype, welke enkel positieve getallen worden geaccepteerd als invoer in de functie. MySQL zal een foutmelding genereren als de functie ontvangt een negatieve waarde als input, zoals weergegeven.

MySQL

DELIMITER //
CREATE FUNCTION factorial(num INT UNSIGNED)
RETURNS INT
BEGIN
    DECLARE result INT DEFAULT 1;
    IF num = 0
        THEN RETURN 0;
    END IF;
    fact: LOOP
        IF num > 0
            THEN SET result = result * num;
                SET num = num - 1;
            ELSE LEAVE fact;
        END IF;
    END LOOP fact;
RETURN result;
END //

(0 row(s) affected)
SELECT factorial(10);

(1 row(s) returned)

HTML clipboard

factorial(10)
13628800
SELECT factorial(-1);

Error Code: 1264. Out of range value for column 'num' at row 2

De WHILE constructie

Een WHILE-lus herhaalt voortdurend, terwijl een vooraf bepaalde voorwaarde waar is. De typische structuur van deze lus ziet er uit als volgt:

loop-name: WHILE condition DO
statement 1;
statement 2;
...
statement n;
END WHILE loop-name;

Het is mogelijk om het vorige voorbeeld te herzien in termen van een WHILE-lus.

Let op de voorwaarde die na het WHILE sleutelwoord; Zolang deze voorwaarde ‘waar’ is, wordt de code binnen de lus-blok uitgevoerd. Zodra de voorwaarde ‘onwaar’ is, stopt de lus met herhalen, en keert de besturing terug naar de regels na de lus.

MSSQL

CREATE FUNCTION factorial(@num INT)
RETURNS INT
BEGIN
    DECLARE @result INT = 1;
    IF @num = 0
    BEGIN
        RETURN 0;
    END
    WHILE @num > 0 
        BEGIN
            SET @result = @result * @num;
            SET @num = @num - 1;
        END
RETURN @result;
END;

Command(s) completed successfully.
SELECT [dbo].[factorial](10);

(1 row(s) affected)
(No column name)
13628800

MySQL

DELIMITER //
CREATE FUNCTION factorial(num INT UNSIGNED)
RETURNS INT
BEGIN
    DECLARE result INT DEFAULT 1;
    IF num = 0
        THEN RETURN 0;
    END IF;
    fact: WHILE num > 0 DO
        SET result = result * num;
        SET num = num - 1;
    END WHILE fact;
RETURN result;
END //

(0 row(s) affected)
SELECT factorial(10);

(1 row(s) returned)
factorial(10)
13628800

De REPEAT constructie

Een REPEAT-lus is iets anders dan een WHILE-lusoop: het herhaalt zichzelf voortdurend tot een vooraf opgegeven voorwaarde ‘waar’ is. Hier kan ziet men hoe de structuur eruit ziet:

loop-name: REPEAT
statement 1;
statement 2;
...
statement n;
UNTIL condition
END REPEAT loop-name;

Het verschil in structuur tussen de WHILE- en REPEAT-constructies moet duidelijk zijn: in een REPEAT-lus, wordt de te evalueren conditie onderaan het LOOP-blok geplaatst. Hier is de faculteitsrekenmachine opnieuw, deze keer geschreven als een herhaling lus. In MSSQL bestaat geen REPEAT-lus en kan men dit oplossen door een conditievoorwaarde met een GOTO-sprong.

MSSQL

CREATE FUNCTION factorial(@num INT)
RETURNS INT
BEGIN
    DECLARE @result INT = 1;
    IF @num = 0
    BEGIN
        RETURN 0;
    END
    fact:
        SET @result = @result * @num;
        SET @num = @num - 1;
    IF NOT(@num <= 0) GOTO fact; 
        -- Workaround voor de UNTIL @num <= 0 
    RETURN @result;
END;

Command(s) completed successfully.
SELECT factorial(10);

(1 row(s) returned)

MySQL

DELIMITER //
CREATE FUNCTION factorial(num INT UNSIGNED)
RETURNS INT
BEGIN
    DECLARE result INT DEFAULT 1;
    IF num = 0
        THEN RETURN 0;
    END IF;
    fact: REPEAT
        SET result = result * num;
        SET num = num - 1;
    UNTIL num <= 0
    END REPEAT fact;
    RETURN result;
END //

(0 row(s) affected)
SELECT factorial(10);

(1 row(s) returned)

Er is een subtiel verschil tussen een WHILE-lus en een DO-WHILE-lus dat een belangrijke implicatie heeft. Met een WHILE-lus, indien de voorwaarde tijdens de eerste lus evalueert naar de waarde ‘onwaar’, zal de lus nooit worden uitgevoerd. Met een REPEAT-lus, anderzijds, zal de lus altijd eenmaal uitgevoerd worden, zelfs als de voorwaarde ‘onwaar’ is, omdat de voorwaarde pas op het einde van de lus wordt vastgesteld in plaats van aan het begin.

De LEAVE and ITERATE-opdrachten

MySQL biedt twee extra opdrachten om te helpen bij de besturing van lussen: de LEAVE-opdracht, welke zorgt dat men de lus verlaat, en de ITERATE-opdracht, die de lus dwingt om opnieuw uit te voeren. Hier is een triviaal voorbeeld dat de LEAVE-opdracht illustreert. In het volgende voorbeeld geval breekt de LEAVE-instructie op de derde iteratie de lus af en verlaat het de procedure, zoals geïllustreerd in de output:

MSSQL

CREATE PROCEDURE f
AS
DECLARE @i INT = 1;
WHILE @i <= 5 
    BEGIN
        IF @i = 3
        BEGIN
            RETURN;
        END
        SELECT @i;
        SET @i = @i + 1;
    END;

Command(s) completed successfully.
EXEC [dbo].[f];

(1 row(s) affected)
(No column name)
12

MySQL

DELIMITER //
CREATE PROCEDURE f()
BEGIN
    DECLARE i INT DEFAULT 1;
    f: WHILE i <= 5 DO
        IF i = 3
            THEN LEAVE f;
        END IF;
    SELECT i;
    SET i = i + 1;
    END WHILE f;
END //

(0 row(s) affected)
CALL f;

(1 row(s) returned)
i
12

Hier is een voorbeeld dat de ITERATE-opdracht aantoont. In dit voorbeeld, wanneer de lus-teller 3 bereikt, zal de ITERATE-instructie een extra iteratie van de lus uitvoert. MSSQL kent geen ITERATE-instructie en zal men dit kunnen oplossen door gebruik te maken van een GOTO-instructie.

MSSQL

CREATE PROCEDURE g
AS
DECLARE @i INT = 1, @j INT = 0;
iterate_f:
WHILE @i <= 5 AND @j < 2 
BEGIN
    SELECT @i;
    IF @i = 3
    BEGIN 
        SET @j = @j + 1;
        GOTO iterate_f;
    END
    SET @i = @i + 1;
END;

Command(s) completed successfully.
EXEC [dbo].[g];

(1 row(s) affected)
(No column name)
13

MySQL

DELIMITER //
CREATE PROCEDURE g()
BEGIN
    DECLARE i INT DEFAULT 1;
    DECLARE j INT DEFAULT 0;
    f: WHILE i <= 5 AND j < 2 DO
        SELECT i;
        IF i = 3
            THEN SET j = j + 1;
            ITERATE f;
        END IF;
        SET i = i + 1;
    END WHILE f;
END //

(0 row(s) affected)
CALL g;

(1 row(s) returned)
i
13