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

Comment sélectionner toutes les tables avec le nom de la colonne et mettre à jour cette colonne

Non, pas dans une seule déclaration.

Pour obtenir les noms de toutes les tables contenant la colonne nommée Foo :

SELECT table_schema, table_name
  FROM information_schema.columns 
  WHERE column_name = 'Foo'

Ensuite, vous auriez besoin d'une instruction UPDATE pour chaque table. (Il est possible de mettre à jour plusieurs tables dans une seule instruction, mais cela nécessiterait une jointure croisée (inutile).) Il est préférable de faire chaque table séparément.

Vous pouvez utiliser SQL dynamique pour exécuter les instructions UPDATE dans un programme stocké MySQL (par exemple, PROCEDURE)

  DECLARE sql VARCHAR(2000);
  SET sql = 'UPDATE db.tbl SET Foo = 0';
  PREPARE stmt FROM sql;
  EXECUTE stmt;
  DEALLOCATE stmt;

Si vous déclarez un curseur pour le select from information_schema.tables, vous pouvez utiliser une boucle de curseur pour traiter un UPDATE dynamique instruction pour chaque table_name renvoyée.

  DECLARE done TINYINT(1) DEFAULT FALSE;
  DECLARE sql  VARCHAR(2000);

  DECLARE csr FOR
  SELECT CONCAT('UPDATE `',c.table_schema,'`.`',c.table_name,'` SET `Foo` = 0') AS sql
    FROM information_schema.columns c
   WHERE c.column_name = 'Foo'
     AND c.table_schema NOT IN ('mysql','information_schema','performance_schema');
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

  OPEN csr;
  do_foo: LOOP
     FETCH csr INTO sql;
     IF done THEN
        LEAVE do_foo;
     END IF;
     PREPARE stmt FROM sql;
     EXECUTE stmt;
     DEALLOCATE PREPARE stmt;
  END LOOP do_foo;
  CLOSE csr;

