Mysql
 sql >> Datenbank >  >> RDS >> Mysql

So wählen Sie alle Tabellen mit Spaltennamen aus und aktualisieren diese Spalte

Nein, nicht in einer einzigen Aussage.

Um die Namen aller Tabellen zu erhalten, die die Spalte mit dem Namen Foo enthalten :

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

Dann benötigen Sie für jede Tabelle eine UPDATE-Anweisung. (Es ist möglich, mehrere Tabellen in einer einzigen Anweisung zu aktualisieren, aber das müsste ein (unnötiger) Cross Join sein.) Es ist besser, jede Tabelle separat zu machen.

Sie könnten dynamisches SQL verwenden, um die UPDATE-Anweisungen in einem gespeicherten MySQL-Programm auszuführen (z. B. PROCEDURE)

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

Wenn Sie einen Cursor für die Auswahl aus information_schema.tables deklarieren, können Sie eine Cursorschleife verwenden, um ein dynamisches UPDATE zu verarbeiten -Anweisung für jeden zurückgegebenen Tabellennamen.

  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;

(Dies ist nur ein grober Umriss eines Beispiels, nicht syntaxgeprüft oder getestet.)

NACHVERFOLGUNG

Einige kurze Anmerkungen zu einigen Ideen, die in der obigen Antwort wahrscheinlich beschönigt wurden.

Um die Namen der Tabellen zu erhalten, die die Spalte Foo enthalten , können wir eine Abfrage von information_schema.columns ausführen Tisch. (Das ist eine der Tabellen, die in MySQL information_schema bereitgestellt werden Datenbank.)

Da wir möglicherweise Tabellen in mehreren Datenbanken haben, reicht der Tabellenname nicht aus, um eine Tabelle zu identifizieren; wir müssen wissen, in welcher Datenbank sich die Tabelle befindet. Anstatt mit einem „use db "-Anweisung, bevor wir ein UPDATE ausführen , können wir einfach auf die Tabelle UPDATE db.mytable SET Foo... verweisen .

Wir können unsere Abfrage von information_schema.columns verwenden um fortzufahren und die Teile, die wir für eine UPDATE-Anweisung erstellen müssen, aneinanderzureihen (zu verketten), und SELECT die tatsächlichen Anweisungen zurückgeben zu lassen, die wir ausführen müssten, um die Spalte Foo zu aktualisieren , im Grunde dies:

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

Aber wir wollen die Werte aus table_schema ersetzen und table_name anstelle von mydatabase und mytable . Wenn wir dieses SELECT ausführen

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

Das gibt uns eine einzelne Zeile zurück, die eine einzelne Spalte enthält (die Spalte heißt zufällig sql , aber der Name der Spalte ist für uns nicht wichtig). Der Wert der Spalte ist nur ein String. Aber der String, den wir zurückbekommen, ist (hoffentlich) eine SQL-Anweisung, die wir ausführen könnten.

Wir würden dasselbe bekommen, wenn wir diese Zeichenfolge in Stücke zerlegen und CONCAT verwenden, um sie für uns wieder zusammenzufügen, z. B.

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

Wir können diese Abfrage als Modell für die Anweisung verwenden, die wir gegen information_schema.columns ausführen möchten . Wir ersetzen 'mydatabase' und 'mytable' mit Verweisen auf Spalten aus information_schema.columns Tabelle, die uns die Datenbank und den Tabellennamen geben.

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

Es gibt einige Datenbanken, die wir definitiv nicht verwenden aktualisieren möchten ... mysql , information_schema , leistungsschema . Wir müssen entweder die Datenbanken, die die zu aktualisierende Tabelle enthalten, auf die Whitelist setzen

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

-oder - Wir müssen die Datenbanken, die wir definitiv nicht aktualisieren möchten, auf die schwarze Liste setzen

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

Wir können diese Abfrage ausführen (wir könnten einen ORDER BY hinzufügen wenn wir möchten, dass die Zeilen in einer bestimmten Reihenfolge zurückgegeben werden) und was wir zurückbekommen, ist eine Liste mit den Anweisungen, die wir ausführen möchten. Wenn wir diesen Satz von Zeichenfolgen als einfache Textdatei speichern (ohne Kopfzeile und zusätzliche Formatierung) und am Ende jeder Zeile ein Semikolon hinzufügen, hätten wir eine Datei, die wir aus mysql> Befehlszeilen-Client.

(Wenn einer der obigen Punkte verwirrend ist, lassen Sie es mich wissen.)

Der nächste Teil ist etwas komplizierter. Der Rest befasst sich mit einer Alternative zum Speichern der Ausgabe von SELECT als reine Textdatei und zum Ausführen der Anweisungen von mysql Befehlszeilen-Client.

MySQL bietet eine Einrichtung/Funktion, die es uns erlaubt, im Grunde jede auszuführen Zeichenfolge als SQL-Anweisung im Kontext eines gespeicherten MySQL-Programms (z. B. einer gespeicherten Prozedur). Die Funktion, die wir verwenden werden, heißt dynamisches SQL .

So verwenden Sie dynamisches SQL verwenden wir die Anweisungen PREPARE , AUSFÜHREN und DEALLOCATE PREPARE . (Das Aufheben der Zuordnung ist nicht unbedingt erforderlich, MySQL wird für uns aufräumen, wenn wir es nicht verwenden, aber ich denke, es ist trotzdem eine gute Praxis, es zu tun.)

Wieder dynamisches SQL ist NUR verfügbar im Kontext eines in MySQL gespeicherten Programms. Dazu benötigen wir einen String, der die SQL-Anweisung enthält, die wir ausführen möchten. Nehmen wir als einfaches Beispiel Folgendes an:

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

