MySQL存储过程插入挂起

时间:2015-05-06 18:32:25

标签: mysql stored-procedures

我尝试做的是编写一个存储过程,该过程将查询视图,处理每一行,并为从视图中提取的每一行进行一次或多次插入。一切似乎都很好,除了在过程中任意中间点,服务器似乎挂在插入命令上。我不知道光标结果集是否存在内存限制,或者可能发生的情况。 SP的相关部分和下面发布的一些澄清意见。

CREATE PROCEDURE `Cache_Network_Observations` ()
BEGIN

-- Declare all variables

/* This cursor is hitting the view which should be returning a number of rows on the scale of ~5M+ records
*/
DECLARE cursor1 CURSOR FOR
SELECT * FROM usanpn2.vw_Network_Observation;

CREATE TABLE Cached_Network_Observation_Temp (observation_id int, name varchar(100), id int);

OPEN cursor1;

load_loop: loop

FETCH cursor1 INTO observation_id, id1, name1, id2, name2, id3, name3, gid1, gname1, gid2, gname2, gid3, gname3;

    IF id1 IS NOT NULL THEN
        INSERT INTO usanpn2.Cached_Network_Observation_Temp values (observation_id, name1, id1);
    END IF;   

    -- some additional logic here, essentially just the same as the above if statement 

END LOOP;
CLOSE cursor1;

END

那是SP,当我实际运行它时,一切都顺利进行,直到进程运行并运行并运行。看一下活动查询报告,我看到了:

| 1076 | root    | localhost                              | mydb | Query   | 3253 | update | INSERT INTO usanpn2.Cached_Network_Observation values ( NAME_CONST('observation_id',2137912),  NAME_ |

NAME_CONST函数来自哪里或与任何事情有什么关系。我已经多次尝试过,视图中的observation_id变量/行每次都有所不同,因此它似乎与记录无关。

TIA!

1 个答案:

答案 0 :(得分:1)

我没有看到你的fetch循环的NOT FOUND处理程序。没有“退出”条件。

DECLARE done INT DEFAULT FALSE;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

fetch之后,立即测试done标志,并在它为真时退出循环。

IF done THEN
  LEAVE load_loop;
END IF;

没有它,我认为你有一个经典的无限循环。

SHOW FULL PROCESSLIST输出中显示的语句正在插入不同的表。 (表名末尾没有_Temp。)

为什么在地球上你需要一个游标循环来处理这个逐行的痛苦行吗?

如果你需要加载一个表,只需加载翻转表,然后完成它。

将所有“声明游标”,“打开游标”,获取循环,退出处理程序,单个插入语句废话替换为执行所需操作的单个语句:

INSERT INTO Cached_Network_Observation_Temp (observation_id, `name`, id)
SELECT s.observation_id, s.name1 AS `name`, s.id1 AS id
  FROM usanpn2.vw_Network_Observation s
 WHERE s.id1 IS NOT NULL

这将更有效率。并且它不会使用大量不必要的INSERT语句阻塞二进制日志。 (这也让我想要备份到更大的图片,并理解为什么甚至需要这个表。这也让我想知道vw_Network_Observation是否是一个视图,以及是否保证了实现派生表的开销。外部查询中的谓词不会被推入视图定义.MySQL处理视图很多与其他RDBMS不同。)

修改

如果注释掉的过程的下一部分是检查id2是否为空以有条件地将id2name2插入_Temp表,那么可以在同样的方式。

或者,多个查询可以与UNION ALL运算符结合使用。

INSERT INTO Cached_Network_Observation_Temp (observation_id, `name`, id)

SELECT s1.observation_id, s1.name1 AS `name`, s1.id1 AS id
  FROM usanpn2.vw_Network_Observation s1
 WHERE s1.id1 IS NOT NULL

 UNION ALL

SELECT s2.observation_id, s2.name2 AS `name`, s2.id2 AS id
  FROM usanpn2.vw_Network_Observation s2
 WHERE s2.id2 IS NOT NULL

......等等。

<强>后续

如果我们需要在一行中生成多行,并且行数不是非常大,我很想测试这样的事情,处理id1id2id3id4一次使用CROSS JOIN行源(s)和人工生成的四行集。

这将从行源(s)每行生成四行,我们可以使用条件表达式返回id1id2等。

举个例子,像这样:

SELECT s.observation_id
     , CASE n.i
         WHEN 1 THEN s.id1
         WHEN 2 THEN s.id2
         WHEN 3 THEN s.id3
         WHEN 4 THEN s.id4
       END AS `id`
     , CASE n.i
         WHEN 1 THEN s.name1
         WHEN 2 THEN s.name2
         WHEN 3 THEN s.name3
         WHEN 4 THEN s.name4
       END AS `name`
  FROM usanpn2.vw_Network_Observation s
 CROSS
  JOIN ( SELECT 1 AS i UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) n
HAVING `id` IS NOT NULL

我们在HAVING子句而不是WHERE子句中使用谓词,因为在访问行时,结果集中为id列生成的值不可用。 HAVING子句中的谓词几乎应用于执行计划中,访问行之后,就在返回行之前。 (我认为满足ORDER BY的“filesort”操作和LIMIT后面的HAVING子句将被应用。)

如果要处理的行数“非常大”,那么我们可以在几个合理大小的批次中获得更好的性能处理行。如果我们的批量大小为2,则每个INSERT处理两行,实际上减半我们需要运行的INSERT数量。每批4行,我们再次将减少一半。一旦我们每批最多有几十行,我们显着减少了我们需要运行的单个INSERT语句的数量。

随着批次逐渐变大,我们的性能提升变得更小。直到批次变得笨拙(“太大”)并且我们开始颠簸到磁盘。在两个极端之间存在性能“最佳点”(一次处理一行与一批处理 ALL 行)。