sql >> Database >  >> RDS >> Mysql

Hoe alle tabellen met kolomnaam te selecteren en die kolom bij te werken?

Nee, niet in een enkele verklaring.

Om de namen te krijgen van alle tabellen die een kolom bevatten met de naam Foo :

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

Dan hebt u voor elke tabel een UPDATE-instructie nodig. (Het is mogelijk om meerdere tabellen in een enkele instructie bij te werken, maar dat zou een (onnodige) cross join moeten zijn.) Het is beter om elke tabel afzonderlijk te doen.

U kunt dynamische SQL gebruiken om de UPDATE-instructies in een MySQL-programma uit te voeren (bijv. PROCEDURE)

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

Als u een cursor declareert voor de select uit information_schema.tables, kunt u een cursorlus gebruiken om een ​​dynamische UPDATE te verwerken statement voor elke geretourneerde table_name.

  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;

(Dit is slechts een ruwe schets van een voorbeeld, geen syntaxis gecontroleerd of getest.)

VERVOLG

Enkele korte opmerkingen over enkele ideeën die waarschijnlijk in het bovenstaande antwoord zijn verdoezeld.

Om de namen te krijgen van de tabellen met kolom Foo , kunnen we een query uitvoeren vanuit de information_schema.columns tafel. (Dat is een van de tabellen in het MySQL information_schema database.)

Omdat we tabellen in meerdere databases kunnen hebben, is de tabelnaam niet voldoende om een ​​tabel te identificeren; we moeten weten in welke database de tabel zich bevindt. In plaats van te mopperen met een "use db " voordat we een UPDATE uitvoeren , we kunnen gewoon verwijzen naar de tabel UPDATE db.mytable SET Foo... .

We kunnen onze zoekopdracht van information_schema.columns . gebruiken om door te gaan en de delen aan elkaar te rijgen (samenvoegen) die we moeten maken voor een UPDATE-instructie, en de SELECT de feitelijke instructies te laten retourneren die we zouden moeten uitvoeren om kolom Foo bij te werken , eigenlijk dit:

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

Maar we willen de waarden van table_schema . vervangen en table_name in plaats van mydatabase en mytable . Als we dit uitvoeren SELECT

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

Dat levert ons een enkele rij op, die een enkele kolom bevat (de kolom heet toevallig sql , maar de naam van de kolom is niet belangrijk voor ons). De waarde van de kolom is slechts een tekenreeks. Maar de string die we terugkrijgen is (hopelijk) een SQL-instructie die we zouden kunnen uitvoeren.

We zouden hetzelfde krijgen als we dat touw in stukken zouden breken en CONCAT zouden gebruiken om ze voor ons weer aan elkaar te rijgen, bijvoorbeeld

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

We kunnen die query gebruiken als een model voor de instructie die we willen uitvoeren tegen information_schema.columns . We vervangen 'mydatabase' en 'mytable' met verwijzingen naar kolommen uit de information_schema.columns tabel die ons de database en table_name geeft.

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

Er zijn enkele databases die we zeker niet doen wil updaten... mysql , information_schema , performance_schema . We moeten ofwel de databases op de witte lijst zetten die de tabel bevatten die we willen bijwerken

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

-of - we moeten de databases die we absoluut niet willen updaten op de zwarte lijst zetten

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

We kunnen die query uitvoeren (we zouden een ORDER BY kunnen toevoegen als we willen dat de rijen in een bepaalde volgorde worden geretourneerd) en wat we terugkrijgen is een lijst met de instructies die we willen uitvoeren. Als we die reeks tekenreeksen zouden opslaan als een tekstbestand zonder opmaak (exclusief koprij en extra opmaak), en een puntkomma aan het einde van elke regel zouden toevoegen, zouden we een bestand hebben dat we zouden kunnen uitvoeren vanuit de mysql> opdrachtregelclient.

(Als een van de bovenstaande dingen verwarrend is, laat het me dan weten.)

Het volgende deel is iets ingewikkelder. De rest hiervan behandelt een alternatief voor het opslaan van de uitvoer van de SELECT als een tekstbestand zonder opmaak en het uitvoeren van de instructies van de mysql opdrachtregelclient.

