Comment puis-je parcourir toutes les lignes d'une table? (MySQL)

J'ai une table A et il y a un ID de clé primaire.

Maintenant, je veux passer par toutes les lignes de A.

J'ai trouvé quelque chose comme 'pour chaque enregistrement dans A', mais cela ne semble pas être la façon dont vous le faites dans MySQL.

La chose est pour chaque ligne que je veux prendre un champ et le transformer, l'insérer dans une autre table, puis mettre à jour certains des champs de la ligne. Je peux mettre la partie select et l'insertion dans une instruction, mais je ne sais pas comment obtenir la mise à jour là aussi. Je tiens donc à boucle. Et pour la pratique, Je ne veux pas utiliser autre chose que MySQL.

Modifier

J'apprécierais un exemple.

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

Modifier 2

Pensez à ce scénario:

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

Maintenant, c'est le pseudo-code pour ce que je veux faire:

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

Copie essentiellement le contenu de A dans B en utilisant une boucle.

(Ceci est juste un exemple simplifié, bien sûr, vous n'utiliseriez pas de boucle pour cela.) }

57
demandé sur Raffael 2011-04-28 14:48:20

5 réponses

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

Toute requête qui fonctionne sur un seul enregistrement pris à partir d'une table peut être enveloppée dans une procédure pour la faire fonctionner à travers chaque ligne d'une table comme ceci:

DROP PROCEDURE IF EXISTS ROWPERROW;
DELIMITER ;;

Alors voici la procédure selon votre exemple (table_A et table_B 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) VALUES(ID, VAL) FROM table_A LIMIT i,1;
  SET i = i + 1;
END WHILE;
End;
;;

Alors n'oubliez pas de réinitialiser le délimiteur

DELIMITER ;

Et exécutez la nouvelle procédure

CALL ROWPERROW();

Vous pouvez faire tout ce que vous voulez à la ligne "insérer dans" que j'ai simplement copié à partir de votre demande d'exemple.

Notez soigneusement que la ligne "insérer dans" utilisée ici reflète la ligne de la question. Selon les commentaires de cette réponse, vous devez vous assurer que votre requête est syntaxiquement correcte pour quelle version de SQL 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 du " SELECT COUNT" ligne avec

SET n=10;

Vous permettra de tester votre requête sur le premier enregistrement 10 dans table_A uniquement.

Une dernière chose. Ce processus est également très facile à imbriquer sur différentes tables et était le seul moyen d'effectuer 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 parent.

Si vous en avez besoin pour courir plus vite, essayez de le définir en fonction, sinon, c'est bien. Vous pouvez également réécrire ce qui précède dans curseur forme mais il peut ne pas améliorer les performances. par exemple:

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 du même type que celles des tables interrogées.

Mon conseil est d'aller avec des requêtes setbased quand vous le pouvez, et n'utilisez que des boucles simples ou des curseurs si vous le devez.

81
répondu Mr Purple 2017-09-13 20:59:40

Vous devriez vraiment utiliser une solution basée sur un ensemble impliquant deux requêtes (insert 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

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

11
répondu Raj More 2011-04-28 11:22:56

Les curseurs sont une option ici, mais généralement mal vus car ils ne font souvent pas le meilleur usage du moteur de requête. Envisagez d'enquêter sur les "requêtes basées sur les ensembles" pour voir si vous pouvez réaliser ce que vous voulez faire sans utiliser de curseur.

4
répondu Ron Weston 2011-04-28 10:57:56

L'exemple de M. Purple que j'ai utilisé dans mysql trigger comme ça,

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
0
répondu Erkan RUA 2018-09-13 07:14:04
    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
    }   
-14
répondu utee 2016-08-22 23:49:23