(Ceci n'est qu'un aperçu d'un exemple, pas de syntaxe vérifiée ou testée.)

SUIVI

Quelques brèves notes sur certaines idées qui ont probablement été passées sous silence dans la réponse ci-dessus.

Pour obtenir les noms des tables contenant la colonne Foo , nous pouvons exécuter une requête depuis le information_schema.columns table. (C'est l'une des tables fournies dans MySQL information_schema base de données.)

Étant donné que nous pouvons avoir des tables dans plusieurs bases de données, le nom_table n'est pas suffisant pour identifier une table ; nous devons savoir dans quelle base de données se trouve la table. Plutôt que de jouer avec un "use db " avant d'exécuter une UPDATE , nous pouvons simplement référencer la table UPDATE db.mytable SET Foo... .

Nous pouvons utiliser notre requête de information_schema.columns pour aller de l'avant et enchaîner (concaténer) les parties que nous devons créer pour une instruction UPDATE, et que le SELECT renvoie les instructions réelles que nous aurions besoin d'exécuter pour mettre à jour la colonne Foo , en gros ceci :

UPDATE `mydatabase`.`mytable` SET `Foo` = 0 

Mais nous voulons remplacer les valeurs de table_schema et table_name à la place de mydatabase et mytable . Si nous exécutons ce SELECT

SELECT 'UPDATE `mydatabase`.`mytable` SET `Foo` = 0' AS sql

Cela nous renvoie une seule ligne, contenant une seule colonne (la colonne se trouve être nommée sql , mais le nom de la colonne n'est pas important pour nous). La valeur de la colonne sera simplement une chaîne. Mais la chaîne que nous récupérons est (nous l'espérons) une instruction SQL que nous pourrions exécuter.

Nous obtiendrions la même chose si nous cassions cette chaîne en morceaux et utilisions CONCAT pour les reconstituer pour nous, par exemple

SELECT CONCAT('UPDATE `','mydatabase','`.`','mytable','` SET `Foo` = 0') AS sql

Nous pouvons utiliser cette requête comme modèle pour l'instruction que nous voulons exécuter sur information_schema.columns . Nous remplacerons 'mydatabase' et 'mytable' avec des références aux colonnes de information_schema.columns table qui nous donne la base de données et table_name.

SELECT CONCAT('UPDATE `',c.table_schema,'`.`',c.table_name,'` SET `Foo` = 0') AS sql
  FROM information_schema.columns 
 WHERE c.column_name = 'Foo'

Il y a certaines bases de données que nous ne faisons certainement pas voulez mettre à jour... mysql , information_schema , performance_schema . Nous devons soit ajouter à la liste blanche les bases de données contenant la table que nous voulons mettre à jour

  AND c.table_schema IN ('mydatabase','anotherdatabase')

-ou - nous devons mettre sur liste noire les bases de données que nous ne voulons absolument pas mettre à jour

  AND c.table_schema NOT IN ('mysql','information_schema','performance_schema')

Nous pouvons exécuter cette requête (nous pourrions ajouter un ORDER BY si nous voulons que les lignes soient renvoyées dans un ordre particulier) et ce que nous récupérons est une liste contenant les instructions que nous voulons exécuter. Si nous enregistrions cet ensemble de chaînes sous forme de fichier texte brut (à l'exclusion de la ligne d'en-tête et du formatage supplémentaire), en ajoutant un point-virgule à la fin de chaque ligne, nous aurions un fichier que nous pourrions exécuter à partir du mysql> client de ligne de commande.

(Si l'un des éléments ci-dessus prête à confusion, faites-le moi savoir.)

La partie suivante est un peu plus compliquée. Le reste de ceci traite d'une alternative à l'enregistrement de la sortie de SELECT sous forme de fichier texte brut et à l'exécution des instructions à partir de mysql client de ligne de commande.

MySQL fournit une installation/fonctionnalité qui nous permet d'exécuter essentiellement tout chaîne en tant qu'instruction SQL, dans le contexte d'un programme stocké MySQL (par exemple, une procédure stockée. La fonctionnalité que nous allons utiliser s'appelle SQL dynamique .

Pour utiliser le SQL dynamique , on utilise les instructions PREPARE , EXECUTE et DEALLOCATE PREPARE . (La désallocation n'est pas strictement nécessaire, MySQL nettoiera pour nous si nous ne l'utilisons pas, mais je pense que c'est une bonne pratique de le faire quand même.)

Encore une fois, SQL dynamique est disponible UNIQUEMENT dans le contexte d'un programme stocké MySQL. Pour ce faire, nous avons besoin d'une chaîne contenant l'instruction SQL que nous voulons exécuter. Comme exemple simple, disons que nous avions ceci :

DECLARE str VARCHAR(2000);
SET str = 'UPDATE mytable SET mycol = 0 WHERE mycol < 0';

Pour obtenir le contenu de str évalué et exécuté comme une instruction SQL, le schéma de base est :

PREPARE stmt FROM str;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

La prochaine partie compliquée consiste à associer cela à la requête que nous exécutons pour obtenir la valeur de chaîne que nous voulons exécuter en tant qu'instructions SQL. Pour ce faire, nous avons créé une boucle de curseur. Le schéma de base pour cela est de prendre notre instruction SELECT :

SELECT bah FROM humbug

Et transformez cela en une définition de curseur :

DECLARE mycursor FOR SELECT bah FROM humbug ;

Ce que nous voulons, c'est exécuter cela et parcourir les lignes qu'il renvoie. Pour exécuter l'instruction et préparer un jeu de résultats, nous "ouvrons" le curseur

OPEN mycursor; 

Lorsque nous en avons terminé, nous allons émettre une "fermeture", pour publier le jeu de résultats, afin que le serveur MySQL sache que nous n'en avons plus besoin, et peut nettoyer et libérer les ressources allouées à cela.

CLOSE mycursor;

Mais, avant de fermer le curseur, nous voulons "faire une boucle" dans le jeu de résultats, récupérer chaque ligne et faire quelque chose avec la ligne. L'instruction que nous utilisons pour obtenir la ligne suivante du jeu de résultats dans une variable de procédure est :

FETCH mycursor INTO some_variable;

Avant de pouvoir récupérer des lignes dans des variables, nous devons définir les variables, par exemple

DECLARE some_variable VARCHAR(2000); 

Étant donné que notre curseur (instruction SELECT) ne renvoie qu'une seule colonne, nous n'avons besoin que d'une seule variable. Si nous avions plus de colonnes, nous aurions besoin d'une variable pour chaque colonne.

Finalement, nous aurons récupéré la dernière ligne du jeu de résultats. Lorsque nous tentons de récupérer le suivant, MySQL va générer une erreur.

D'autres langages de programmation nous laisseraient simplement faire un while boucle, et récupérons les lignes et quittons la boucle lorsque nous les avons toutes traitées. MySQL est plus mystérieux. Pour faire une boucle :

mylabel: LOOP
  -- do something
END LOOP mylabel;

Cela en soi fait une boucle infinie très fine, car cette boucle n'a pas de "sortie". Heureusement, MySQL nous donne le LEAVE instruction comme moyen de sortir d'une boucle. Nous ne voulons généralement pas quitter la boucle la première fois que nous y entrons, donc il y a généralement un test conditionnel que nous utilisons pour déterminer si nous avons terminé et devrions quitter la boucle, ou nous n'avons pas terminé et devrions faire le tour la boucle à nouveau.

 mylabel: LOOP
     -- do something useful
     IF some_condition THEN 
         LEAVE mylabel;
     END IF;
 END LOOP mylabel;

Dans notre cas, nous voulons parcourir toutes les lignes du jeu de résultats, nous allons donc mettre un FETCH a la première instruction à l'intérieur de la boucle (ce que nous voulons faire d'utile).

Pour obtenir un lien entre l'erreur générée par MySQL lorsque nous tentons d'extraire la dernière ligne du jeu de résultats et le test conditionnel, nous devons déterminer si nous devons partir...

MySQL nous permet de définir un CONTINUE HANDLER (une déclaration que nous voulons exécutée) lorsque l'erreur est générée...

 DECLARE CONTINUE HANDLER FOR NOT FOUND 

L'action que nous voulons effectuer est de définir une variable sur TRUE.

 SET done = TRUE;

Avant de pouvoir exécuter le SET, nous devons définir la variable :

 DECLARE done TINYINT(1) DEFAULT FALSE;

Avec cela, nous pouvons changer notre BOUCLE pour tester si le done est définie sur TRUE, comme condition de sortie, donc notre boucle ressemble à ceci :

 mylabel: LOOP
     FETCH mycursor INTO some_variable;
     IF done THEN 
         LEAVE mylabel;
     END IF;
     -- do something with the row
 END LOOP mylabel;

Le "faire quelque chose avec la ligne" est l'endroit où nous voulons prendre le contenu de some_variable et faire quelque chose d'utile avec. Notre curseur nous renvoie une chaîne que nous voulons exécuter en tant qu'instruction SQL. Et MySQL nous donne le SQL dynamique fonctionnalité que nous pouvons utiliser pour le faire.

REMARQUE :MySQL a des règles sur l'ordre des instructions dans la procédure. Par exemple le DECLARE déclaration doit venir au début. Et je pense que le CONTINUE HANDLER doit être la dernière chose déclarée.

Encore une fois :le curseur et SQL dynamique les fonctionnalités sont disponibles UNIQUEMENT dans le contexte d'un programme stocké MySQL, tel qu'une procédure stockée. L'exemple que j'ai donné ci-dessus n'était que l'exemple du corps d'une procédure.

Pour que cela soit créé en tant que procédure stockée, il faudrait l'incorporer dans quelque chose comme ceci :

DELIMITER $$

DROP PROCEDURE IF EXISTS myproc $$

CREATE PROCEDURE myproc 
NOT DETERMINISTIC
MODIFIES SQL DATA
BEGIN

   -- procedure body goes here

END$$

DELIMITER ;

J'espère que cela explique l'exemple que j'ai donné un peu plus en détail.