MySql - @Var Not Null失败

时间:2018-01-04 00:32:07

标签: mysql stored-procedures

如果我按原样运行此存储过程,它会给出错误的返回值,当我检查我的日志时,它从不记录其他条件:

CREATE DEFINER=`root`@`localhost` PROCEDURE `rpt_PlayerOnlineTime`(IN ucid VARCHAR(128), INOUT totaltime INT)
BEGIN

DECLARE v_finished INTEGER DEFAULT 0;

DECLARE v_event varchar(20) DEFAULT "";
DECLARE v_time BIGINT DEFAULT 0;
DECLARE v_server_id INT DEFAULT 0;
DECLARE v_session_id INT DEFAULT 0;

DECLARE v_start BIGINT DEFAULT 0;
DECLARE v_end BIGINT;


DEClARE con_cursor CURSOR FOR 
SELECT type, real_time, server_id, session_id FROM ki.raw_connection_log WHERE player_ucid = ucid;

-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER 
    FOR NOT FOUND SET v_finished = 1;

CALL ki.log("rpt_PlayerOnlineTime", CONCAT("totaltime: ", totaltime));

OPEN con_cursor;

online_time: LOOP
    CALL ki.log("rpt_PlayerOnlineTime", "In Loop");

    FETCH con_cursor INTO v_event, v_time, v_server_id, v_session_id;

    IF v_finished = 1 THEN 
        CALL ki.log("rpt_PlayerOnlineTime", "Finished Loop");
        LEAVE online_time;
    END IF;

    IF v_event = "CONNECTED" THEN


        IF v_start != 0 then
            CALL ki.log("rpt_PlayerOnlineTime", "CONNECTED EVENT TWICE IN A ROW");
            CALL ki.log("rpt_PlayerOnlineTime", CONCAT("v_server_id - ", v_server_id));
            CALL ki.log("rpt_PlayerOnlineTime", CONCAT("v_session_id - ", v_session_id));
            -- If the next event is not a disconnect event, assume that the player or server crashed, and get the time from the session
            SELECT session.real_time_end INTO v_end FROM ki.session WHERE server_id = v_server_id AND session_id = v_session_id;
            IF v_end IS NOT NULL THEN
                CALL ki.log("rpt_PlayerOnlineTime", "SETTING v_end TO SESSION END TIME");
            ELSE
                SELECT session.last_heartbeat INTO v_end FROM ki.session WHERE session.server_id = v_server_id AND session.session_id = v_session_id;
                CALL ki.log("rpt_PlayerOnlineTime", "SETTING v_end To Last HeartBeat Received from session");
                CALL ki.log("rpt_PlayerOnlineTime", CONCAT("totaltime : ", totaltime));
                CALL ki.log("rpt_PlayerOnlineTime", CONCAT("v_start : ", v_start));

                IF v_end IS NULL THEN
                    CALL ki.log("rpt_PlayerOnlineTime", "Last Heartbeat is NULL - defaulting to start");
                    SET v_end = v_start; -- if the last_heartbeat is null, then assume there was no end date
                END IF;
                CALL ki.log("rpt_PlayerOnlineTime", CONCAT("v_end : ", v_end));
            END IF;

            SET totaltime = totaltime + (v_end - v_start);
        END IF;
        SET v_start = v_time;
    ELSEIF v_event = "DISCONNECTED" THEN
        CALL ki.log("rpt_PlayerOnlineTime", "DISCONNECTED EVENT - CALCULATING END TIME");
        SET totaltime = totaltime + (v_time - v_start);
        SET v_start = 0;
    END IF;

    CALL ki.log("rpt_PlayerOnlineTime", CONCAT("IN LOOP: Total Time: ", totaltime));
END LOOP online_time;

CLOSE con_cursor;

CALL ki.log("rpt_PlayerOnlineTime", CONCAT("Total Time: ", totaltime));
END

