【问题标题】:MySql - @Var Is Not Null failingMySql - @Var 不是 Null 失败
【发布时间】:2018-01-04 00:32:06
【问题描述】:

如果我按原样运行这个存储过程,它会给出不正确的返回值,并且当我检查我的日志时,它永远不会记录 else 条件:

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 语句它永远不会为真(我从未看到记录的 else 信息)

        -- 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 会这样工作?我正在查看有关检查变量是否为空的正确方法的其他 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 中的错误吗?

【问题讨论】:

  • 这是一个整数值session.real_time_end
  • 不,两者都是 bigint,它们来自第三方游戏。

标签: mysql stored-procedures


【解决方案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 的扩展示例。它使用用户定义的变量而不是程序变量,但它说明的行为对于两者是相同的。

或者,在 SELECT ... INTO 之前的循环内将 v_end 显式设置为 NULL

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

【讨论】:

  • 很好的解释,现在我知道要避免因为这些原因而选择进入(或者每次在循环中将变量重置为 NULL)。
【解决方案2】:

您将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 本身。

【讨论】:

  • 我知道如何解决它,我的问题是为什么使用 IS NOT NULL 不起作用,但使用 != NULL 呢?
  • session.real_time_end的内容是什么
  • 它是一个bigint,例如可以有63013593或NULL之类的值。
  • != null 不建议在 MYSQL 中使用,它无法返回真实条件,尽管它会接受您的代码而不会返回任何错误。而是使用IS NOT NULLIS NULL
  • @reds 这就是问题的重点。他尝试使用IS NOT NULL,但没有成功。
猜你喜欢
  • 2014-02-14
  • 1970-01-01
  • 1970-01-01
  • 2017-09-26
  • 1970-01-01
  • 1970-01-01
  • 2011-08-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多