2012-07-18 3 views
2

Je crée une procédure Mysql l'aide du curseur, mais il est géré trop lent ... Il est obtenir entre 40 et 60 lignes par seconde .. Voir:Procédure mysql avec le curseur est trop lent .. Pourquoi?

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 ; 

comment puis-je améliorer cela?

Répondre

9

Cette opération est lente car vous effectuez une boucle dans un ensemble de résultats, ligne par ligne, et exécutez des instructions d'insertion individuelles pour chaque ligne renvoyée. C'est pourquoi ça va être lent.

Résumons brièvement ce que vous faites. Tout d'abord, vous exécutez une requête:

select IdSocio 
    from socios 
where Sorteio=1 
    and Finalizado='S' 
    and CodClientes IS NOT NULL; 

(. Apparemment, l'ordre de ces lignes sont renvoyées dans n'a pas d'importance)

Ensuite, pour chaque ligne renvoyée de cette requête, vous voulez insérer une ligne dans un autre table.

insert INTO socios_numeros_sorteio 
(IdSocio 
,IdSorteio 
,NumerodeSorteio 
) VALUES 
(vc_idsocio 
,v_idsorteio 
,z); 

La valeur de la première colonne provient d'une valeur renvoyée par la requête. La valeur de la deuxième colonne est affectée d'une valeur passée en tant qu'argument à la procédure. Et la valeur de la troisième colonne provient d'un compteur qui commence à 1 et est incrémenté de 1 pour chaque ligne. MySQL est optimisé pour effectuer une opération comme celle-ci. Mais ce n'est PAS optimisé pour le faire en utilisant une procédure stockée qui parcourt un curseur ligne par ligne. Si vous souhaitez obtenir des performances raisonnables, vous DEVEZ REDUIRE DE MANIÈRE SIGNIFICATIVE le nombre d'instructions INSERT individuelles que vous exécutez, et penser plutôt au traitement des données dans des "ensembles" plutôt qu'à des lignes individuelles. Une approche consiste à grouper les lignes dans des instructions "insert étendu", qui peuvent insérer plusieurs lignes à la fois. (Le nombre de lignes que vous pouvez insérer dans une instruction est effectivement limité par max_allowed_packet.)

Cette approche améliorera de manière significative les performances, mais elle n'évite pas le surdébit du curseur, en récupérant chaque ligne dans les variables de procédure. Quelque chose comme cela (dans le corps de votre procédure) est susceptible de fonctionner beaucoup, beaucoup mieux, car il prend le jeu de résultats de votre sélection et insère toutes les lignes dans la table de destination d'un seul coup, sans déranger de jouer avec la mise à jour des valeurs des variables dans la procédure.

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$$ 
+0

I seconde, 'INSERT .. select' est un excellent outil pour apprendre – spacediver

+0

Excellente réponse :) –

+1

Un avertissement est nécessaire en ce qui concerne cet exemple. Les tables impliquées dans de telles instructions d'insertion resteront verrouillées pendant la durée de l'insertion, empêchant ainsi d'autres actions (par exemple, sélectionner, insérer, mettre à jour, supprimer) sur les tables tant que le verrou est en place. –

0

Une autre solution simple est seulement de changer le moteur de la table à MyISAM en exécutant la requête ci-dessous,

ALTER TABLE `socios_numeros_sorteio` 
ENGINE=MyISAM; 

ensuite appeler à nouveau la procédure. Note: MyISAM rendre le processus d'insertion très rapide