如果我按原样运行此存储过程,它会给出错误的返回值,当我检查我的日志时,它从不记录其他条件:
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中的一个错误吗?
答案 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_end
为NULL
或0
您可以使用:
IF v_end != NULL or v_end = 0 THEN
我不确定你的session.real_time_end
,但你可以尝试一下。
正如我所观察到的,你可以使用像LENGTH FUNCTION:
IF LENGTH(v_end)>0
它将接受NULL == 0否则返回Length本身。