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

Fab*_*bio 3 mysql performance procedure cursor

我使用游标创建了一个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 ;
Run Code Online (Sandbox Code Playgroud)

我该如何改进呢?

spe*_*593 11

这很慢,因为您逐行循环遍历结果集,并为返回的每一行执行单独的insert语句.这就是为什么它会变慢的原因.

让我们简要总结一下你在做什么.首先,您正在运行查询:

select IdSocio
  from socios
 where Sorteio=1
   and Finalizado='S'
   and CodClientes IS NOT NULL;
Run Code Online (Sandbox Code Playgroud)

(显然这些行的返回顺序并不重要.)

然后,对于从该查询返回的每一行,您希望将行插入另一个表中.

insert INTO socios_numeros_sorteio
(IdSocio
,IdSorteio
,NumerodeSorteio
) VALUES
(vc_idsocio
,v_idsorteio
,z);
Run Code Online (Sandbox Code Playgroud)

第一列的值来自查询返回的值.将为第二列的值分配一个作为参数传递给过程的值.并且第三列的值来自从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$$
Run Code Online (Sandbox Code Playgroud)