118 votes

Comment puis-je parcourir en boucle toutes les lignes d'un tableau ? (MySQL)

J'ai une table A et une clé primaire ID.

Je veux maintenant parcourir toutes les lignes de A.

J'ai trouvé quelque chose comme "pour chaque enregistrement dans A", mais il semble que ce ne soit pas la façon de procéder dans MySQL.

Le problème est que pour chaque ligne, je veux prendre un champ et le transformer, l'insérer dans une autre table et ensuite mettre à jour certains des champs de la ligne. Je peux mettre la partie sélection et l'insertion dans une seule instruction, mais je ne sais pas comment faire pour que la mise à jour soit également incluse. Je veux donc faire une boucle. Et pour la pratique, je ne veux pas utiliser autre chose que MySQL.

éditer

J'aimerais avoir un exemple.

Et une solution qui n'a pas besoin d'être intégrée dans une procédure.

modifier 2

imaginez ce scénario :

Table A et B, chacune avec les champs ID et VAL.

Voici maintenant le pseudo-code de ce que je veux faire :

for(each row in A as rowA)
{
  insert into B(ID, VAL) values(rowA[ID], rowA[VAL]);
}

Il s'agit essentiellement de copier le contenu de A dans B à l'aide d'une boucle.

(il s'agit d'un exemple simplifié, il est évident que vous n'utiliserez pas de boucle pour cela). }

170voto

Mr Purple Points 308

Puisque la suggestion d'une boucle implique la demande d'une solution de type procédure. Voici la mienne.

Toute requête portant sur un enregistrement unique extrait d'un tableau peut être intégrée dans une procédure afin d'être exécutée sur chaque ligne du tableau, comme suit :

Supprimez d'abord toute procédure existante portant le même nom et modifiez le délimiteur afin que votre SQL n'essaie pas d'exécuter chaque ligne pendant que vous essayez d'écrire la procédure.

DROP PROCEDURE IF EXISTS ROWPERROW;
DELIMITER ;;

Voici donc la procédure selon votre exemple (les tableaux_A et_B sont utilisés pour plus de clarté)

CREATE PROCEDURE ROWPERROW()
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
SELECT COUNT(*) FROM table_A INTO n;
SET i=0;
WHILE i<n DO 
  INSERT INTO table_B(ID, VAL) SELECT (ID, VAL) FROM table_A LIMIT i,1;
  SET i = i + 1;
END WHILE;
End;
;;

Ensuite, n'oubliez pas de réinitialiser le délimiteur.

DELIMITER ;

Et exécutez la nouvelle procédure

CALL ROWPERROW();

Vous pouvez faire ce que vous voulez à la ligne "INSERT INTO" que j'ai simplement copiée à partir de votre exemple de demande.

Notez BIEN que la ligne "INSERT INTO" utilisée ici correspond à la ligne de la question. Comme indiqué dans les commentaires de cette réponse, vous devez vous assurer que votre requête est syntaxiquement correcte, quelle que soit la version de SQL que vous utilisez.

Dans le cas simple où votre champ ID est incrémenté et commence à 1, la ligne de l'exemple pourrait devenir :

INSERT INTO table_B(ID, VAL) VALUES(ID, VAL) FROM table_A WHERE ID=i;

Remplacement de la ligne "SELECT COUNT" par

SET n=10;

Vous pourrez tester votre requête sur les 10 premiers enregistrements de la table_A uniquement.

Une dernière chose. Ce processus est également très facile à imbriquer dans différentes tables et c'était le seul moyen pour moi d'exécuter un processus sur une table qui insérait dynamiquement différents nombres d'enregistrements dans une nouvelle table à partir de chaque ligne d'une table parente.

Si vous avez besoin qu'il fonctionne plus rapidement, essayez de le paramétrer, mais si ce n'est pas le cas, c'est très bien. Vous pouvez également réécrire ce qui précède sous la forme d'un curseur, mais cela n'améliorera pas les performances :

DROP PROCEDURE IF EXISTS cursor_ROWPERROW;
DELIMITER ;;

CREATE PROCEDURE cursor_ROWPERROW()
BEGIN
  DECLARE cursor_ID INT;
  DECLARE cursor_VAL VARCHAR;
  DECLARE done INT DEFAULT FALSE;
  DECLARE cursor_i CURSOR FOR SELECT ID,VAL FROM table_A;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
  OPEN cursor_i;
  read_loop: LOOP
    FETCH cursor_i INTO cursor_ID, cursor_VAL;
    IF done THEN
      LEAVE read_loop;
    END IF;
    INSERT INTO table_B(ID, VAL) VALUES(cursor_ID, cursor_VAL);
  END LOOP;
  CLOSE cursor_i;
END;
;;

N'oubliez pas de déclarer les variables que vous utiliserez comme étant du même type que celles des tables interrogées.

Je vous conseille d'utiliser des requêtes basées sur des ensembles lorsque vous le pouvez, et de n'utiliser des boucles simples ou des curseurs que si vous devez le faire.

24voto

Raj More Points 22358

Vous devriez vraiment utiliser une solution basée sur un ensemble impliquant deux requêtes (insertion de base) :

INSERT INTO TableB (Id2Column, Column33, Column44)
SELECT id, column1, column2 FROM TableA

UPDATE TableA SET column1 = column2 * column3

Et pour votre transformation :

INSERT INTO TableB (Id2Column, Column33, Column44)
SELECT 
    id, 
    column1 * column4 * 100, 
    (column2 / column12) 
FROM TableA

UPDATE TableA SET column1 = column2 * column3

Si votre transformation est plus compliquée que cela et implique plusieurs tables, posez une autre question avec les détails.

4voto

Ron Weston Points 280

Les CURSEURS sont une option ici, mais ils sont généralement déconseillés car ils ne permettent pas d'utiliser au mieux le moteur de recherche. Envisagez d'étudier les "requêtes basées sur la méthode SET" pour voir si vous pouvez réaliser ce que vous voulez faire sans utiliser de CURSEUR.

2voto

Erkan RUA Points 1

L'exemple de M. Purple que j'ai utilisé dans mysql déclenche comme cela,

begin
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
Select COUNT(*) from user where deleted_at is null INTO n;
SET i=0;
WHILE i<n DO 
  INSERT INTO user_notification(notification_id,status,userId)values(new.notification_id,1,(Select userId FROM user LIMIT i,1)) ;
  SET i = i + 1;
END WHILE;
end

-32voto

utee Points 1
    Use this:

    $stmt = $user->runQuery("SELECT * FROM tbl WHERE ID=:id");
    $stmt->bindparam(":id",$id);
    $stmt->execute();

        $stmt->bindColumn("a_b",$xx);
        $stmt->bindColumn("c_d",$yy);

    while($rows = $stmt->fetch(PDO::FETCH_BOUND))
    {
        //---insert into new tble
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X