Um den Inhalt von str zu erhalten evaluiert und als SQL-Anweisung ausgeführt, die Grundzüge sind:

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

Der nächste komplizierte Teil besteht darin, dies mit der Abfrage zusammenzusetzen, die wir ausführen, um den Zeichenfolgenwert zu erhalten, den wir als SQL-Anweisungen ausführen möchten. Dazu bauen wir eine Cursor-Schleife zusammen. Die Grundstruktur dafür ist unsere SELECT-Anweisung:

SELECT bah FROM humbug

Und verwandeln Sie das in eine Cursor-Definition:

DECLARE mycursor FOR SELECT bah FROM humbug ;

Wir wollen das ausführen und die Zeilen durchlaufen, die es zurückgibt. Um die Anweisung auszuführen und eine Ergebnismenge vorzubereiten, "öffnen" wir den Cursor

OPEN mycursor; 

Wenn wir damit fertig sind, geben wir ein "Schließen" aus, um die Ergebnismenge freizugeben, damit der MySQL-Server weiß, dass wir sie nicht mehr benötigen, und die dafür zugewiesenen Ressourcen bereinigen und freigeben kann.

CLOSE mycursor;

Aber bevor wir den Cursor schließen, wollen wir die Ergebnismenge "schleifen", jede Zeile abrufen und etwas mit der Zeile tun. Die Anweisung, die wir verwenden, um die nächste Zeile aus der Ergebnismenge in eine Prozedurvariable zu bekommen, lautet:

FETCH mycursor INTO some_variable;

Bevor wir Zeilen in Variablen holen können, müssen wir die Variablen definieren, z. B.

DECLARE some_variable VARCHAR(2000); 

Da unser Cursor (SELECT-Anweisung) nur eine einzige Spalte zurückgibt, benötigen wir nur eine Variable. Hätten wir mehr Spalten, bräuchten wir für jede Spalte eine Variable.

Schließlich haben wir die letzte Zeile aus der Ergebnismenge abgerufen. Wenn wir versuchen, das nächste abzurufen, wird MySQL einen Fehler ausgeben.

Andere Programmiersprachen würden uns einfach while machen lassen Schleife, und lassen Sie uns die Zeilen abrufen und die Schleife verlassen, wenn wir sie alle verarbeitet haben. MySQL ist geheimnisvoller. Um eine Schleife zu machen:

mylabel: LOOP
  -- do something
END LOOP mylabel;

Das allein ergibt eine sehr feine Endlosschleife, weil diese Schleife keinen "Ausgang" hat. Glücklicherweise gibt uns MySQL den LEAVE -Anweisung als Möglichkeit, eine Schleife zu verlassen. Normalerweise möchten wir die Schleife nicht verlassen, wenn wir sie das erste Mal betreten, daher gibt es normalerweise einen bedingten Test, den wir verwenden, um festzustellen, ob wir fertig sind und die Schleife verlassen sollten oder ob wir nicht fertig sind und herumgehen sollten die Schleife wieder.

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

In unserem Fall möchten wir alle Zeilen in der Ergebnismenge durchlaufen, also setzen wir einen FETCH ein a die erste Anweisung innerhalb der Schleife (das Nützliche, was wir tun wollen).

Um eine Verbindung zwischen dem Fehler, den MySQL auslöst, wenn wir versuchen, über die letzte Zeile in der Ergebnismenge hinaus abzurufen, und dem bedingten Test herzustellen, müssen wir bestimmen, ob wir gehen sollen...

MySQL bietet uns eine Möglichkeit, einen CONTINUE HANDLER zu definieren (irgendeine Anweisung, die wir ausführen wollen), wenn der Fehler geworfen wird...

 DECLARE CONTINUE HANDLER FOR NOT FOUND 

Die Aktion, die wir ausführen möchten, besteht darin, eine Variable auf TRUE zu setzen.

 SET done = TRUE;

Bevor wir SET ausführen können, müssen wir die Variable definieren:

 DECLARE done TINYINT(1) DEFAULT FALSE;

Damit können wir unsere LOOP ändern, um zu testen, ob der done Variable als Austrittsbedingung auf TRUE gesetzt, unsere Schleife sieht also etwa so aus:

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

Das "tu etwas mit der Zeile" ist, wo wir den Inhalt von some_variable nehmen wollen und etwas Sinnvolles damit machen. Unser Cursor gibt uns eine Zeichenfolge zurück, die wir als SQL-Anweisung ausführen möchten. Und MySQL gibt uns das dynamische SQL Funktion, die wir dafür verwenden können.

HINWEIS:MySQL hat Regeln über die Reihenfolge der Anweisungen in der Prozedur. Zum Beispiel DECLARE Aussage muss am Anfang stehen. Und ich denke, der CONTINUE HANDLER muss als letztes deklariert werden.

Nochmals:Der Cursor und dynamisches SQL Funktionen sind NUR verfügbar im Kontext eines gespeicherten MySQL-Programms, z. B. einer gespeicherten Prozedur. Das Beispiel, das ich oben gegeben habe, war nur das Beispiel des Körpers eines Verfahrens.

Um dies als gespeicherte Prozedur zu erstellen, müsste es als Teil von etwas wie diesem eingebunden werden:

DELIMITER $$

DROP PROCEDURE IF EXISTS myproc $$

CREATE PROCEDURE myproc 
NOT DETERMINISTIC
MODIFIES SQL DATA
BEGIN

   -- procedure body goes here

END$$

DELIMITER ;

Hoffentlich erklärt das das Beispiel, das ich gegeben habe, etwas ausführlicher.