web-dev-qa-db-fra.com

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

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

Maintenant, je veux parcourir toutes les lignes de A.

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

C'est pour chaque ligne que je veux prendre un champ et le transformer, l'insérer dans une autre table, puis mettre à jour certains champs de la ligne. Je peux mettre la partie sélectionnée et l'insert dans une seule déclaration, mais je ne sais pas non plus comment obtenir la mise à jour. Alors je veux faire une boucle. Et pour la pratique, je ne veux utiliser autre chose que MySQL.

modifier

J'apprécierais un exemple.

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

modifier 2

ok pense à ce scénario:

Tableau A et B, chacun avec les champs ID et VAL.

Voici 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]);
}

essentiellement copier 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.) }

59
Raffael

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

Toute requête qui fonctionne sur un seul enregistrement pris dans une table peut être encapsulée dans une procédure pour la faire passer par chaque ligne d'une table de la manière suivante:

DROP PROCEDURE IF EXISTS ROWPERROW;
DELIMITER ;;

Ensuite, voici la procédure selon votre exemple (table_A et table_B utilisées 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;
;;

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

DELIMITER ;

Et lancez 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 demande d'exemple.

Notez ATTENTIVEMENT que la ligne "INSERT INTO" utilisée ici reflète celle de la question. Selon les commentaires de cette réponse, vous devez vous assurer que votre requête est syntaxiquement correcte pour chaque version de SQL que vous exécutez.

Dans le cas simple où votre champ ID est incrémenté et commence à 1, la ligne de l'exemple peut 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 permettra de 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 constituait le seul moyen de réaliser un processus sur une table en insérant dynamiquement différents nombres d’enregistrements dans une nouvelle table à partir de chaque ligne d’une table parente.

Si vous avez besoin que cela fonctionne plus vite, essayez de le définir en fonction des autres. Sinon, tout va bien .. Vous pouvez également réécrire ce qui précède sous forme de curseur, mais cela n'améliorera peut-être pas 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 étant du même type que celles des tables interrogées.

Mon conseil est d’aller avec les requêtes basées sur les ensembles quand vous le pouvez, et d’utiliser des boucles simples ou des curseurs si nécessaire.

90
Mr Purple

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
Raj More

Les CURSEURS sont une option, mais ils sont généralement mal vus, car ils n’utilisent souvent pas au mieux le moteur de recherche. Envisagez d’enquêter sur les «requêtes basées sur SET» pour voir si vous pouvez réaliser ce que vous voulez faire sans utiliser de CURSEUR. 

4
Ron Weston

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
Erkan RUA