MySQL 存储过程失败,错误代码为 2013(在查询期间丢失与 MySQL 服务器的连接)
MySQL Stored Procedure fails with Error Code 2013 (Lost connection to MySQL server during query)
在此先感谢您的帮助!
我的问题如下:我有一个名为 fill_enrollment_id() 的存储过程。此过程声明一个游标,其中数据填充一些变量,这些变量稍后用于 SELECT INTO 语句以填充另一个变量 (v_enrollmentid)。然后,该过程检查 SELECT INTO 语句 returns 是否为 NOT FOUND 异常。如果确实引发了 NOT FOUND 异常,则该过程对 table "mdl_edulevel2_log" 的列 "enrollmentid" 进行更新,默认值为“0 - 0”;然后更新 NOT FOUND 处理程序变量,以便游标可以迭代。否则,如果未引发异常,则该过程使用通过 SELECT INTO 语句获得的值执行 UPDATE 语句。最后设置关闭游标和退出循环的指令。程序代码如下:
CREATE DEFINER=`mutual`@`%` PROCEDURE `fill_enrollment_id`()
BEGIN
DECLARE v_id BIGINT DEFAULT 0;
DECLARE v_userid BIGINT DEFAULT 0;
DECLARE v_courseid BIGINT DEFAULT 0;
DECLARE v_timecreated BIGINT DEFAULT 0;
DECLARE v_enrollmentid VARCHAR(255) DEFAULT null;
DECLARE exit_loop BOOLEAN;
DECLARE edulog_cursor CURSOR FOR SELECT id, userid, courseid, timecreated FROM mdl_edulevel2_log WHERE userid not in (0, 2, 3);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;
OPEN edulog_cursor;
edulog_loop: LOOP
FETCH edulog_cursor INTO v_id, v_userid, v_courseid, v_timecreated;
SELECT ifnull(enrollmentid, "0 - 0")
INTO v_enrollmentid
FROM mdl_enrollments
WHERE m_relateduserid = v_userid
AND m_courseid = v_courseid
AND ((v_timecreated >= m_timecreated) AND (v_timecreated <= dm_timecreated OR dm_timecreated = 0));
IF exit_loop THEN
UPDATE mdl_edulevel2_log
SET enrollmentid = "0 - 0"
WHERE id = v_id;
SET exit_loop = FALSE;
ELSE
UPDATE mdl_edulevel2_log
SET enrollmentid = v_enrollmentid
WHERE id = v_id;
END IF;
IF exit_loop THEN
CLOSE edulog_cursor;
LEAVE edulog_loop;
END IF;
END LOOP edulog_loop;
END
在我看来逻辑没有问题,但程序失败并显示 错误代码 2013。查询期间与 MySQL 服务器失去连接。我真的不知道为什么会这样。我的直觉告诉我,这与游标查询的大小(143.115 行)以及我 运行 在我的笔记本电脑(Intel(R) Core(TM) i5 M250 2.4GHz CPU、8GB RAM 和 64 位 Windows 10 Pro)。另一方面,超时间隔在 MySQL Workbench 中设置为 600(见下图)。我不确定增加此值是否会解决问题或使问题变得更糟。
最后,我将与您分享程序中使用的两个 table 的定义(mdl_edulevel2_log 和 mdl_enrollments),因此您可以看到定义了正确的索引所以程序运行得更快。他们在这里:
CREATE TABLE `mdl_edulevel2_log` (
`id` bigint NOT NULL AUTO_INCREMENT,
`eventname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`component` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`action` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`target` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`objecttable` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`objectid` bigint DEFAULT NULL,
`crud` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`edulevel` tinyint(1) NOT NULL,
`contextid` bigint NOT NULL,
`contextlevel` bigint NOT NULL,
`contextinstanceid` bigint NOT NULL,
`userid` bigint NOT NULL,
`courseid` bigint DEFAULT NULL,
`relateduserid` bigint DEFAULT NULL,
`anonymous` tinyint(1) NOT NULL DEFAULT '0',
`other` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`timecreated` bigint NOT NULL,
`origin` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`realuserid` bigint DEFAULT NULL,
`enrollmentid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `mdl_edulevel2log_tim_ix` (`timecreated`),
KEY `mdl_edulevel2log_couanotim_ix` (`courseid`,`anonymous`,`timecreated`),
KEY `mdl_edulevel2log_useconconcr_ix` (`userid`,`contextlevel`,`contextinstanceid`,`crud`,`edulevel`,`timecreated`),
KEY `mdl_edulevel2log_con_ix` (`contextid`),
KEY `idx_mdl_edulevel2_log_userid` (`userid`)
) ENGINE=InnoDB AUTO_INCREMENT=4727019 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Standard log table';
CREATE TABLE `mdl_enrollments` (
`m_id` bigint NOT NULL,
`m_objectid` bigint DEFAULT NULL,
`m_userid` bigint NOT NULL,
`m_courseid` bigint DEFAULT NULL,
`m_relateduserid` bigint DEFAULT NULL,
`m_timecreated` bigint NOT NULL,
`m_ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`dm_id` bigint DEFAULT NULL,
`dm_objectid` bigint DEFAULT NULL,
`dm_userid` bigint DEFAULT NULL,
`dm_courseid` bigint DEFAULT NULL,
`dm_relateduserid` bigint DEFAULT NULL,
`dm_timecreated` bigint DEFAULT NULL,
`dm_ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`enrollmentid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`enrollmentid`),
KEY `idx_mdl_enrollments_m_relateduserid` (`m_relateduserid`),
KEY `idx_mdl_enrollments_m_courseid` (`m_courseid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Standard log table';
但是,老实说,我不太确定是否使用了这些索引。我不知道如何为存储过程调用执行 EXPLAIN 语句。但是,当我分别为查询执行 EXPLAIN 语句时,结果如下:
因此,似乎可以使用索引,但正如我之前所说,我不太确定调用过程后是否会使用索引。
我只能说这么多。我希望你们能给我一些技巧、线索或提示来解决这个问题或以其他方式实现它。如果我还没有提供,请不要犹豫,询问与手头主题相关的其他信息。
来自委内瑞拉的问候,如果我的英语不够清楚,请见谅。
首先,您正在序列化一个不需要的查询。具有 143115 行的游标导致 1 select 和每行 1 次更新导致 143115*2 + 1 = 286231 SQL 操作。这将花费太多时间,以至于您的执行超时(错误 2013)。
其次,您的游标逻辑似乎有点不对劲。当游标循环结束时(没有更多的行要处理),它会将 exit_loop
变量变为 true
。然而,您正在尝试进行上次更新并将 exit_loop
变回 false
,就好像光标应该继续一样。
您可能可以对具有所有逻辑的单个 SQL 操作执行相同操作:
UPDATE mdl_edulevel2_log l
INNER JOIN mdl_enrollments e ON e.m_relateduserid = l.userid
AND ((l.timecreated >= e.m_timecreated)
AND (l.timecreated <= e.dm_timecreated OR e.dm_timecreated = 0)
)
SET enrollmentid = ifnull(e.enrollmentid, "0 - 0")
WHERE l.userid not in (0, 2, 3);
在此先感谢您的帮助!
我的问题如下:我有一个名为 fill_enrollment_id() 的存储过程。此过程声明一个游标,其中数据填充一些变量,这些变量稍后用于 SELECT INTO 语句以填充另一个变量 (v_enrollmentid)。然后,该过程检查 SELECT INTO 语句 returns 是否为 NOT FOUND 异常。如果确实引发了 NOT FOUND 异常,则该过程对 table "mdl_edulevel2_log" 的列 "enrollmentid" 进行更新,默认值为“0 - 0”;然后更新 NOT FOUND 处理程序变量,以便游标可以迭代。否则,如果未引发异常,则该过程使用通过 SELECT INTO 语句获得的值执行 UPDATE 语句。最后设置关闭游标和退出循环的指令。程序代码如下:
CREATE DEFINER=`mutual`@`%` PROCEDURE `fill_enrollment_id`()
BEGIN
DECLARE v_id BIGINT DEFAULT 0;
DECLARE v_userid BIGINT DEFAULT 0;
DECLARE v_courseid BIGINT DEFAULT 0;
DECLARE v_timecreated BIGINT DEFAULT 0;
DECLARE v_enrollmentid VARCHAR(255) DEFAULT null;
DECLARE exit_loop BOOLEAN;
DECLARE edulog_cursor CURSOR FOR SELECT id, userid, courseid, timecreated FROM mdl_edulevel2_log WHERE userid not in (0, 2, 3);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;
OPEN edulog_cursor;
edulog_loop: LOOP
FETCH edulog_cursor INTO v_id, v_userid, v_courseid, v_timecreated;
SELECT ifnull(enrollmentid, "0 - 0")
INTO v_enrollmentid
FROM mdl_enrollments
WHERE m_relateduserid = v_userid
AND m_courseid = v_courseid
AND ((v_timecreated >= m_timecreated) AND (v_timecreated <= dm_timecreated OR dm_timecreated = 0));
IF exit_loop THEN
UPDATE mdl_edulevel2_log
SET enrollmentid = "0 - 0"
WHERE id = v_id;
SET exit_loop = FALSE;
ELSE
UPDATE mdl_edulevel2_log
SET enrollmentid = v_enrollmentid
WHERE id = v_id;
END IF;
IF exit_loop THEN
CLOSE edulog_cursor;
LEAVE edulog_loop;
END IF;
END LOOP edulog_loop;
END
在我看来逻辑没有问题,但程序失败并显示 错误代码 2013。查询期间与 MySQL 服务器失去连接。我真的不知道为什么会这样。我的直觉告诉我,这与游标查询的大小(143.115 行)以及我 运行 在我的笔记本电脑(Intel(R) Core(TM) i5 M250 2.4GHz CPU、8GB RAM 和 64 位 Windows 10 Pro)。另一方面,超时间隔在 MySQL Workbench 中设置为 600(见下图)。我不确定增加此值是否会解决问题或使问题变得更糟。
最后,我将与您分享程序中使用的两个 table 的定义(mdl_edulevel2_log 和 mdl_enrollments),因此您可以看到定义了正确的索引所以程序运行得更快。他们在这里:
CREATE TABLE `mdl_edulevel2_log` (
`id` bigint NOT NULL AUTO_INCREMENT,
`eventname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`component` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`action` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`target` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`objecttable` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`objectid` bigint DEFAULT NULL,
`crud` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`edulevel` tinyint(1) NOT NULL,
`contextid` bigint NOT NULL,
`contextlevel` bigint NOT NULL,
`contextinstanceid` bigint NOT NULL,
`userid` bigint NOT NULL,
`courseid` bigint DEFAULT NULL,
`relateduserid` bigint DEFAULT NULL,
`anonymous` tinyint(1) NOT NULL DEFAULT '0',
`other` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`timecreated` bigint NOT NULL,
`origin` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`realuserid` bigint DEFAULT NULL,
`enrollmentid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `mdl_edulevel2log_tim_ix` (`timecreated`),
KEY `mdl_edulevel2log_couanotim_ix` (`courseid`,`anonymous`,`timecreated`),
KEY `mdl_edulevel2log_useconconcr_ix` (`userid`,`contextlevel`,`contextinstanceid`,`crud`,`edulevel`,`timecreated`),
KEY `mdl_edulevel2log_con_ix` (`contextid`),
KEY `idx_mdl_edulevel2_log_userid` (`userid`)
) ENGINE=InnoDB AUTO_INCREMENT=4727019 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Standard log table';
CREATE TABLE `mdl_enrollments` (
`m_id` bigint NOT NULL,
`m_objectid` bigint DEFAULT NULL,
`m_userid` bigint NOT NULL,
`m_courseid` bigint DEFAULT NULL,
`m_relateduserid` bigint DEFAULT NULL,
`m_timecreated` bigint NOT NULL,
`m_ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`dm_id` bigint DEFAULT NULL,
`dm_objectid` bigint DEFAULT NULL,
`dm_userid` bigint DEFAULT NULL,
`dm_courseid` bigint DEFAULT NULL,
`dm_relateduserid` bigint DEFAULT NULL,
`dm_timecreated` bigint DEFAULT NULL,
`dm_ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`enrollmentid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`enrollmentid`),
KEY `idx_mdl_enrollments_m_relateduserid` (`m_relateduserid`),
KEY `idx_mdl_enrollments_m_courseid` (`m_courseid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Standard log table';
但是,老实说,我不太确定是否使用了这些索引。我不知道如何为存储过程调用执行 EXPLAIN 语句。但是,当我分别为查询执行 EXPLAIN 语句时,结果如下:
因此,似乎可以使用索引,但正如我之前所说,我不太确定调用过程后是否会使用索引。
我只能说这么多。我希望你们能给我一些技巧、线索或提示来解决这个问题或以其他方式实现它。如果我还没有提供,请不要犹豫,询问与手头主题相关的其他信息。
来自委内瑞拉的问候,如果我的英语不够清楚,请见谅。
首先,您正在序列化一个不需要的查询。具有 143115 行的游标导致 1 select 和每行 1 次更新导致 143115*2 + 1 = 286231 SQL 操作。这将花费太多时间,以至于您的执行超时(错误 2013)。
其次,您的游标逻辑似乎有点不对劲。当游标循环结束时(没有更多的行要处理),它会将 exit_loop
变量变为 true
。然而,您正在尝试进行上次更新并将 exit_loop
变回 false
,就好像光标应该继续一样。
您可能可以对具有所有逻辑的单个 SQL 操作执行相同操作:
UPDATE mdl_edulevel2_log l
INNER JOIN mdl_enrollments e ON e.m_relateduserid = l.userid
AND ((l.timecreated >= e.m_timecreated)
AND (l.timecreated <= e.dm_timecreated OR e.dm_timecreated = 0)
)
SET enrollmentid = ifnull(e.enrollmentid, "0 - 0")
WHERE l.userid not in (0, 2, 3);