我使用游标创建了一个Mysql程序,但是它运行得太慢了......它每秒得到40到60行......参见:
DELIMITER $$
CREATE PROCEDURE sp_create(IN v_idsorteio INT,OUT afetados INT)
BEGIN
DECLARE done INT default 0;
DECLARE vc_idsocio INT;
DECLARE z INT;
DECLARE cur1 CURSOR FOR select IdSocio from socios where Sorteio=1 and Finalizado='S' and CodClientes IS NOT NULL;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
SET z=1;
OPEN cur1;
FETCH cur1 INTO vc_idsocio;
WHILE done=0 DO
-- SELECT register as t;
insert INTO socios_numeros_sorteio (IdSocio,IdSorteio,NumerodeSorteio) VALUES (vc_idsocio,v_idsorteio,z);
FETCH cur1 INTO vc_idsocio;
SET z = z+1;
END WHILE;
CLOSE cur1;
Select z-1 as total INTO afetados;
END$$
DELIMITER ;
我该如何改进?
答案 0 :(得分:10)
这很慢,因为您逐行循环遍历结果集,并为返回的每一行执行单独的insert语句。这就是为什么它会变慢的原因。
让我们简要总结一下你在做什么。首先,您正在运行查询:
select IdSocio
from socios
where Sorteio=1
and Finalizado='S'
and CodClientes IS NOT NULL;
(显然,返回这些行的顺序并不重要。)
然后,对于从该查询返回的每一行,您希望在另一个表中插入一行。
insert INTO socios_numeros_sorteio
(IdSocio
,IdSorteio
,NumerodeSorteio
) VALUES
(vc_idsocio
,v_idsorteio
,z);
第一列的值来自查询返回的值。 将为第二列的值分配一个作为参数传递给过程的值。 并且第三列的值来自一个从1开始的计数器,并且每行增加1。
MySQL经过优化,可以执行这样的操作。但是并没有使用逐行循环遍历游标的存储过程来优化它。
如果您希望获得一些合理的性能,则需要显着减少所运行的单个INSERT语句的数量,而是考虑在“集合”而不是单个行中处理数据。一种方法是将行批处理为“扩展插入”语句,它可以一次插入多行。 (您可以在一个语句中插入的行数实际上受到max_allowed_packet的限制。)
该方法将显着提高性能,但它不会避免游标的开销,将每一行提取到过程变量中。
这样的事情(在程序的主体中)可能会执行更多,更好,因为它从您的选择中获取结果集并一举将所有行插入到目标表中,而无需费心去做搞乱更新程序中的变量值。
BEGIN
SET @idsorteio = v_idsorteio;
INSERT INTO socios_numeros_sorteio
( IdSocio
, IdSorteio
, NumerodeSorteio
)
SELECT s.IdSocio AS IdSocio
, @idsorteio AS IdSorteio
, @z := @z+1 AS NumerodeSorteio
FROM socios s
JOIN (SELECT @z := 0) z
WHERE s.Sorteio=1
AND s.Finalizado='S'
AND s.CodClientes IS NOT NULL;
SELECT ROW_NUMBER() INTO afetados;
END$$
答案 1 :(得分:0)
另一个简单的解决方案是通过运行以下查询
将表的引擎更改为MyISAMALTER TABLE `socios_numeros_sorteio`
ENGINE=MyISAM;
然后再次打电话给程序。 注意:MyISAM使插入过程非常快