Mysql
 sql >> Base de données >  >> RDS >> Mysql

Instruction préparée MySQL - Comment boucler

Comme d'autres l'ont déjà suggéré, nous évitons généralement parcourir un jeu de résultats RBAR (ligne par ligne angoissante) principalement pour des raisons de performances. Nous ne voulons tout simplement pas prendre l'habitude de parcourir en boucle un ensemble de résultats. Mais cela ne répond pas à la question que vous avez posée.

Pour répondre à la question que vous avez posée, voici un exemple rudimentaire d'un programme stocké MySQL qui utilise un CURSEUR pour traiter individuellement les lignes renvoyées par une requête. MySQL ne prend pas en charge les blocs anonymes, donc la seule façon de le faire est dans un programme stocké MySQL, comme une PROCÉDURE

DELIMITER $$

CREATE PROCEDURE loop_through_var_list
BEGIN
   DECLARE done INT DEFAULT 0;
   DECLARE v_id INT DEFAULT NULL;  
   DECLARE csr_var_list CURSOR FOR SELECT id FROM var_list ORDER BY id;
   DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
   OPEN csr_var_list;
   get_id: LOOP
      FETCH csr_var_list INTO v_id; 
      IF done = 1 THEN
         LEAVE get_id;
      END IF;

      -- at this point, we have an id value in v_id, so we can do whatever
      SET @s1 = CONCAT('SELECT ... WHERE id =''', v_id, ''' ...');


   END LOOP get_id;
   CLOSE csr_var_list;
END$$

DELIMITER ;

Pour exécuter la procédure :

CALL loop_through_var_list();

REMARQUES :La syntaxe de traitement d'un CURSEUR dans MySQL est assez différente de celle des autres bases de données.

Pour obtenir la "boucle", nous devons utiliser un LOOP ... END LOOP construire.

Mais pour empêcher cette boucle de s'exécuter indéfiniment, nous avons besoin d'une instruction LEAVE qui nous permettra de sortir de la boucle.

Nous utilisons un test conditionnel pour déterminer quand partir. Dans cet exemple, nous voulons quitter après avoir fini de traiter la dernière ligne.

Le FETCH va lever une exception lorsqu'il n'y a plus de lignes à récupérer.

Nous "attrapons" cette exception dans un CONTINUE HANDLER (pour une raison obscure, les "handlers" doivent être les dernières choses déclarées ; MySQL génère une erreur si nous essayons de déclarer quelque chose après un HANDLER (autre qu'un autre HANDLER.)

Lorsque MySQL lève l'exception "no more rows", cela déclenche le code du gestionnaire. Dans cet exemple, nous définissons simplement une variable (nommée done ) à une valeur.

Puisqu'il s'agit d'un gestionnaire "continue", le traitement redémarre à l'instruction où l'exception a été levée, dans ce cas, ce sera l'instruction suivant FETCH. Donc, la première chose que nous faisons est de vérifier si nous avons "terminé" ou non. Si nous avons "terminé", nous quittons la boucle et fermons le curseur.

Sinon, nous savons que nous avons un id valeur de var_list stocké dans une variable de procédure nommée v_id . Alors maintenant, nous pouvons faire ce que nous voulons. On dirait que vous voulez mettre du texte SQL dans une variable définie par l'utilisateur (y compris dans la valeur de v_id dans le texte SQL, puis PREPARE, EXECUTE et DEALLOCATE PREPARE.

Assurez-vous de déclarer le v_id variable avec le type de données approprié, qui correspond au type de données de l'id colonne dans var_list , j'ai juste supposé qu'il s'agissait d'un INT.

Lorsque nous atteignons la fin de la boucle, MySQL "boucle" vers le début de la boucle, et c'est reparti.

Dans le corps de la boucle, vous souhaiterez probablement CONCAT v_id dans le texte SQL que vous souhaitez exécuter. On dirait que vous maîtrisez déjà la préparation PREPARE, DEALLOCATE. Pour les tests, vous pouvez ajouter une clause LIMIT sur le SELECT dans la déclaration du curseur, puis effectuer un simple SELECT v_id ; dans le corps, juste pour vérifier que la boucle fonctionne, avant d'ajouter plus de code.

SUIVI

Je voulais mentionner une autre approche alternative à la tâche, c'est-à-dire exécuter une série d'instructions basées sur un modèle, en remplaçant les valeurs fournies par une seule instruction de sélection SQL...

Par exemple, si j'avais ce modèle :

SELECT * 
  INTO OUTFILE '/tmp/[email protected]'
  FIELDS TERMINATED BY ',' ENCLOSED BY '"'
  LINES TERMINATED BY '\n'
FROM data 
WHERE id = @ID
ORDER BY 1 

et j'avais besoin de remplacer les occurrences de @ID par une valeur d'identifiant spécifique à partir d'une liste renvoyée par une instruction SELECT, par ex.

SELECT id
  FROM var_list
 WHERE id IS NOT NULL
 GROUP BY id

Je n'utiliserais probablement pas un programme stocké MySQL avec une boucle CURSOR, j'utiliserais une approche différente.

J'utiliserais une instruction SELECT pour générer un ensemble d'instructions SQL pouvant être exécutées. En supposant id est de type entier, je ferais probablement quelque chose comme ceci :

SELECT CONCAT(' SELECT * 
                   INTO OUTFILE ''/tmp/orders_',s.id,'.csv''
                   FIELDS TERMINATED BY '','' ENCLOSED BY ''"''
                   LINES TERMINATED BY ''\n''
                FROM data
               WHERE id = ',s.id,'
               ORDER BY 1;') AS `stmt`
 FROM ( SELECT v.id
          FROM var_list v
         WHERE v.id IS NOT NULL
         GROUP BY v.id
      ) s
ORDER BY s.id

Pour chaque valeur de id renvoyé de s , l'instruction renvoie le texte d'une instruction SQL SELECT qui peut (et doit) s'exécuter. Capturer cela dans un fichier texte me donnerait un script SQL que je pourrais exécuter.