MySQL biedt een faciliteit/functie waarmee we in principe elke . kunnen uitvoeren tekenreeks als een SQL-instructie, in de context van een opgeslagen MySQL-programma (bijvoorbeeld een opgeslagen procedure. De functie die we gaan gebruiken heet dynamische SQL .

dynamische SQL gebruiken , gebruiken we de statements PREPARE , EXECUTE en DEALLOCATE PREPARE . (De deallocate is niet strikt noodzakelijk, MySQL zal voor ons opschonen als we het niet gebruiken, maar ik denk dat het een goede gewoonte is om het toch te doen.)

Nogmaals, dynamische SQL is ALLEEN beschikbaar in de context van een MySQL opgeslagen programma. Om dit te doen, hebben we een string nodig die de SQL-instructie bevat die we willen uitvoeren. Laten we als eenvoudig voorbeeld zeggen dat we dit hadden:

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

Om de inhoud van str . te krijgen geëvalueerd en uitgevoerd als een SQL-instructie, de basisstructuur is:

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

Het volgende gecompliceerde deel is om dat samen te voegen met de query die we uitvoeren om de tekenreekswaarde te krijgen die we willen uitvoeren als SQL-instructies. Om dat te doen, hebben we een cursorlus samengesteld. Het basisschema daarvoor is om onze SELECT-instructie te nemen:

SELECT bah FROM humbug

En verander dat in een cursordefinitie:

DECLARE mycursor FOR SELECT bah FROM humbug ;

Wat we willen is dat uitvoeren en door de rijen lopen die het retourneert. Om de instructie uit te voeren en een resultatenset voor te bereiden, "openen" we de cursor

OPEN mycursor; 

Als we hiermee klaar zijn, gaan we een "close" uitgeven om de resultatenset vrij te geven, zodat de MySQL-server weet dat we deze niet meer nodig hebben, en kan opschonen en de daaraan toegewezen middelen vrijmaken.

CLOSE mycursor;

Maar voordat we de cursor sluiten, willen we "doorlopen" door de resultatenset, elke rij ophalen en iets met de rij doen. Het statement dat we gebruiken om de volgende rij uit de resultatenset in een procedurevariabele te krijgen is:

FETCH mycursor INTO some_variable;

Voordat we rijen in variabelen kunnen ophalen, moeten we de variabelen definiëren, bijvoorbeeld

DECLARE some_variable VARCHAR(2000); 

Omdat onze cursor (SELECT-instructie) slechts één kolom retourneert, hebben we maar één variabele nodig. Als we meer kolommen hadden, hadden we voor elke kolom een ​​variabele nodig.

Uiteindelijk hebben we de laatste rij uit de resultatenset opgehaald. Wanneer we proberen de volgende op te halen, geeft MySQL een foutmelding.

Andere programmeertalen zouden ons gewoon een while laten doen lus, en laten we de rijen ophalen en de lus verlaten als we ze allemaal hebben verwerkt. MySQL is geheimzinniger. Een lus maken:

mylabel: LOOP
  -- do something
END LOOP mylabel;

Dat op zich zorgt voor een hele fijne oneindige lus, omdat die lus geen "uitgang" heeft. Gelukkig geeft MySQL ons de LEAVE statement als een manier om een ​​lus te verlaten. We willen de lus meestal niet verlaten als we hem voor het eerst betreden, dus er is meestal een voorwaardelijke test die we gebruiken om te bepalen of we klaar zijn, en de lus moeten verlaten, of dat we niet klaar zijn, en rond moeten gaan de lus weer.

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

In ons geval willen we alle rijen in de resultatenset doorlopen, dus we gaan een FETCH plaatsen a de eerste instructie binnen de lus (het iets nuttigs dat we willen doen).

Om een ​​koppeling te krijgen tussen de fout die MySQL genereert wanneer we proberen voorbij de laatste rij in de resultatenset te halen, en de voorwaardelijke test moeten we bepalen of we moeten vertrekken...

MySQL biedt ons een manier om een ​​CONTINUE HANDLER te definiëren (een verklaring die we willen uitvoeren) wanneer de fout wordt gegenereerd...

 DECLARE CONTINUE HANDLER FOR NOT FOUND 

De actie die we willen uitvoeren is om een ​​variabele in te stellen op TRUE.

 SET done = TRUE;

Voordat we de SET kunnen uitvoeren, moeten we de variabele definiëren:

 DECLARE done TINYINT(1) DEFAULT FALSE;

Daarmee kunnen we onze LOOP veranderen om te testen of de done variabele is ingesteld op TRUE, als de exit-voorwaarde, dus onze lus ziet er ongeveer zo uit:

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

De "doe iets met de rij" is waar we de inhoud van some_variable naartoe willen nemen en doe er iets nuttigs mee. Onze cursor geeft ons een string terug die we willen uitvoeren als een SQL-instructie. En MySQL geeft ons de dynamische SQL functie die we daarvoor kunnen gebruiken.

OPMERKING:MySQL heeft regels over de volgorde van de instructies in de procedure. Bijvoorbeeld de DECLARE verklaring moet aan het begin komen. En ik denk dat de CONTINUE HANDLER het laatste moet zijn dat wordt aangegeven.

Nogmaals:de cursor en dynamische SQL functies zijn ALLEEN beschikbaar in de context van een opgeslagen MySQL-programma, zoals een opgeslagen procedure. Het voorbeeld dat ik hierboven gaf was alleen het voorbeeld van de body van een procedure.

Om dit te creëren als een opgeslagen procedure, zou het moeten worden opgenomen als onderdeel van zoiets als dit:

DELIMITER $$

DROP PROCEDURE IF EXISTS myproc $$

CREATE PROCEDURE myproc 
NOT DETERMINISTIC
MODIFIES SQL DATA
BEGIN

   -- procedure body goes here

END$$

DELIMITER ;

Hopelijk verklaart dat het voorbeeld dat ik heb gegeven wat gedetailleerder.



  1. Python en Django OperationalError (2006, 'MySQL-server is verdwenen')

  2. Hoe transacties en vergrendelingen correct te gebruiken om de database-integriteit te waarborgen?

  3. Hoe id met max datumgroep per categorie selecteren in PostgreSQL?

  4. Mijn sql-query om waarden in de middelste rij te krijgen met de functie GROUP BY