问题出现在这段代码中 - v_end可能为NULL,但是使用当前的if语句它永远不会是真的(我从未看到记录的其他信息)

        -- If the next event is not a disconnect event, assume that the player or server crashed, and get the time from the session
        SELECT session.real_time_end INTO v_end FROM ki.session WHERE server_id = v_server_id AND session_id = v_session_id;
        IF v_end IS NOT NULL THEN
            CALL ki.log("rpt_PlayerOnlineTime", "SETTING v_end TO SESSION END TIME");
        ELSE
            SELECT session.last_heartbeat INTO v_end FROM ki.session WHERE session.server_id = v_server_id AND session.session_id = v_session_id;
            CALL ki.log("rpt_PlayerOnlineTime", "SETTING v_end To Last HeartBeat Received from session");
            CALL ki.log("rpt_PlayerOnlineTime", CONCAT("totaltime : ", totaltime));
            CALL ki.log("rpt_PlayerOnlineTime", CONCAT("v_start : ", v_start));

            IF v_end IS NULL THEN
                CALL ki.log("rpt_PlayerOnlineTime", "Last Heartbeat is NULL - defaulting to start");
                SET v_end = v_start; -- if the last_heartbeat is null, then assume there was no end date
            END IF;
            CALL ki.log("rpt_PlayerOnlineTime", CONCAT("v_end : ", v_end));
        END IF;

但是,如果我修改if语句使用!= NULL,它就可以工作。

        -- If the next event is not a disconnect event, assume that the player or server crashed, and get the time from the session
        SELECT session.real_time_end INTO v_end FROM ki.session WHERE server_id = v_server_id AND session_id = v_session_id;
        IF v_end != NULL THEN
            CALL ki.log("rpt_PlayerOnlineTime", "SETTING v_end TO SESSION END TIME");
        ELSE
            SELECT session.last_heartbeat INTO v_end FROM ki.session WHERE session.server_id = v_server_id AND session.session_id = v_session_id;
            CALL ki.log("rpt_PlayerOnlineTime", "SETTING v_end To Last HeartBeat Received from session");
            CALL ki.log("rpt_PlayerOnlineTime", CONCAT("totaltime : ", totaltime));
            CALL ki.log("rpt_PlayerOnlineTime", CONCAT("v_start : ", v_start));

            IF v_end IS NULL THEN
                CALL ki.log("rpt_PlayerOnlineTime", "Last Heartbeat is NULL - defaulting to start");
                SET v_end = v_start; -- if the last_heartbeat is null, then assume there was no end date
            END IF;
            CALL ki.log("rpt_PlayerOnlineTime", CONCAT("v_end : ", v_end));
        END IF;

为什么MySQL以这种方式工作?我正在查看有关检查变量是否为null的正确方法的其他stackoverflow帖子,他们建议使用IS NOT NULL而不是!= NULL。

How to check if a variable is NULL, then set it with a MySQL stored procedure?

我正在使用MySql 5.7.20 - 这是MySql中的一个错误吗?

2 个答案:

答案 0 :(得分:1)

expr != NULL永远不会成真。它总是评估为NULL

我建议它实际上没有工作,你只相信它,因为你有另一个错误,在这里:

SELECT session.real_time_end INTO v_end FROM ki.session WHERE server_id = v_server_id AND session_id = v_session_id;

不要在没有理解的情况下使用它完全它的功能......一旦你理解了,你可能不会想要使用它。

,而不是...

SET v_end = (SELECT session.real_time_end FROM ki.session WHERE server_id = v_server_id AND session_id = v_session_id);

问题是,当没有行匹配查询时,第一个表单将不会执行您期望的操作,因为没有返回结果,因此实际上从未实现INTO。它没有像您期望的那样将变量的值设置为NULL

循环的前一次迭代的值仍然在变量中。

第二种形式是显式标量,总是只返回一个值,当没有匹配行时,该值设置为NULL

请参阅https://dba.stackexchange.com/a/35207/11651的扩展示例。它使用用户定义的变量而不是程序变量,但它说明的行为对于两者都是相同的。

或者,在v_end之前,在循环内明确地将NULL设置为SELECT ... INTO

SELECT ... INTO的所有实例重复。

答案 1 :(得分:0)

您将v_end声明为BIGINT

如果session.real_time_endNULL0

您可以使用:

IF v_end != NULL or v_end = 0 THEN

我不确定你的session.real_time_end,但你可以尝试一下。

正如我所观察到的,你可以使用像LENGTH FUNCTION:

IF LENGTH(v_end)>0

它将接受NULL == 0否则返回Length本身。