程序mysql用光标太慢..为什么?

时间:2012-07-18 20:17:13

标签: mysql performance cursor procedure

我使用游标创建了一个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 ;

我该如何改进?

2 个答案:

答案 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_pa​​cket的限制。)

该方法将显着提高性能,但它不会避免游标的开销,将每一行提取到过程变量中。

这样的事情(在程序的主体中)可能会执行更多,更好,因为它从您的选择中获取结果集并一举将所有行插入到目标表中,而无需费心去做搞乱更新程序中的变量值。

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)

另一个简单的解决方案是通过运行以下查询

将表的引擎更改为MyISAM
ALTER TABLE `socios_numeros_sorteio`
ENGINE=MyISAM;

然后再次打电话给程序。 注意:MyISAM使插入过程非常快