MySQL Case Select not behaving as expected

2019-03-04 06:05发布

问题:

While creating a more complex stored procedure in MySQL, I encountered a weird problem with my CASE statement. I have simplified my procedure to show the issue. I am selecting three things per loop to clarify the problem: the count variable, the modTemp variable before the CASE, and then a number representing which CASE path was taken.

DELIMITER //
CREATE PROCEDURE `AddPlants` (IN plantNum int)
BEGIN
    DECLARE count int;
    DECLARE modTemp int;

    SET count = 1;
    WHILE count <= plantNum DO
        SELECT CONCAT('count = ', count);
        SET modTemp = count % 3;
        SELECT CONCAT('modTemp = ', modTemp);

        CASE modTemp
            WHEN modTemp = 1 THEN
                SELECT 1;
            WHEN modTemp = 2 THEN
                SELECT 2;
            WHEN modTemp = 0 THEN
                SELECT 3;
            ELSE
                SELECT CONCAT('Error: modTemp = ', modTemp);
        END CASE;

        SET count = count + 1;
    END WHILE;
END//

Now I use CALL AddPlants(3); Here are my results:


Expected output

count = 1, modTemp = 1, 1

count = 2, modTemp = 2, 2

count = 3, modTemp = 0, 3


Actual output

count = 1, modTemp = 1, 1

count = 2, modTemp = 2, Error: modTemp = 2

count = 3, modTemp = 0, 1


Each time, count and modTemp are the correct values. However, on the second loop, modTemp is 2 but does not enter CASE 2. And in the third loop, modTemp is 0 but goes into CASE 1. Any ideas what is happening here?

回答1:

You're mixing the two ways to use CASE. You either write:

CASE
    WHEN <expression1> THEN <result1>;
    WHEN <expression2> THEN <result2>;
    ...
END CASE

This evaluates each expression, and executes the corresponding result for the first true one. Or:

CASE <expression>
    WHEN <val1> THEN <result1>;
    WHEN <val2> THEN <result2>;
    ...
END CASE

This compares <expression> to each value, and executes the corresponding result for the first one that matches.

You used the second syntax, but your values also contain a comparison. So they're all either 0 (for false) or 1 (for true), and that's what you're comparing modTemp to. Change to:

    CASE modTemp
        WHEN 1 THEN
            SELECT 1;
        WHEN 2 THEN
            SELECT 2;
        WHEN 0 THEN
            SELECT 3;
        ELSE
            SELECT CONCAT('Error: modTemp = ', modTemp);
    END CASE;


回答2:

Shouldn't it be more like

CREATE PROCEDURE `AddPlants` (IN plantNum int)

BEGIN DECLARE count int; DECLARE modTemp int;

SET count = 1;
WHILE count <= plantNum DO
    SELECT CONCAT('count = ', count);
    SET modTemp = count % 3;
    SELECT CONCAT('modTemp = ', modTemp);

    CASE modTemp
        WHEN 1 THEN
            SELECT 1;
        WHEN 2 THEN
            SELECT 2;
        WHEN 0 THEN
            SELECT 3;
        ELSE
            SELECT CONCAT('Error: modTemp = ', modTemp);
    END CASE;

    SET count = count + 1;
END WHILE;

END//