sql >> Database teknologi >  >> RDS >> Mysql

Sådan vælger du alle tabeller med kolonnenavn og opdaterer den kolonne

Nej, ikke i et enkelt udsagn.

For at få navnene på alle de tabeller, der indeholder kolonne med navnet Foo :

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

Derefter skal du bruge en UPDATE-erklæring for hver tabel. (Det er muligt at opdatere flere tabeller i en enkelt sætning, men det skal være en (unødvendig) krydsforbindelse.) Det er bedre at lave hver tabel separat.

Du kan bruge dynamisk SQL til at udføre UPDATE-sætningerne i et MySQL-lagret program (f.eks. PROCEDURE)

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

Hvis du erklærer en markør for select from information_schema.tables, kan du bruge en markørløkke til at behandle en dynamisk UPDATE sætning for hver returneret tabelnavn.

  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;
 

(Dette er kun en grov oversigt over et eksempel, ikke syntaks kontrolleret eller testet.)

OPFØLGNING

Nogle korte bemærkninger om nogle ideer, der sandsynligvis blev forsvundet i svaret ovenfor.

For at få navnene på de tabeller, der indeholder kolonnen Foo , kan vi køre en forespørgsel fra information_schema.columns bord. (Det er en af ​​tabellerne i MySQL information_schema database.)

Fordi vi kan have tabeller i flere databaser, er tabelnavnet ikke tilstrækkeligt til at identificere en tabel; vi har brug for at vide, hvilken database tabellen er i. I stedet for at mucke med en "use db " sætning, før vi kører en UPDATE , vi kan bare henvise til tabellen UPDATE db.mytable SET Foo... .

Vi kan bruge vores forespørgsel om information_schema.columns at gå videre og sammensætte (sammenkæde) de dele, vi skal oprette til en UPDATE-sætning, og få SELECT til at returnere de faktiske sætninger, vi skal køre for at opdatere kolonne Foo , dybest set dette:

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

Men vi ønsker at erstatte værdierne fra table_schema og table_name i stedet for mydatabase og mytable . Hvis vi kører denne SELECT

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

Det returnerer os en enkelt række, der indeholder en enkelt kolonne (kolonnen hed tilfældigvis sql , men navnet på kolonnen er ikke vigtigt for os). Værdien af ​​kolonnen vil kun være en streng. Men den streng, vi får tilbage, er tilfældigvis (håber vi) en SQL-sætning, som vi kunne køre.

Vi ville få det samme, hvis vi brækkede den snor op i stykker og brugte CONCAT til at sætte dem sammen igen for os, f.eks.

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

Vi kan bruge den forespørgsel som en model for den sætning, vi ønsker at køre mod information_schema.columns . Vi erstatter 'mydatabase' og 'mytable' med referencer til kolonner fra information_schema.columns tabel, der giver os databasen og tabelnavn.

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

Der er nogle databaser, vi absolut ikke gør ønsker at opdatere... mysql , information_schema , performance_schema . Vi skal enten hvidliste de databaser, der indeholder den tabel, vi ønsker at opdatere

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

-eller - vi skal sortliste de databaser, vi absolut ikke ønsker at opdatere

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

Vi kan køre den forespørgsel (vi kunne tilføje en ORDER BY). hvis vi vil have rækkerne returneret i en bestemt rækkefølge), og hvad vi får tilbage er en liste, der indeholder de udsagn, vi ønsker at køre. Hvis vi gemte det sæt strenge som en almindelig tekstfil (ekskl. overskriftsrække og ekstra formatering), og tilføjede et semikolon i slutningen af ​​hver linje, ville vi have en fil, vi kunne udføre fra mysql> kommandolinjeklient.

(Hvis noget af ovenstående er forvirrende, så lad mig det vide.)

Den næste del er lidt mere kompliceret. Resten af ​​dette omhandler et alternativ til at gemme outputtet fra SELECT som en almindelig tekstfil og udføre sætningerne fra mysql kommandolinjeklient.

MySQL tilbyder en facilitet/funktion, der giver os mulighed for at udføre stort set hvilket som helst streng som en SQL-sætning i sammenhæng med et MySQL-lagret program (f.eks. en lagret procedure. Funktionen, vi skal bruge, hedder dynamisk SQL .

At bruge dynamisk SQL , bruger vi sætningerne PREPARE , EXECUTE og DEALLOCATE PREPARE . (Deallokeringen er ikke strengt nødvendig, MySQL vil rydde op for os, hvis vi ikke bruger det, men jeg synes, det er god praksis at gøre det alligevel.)

Igen, dynamisk SQL er KUN tilgængelig i sammenhæng med et MySQL-lagret program. For at gøre dette skal vi have en streng, der indeholder den SQL-sætning, vi ønsker at udføre. Som et simpelt eksempel, lad os sige, at vi havde dette:

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

For at få indholdet af str evalueret og udført som en SQL-sætning, er den grundlæggende disposition:

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

Den næste komplicerede del er at sætte, at sammen med den forespørgsel, vi kører for at få strengværdi, vi ønsker at udføre som SQL-sætninger. For at gøre det sammensætter vi en markørløkke. Den grundlæggende disposition for det er at tage vores SELECT-sætning:

SELECT bah FROM humbug
 

Og gør det til en markørdefinition:

DECLARE mycursor FOR SELECT bah FROM humbug ;
 

Det, vi vil, er at udføre det og gå gennem de rækker, det returnerer. For at udføre sætningen og forberede et resultatsæt, "åbner" vi markøren

OPEN mycursor; 
 

Når vi er færdige med det, vil vi udstede et "luk", for at frigive resultatsættet, så MySQL-serveren ved, at vi ikke har brug for det længere, og kan rydde op og frigøre de ressourcer, der er allokeret til det.

CLOSE mycursor;
 

Men før vi lukker markøren, vil vi "løkke" gennem resultatsættet, hente hver række og gøre noget med rækken. Udsagnet vi bruger til at få den næste række fra resultatsættet til en procedurevariabel er:

FETCH mycursor INTO some_variable;
 

Før vi kan hente rækker ind i variabler, skal vi definere variablerne, f.eks.

DECLARE some_variable VARCHAR(2000); 
 

Da vores markør (SELECT-sætning) kun returnerer en enkelt kolonne, behøver vi kun én variabel. Hvis vi havde flere kolonner, ville vi have brug for en variabel for hver kolonne.

Til sidst har vi hentet den sidste række fra resultatsættet. Når vi forsøger at hente den næste, vil MySQL give en fejl.

Andre programmeringssprog ville lade os gøre et while løkke, og lad os hente rækkerne og forlade løkken, når vi har behandlet dem alle. MySQL er mere mystisk. Sådan laver du en loop:

mylabel: LOOP
  -- do something
END LOOP mylabel;
 

Det i sig selv giver en meget fin uendelig løkke, fordi den løkke ikke har en "udgang". Heldigvis giver MySQL os LEAVE statement som en måde at forlade et loop. Vi ønsker typisk ikke at forlade løkken første gang, vi går ind i den, så der er normalt en betinget test, vi bruger til at afgøre, om vi er færdige, og bør forlade løkken, eller vi er ikke færdige, og bør gå rundt løkken igen.

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

I vores tilfælde ønsker vi at gå gennem alle rækkerne i resultatsættet, så vi vil sætte en FETCH a den første sætning inde i løkken (det nyttigt, vi ønsker at gøre).

For at få en sammenhæng mellem den fejl, som MySQL kaster, når vi forsøger at hente forbi den sidste række i resultatsættet, og den betingede test, skal vi afgøre, om vi skal forlade...

MySQL giver os mulighed for at definere en CONTINUE HANDLER (noget udsagn, vi ønsker udført), når fejlen er smidt...

 DECLARE CONTINUE HANDLER FOR NOT FOUND 
 

Den handling, vi ønsker at udføre, er at sætte en variabel til TRUE.

 SET done = TRUE;
 

Før vi kan køre SET, skal vi definere variablen:

 DECLARE done TINYINT(1) DEFAULT FALSE;
 

Med det kan vi ændre vores LOOP for at teste om done variabel er sat til TRUE, som exit-betingelsen, så vores loop ser nogenlunde sådan ud:

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

"Gør noget med rækken" er, hvor vi ønsker at tage indholdet af some_variable og gør noget nyttigt med det. Vores markør returnerer os en streng, som vi ønsker at udføre som en SQL-sætning. Og MySQL giver os den dynamiske SQL funktion vi kan bruge til at gøre det.

BEMÆRK:MySQL har regler om rækkefølgen af ​​udsagn i proceduren. For eksempel DECLARE udtalelse skal komme i begyndelsen. Og jeg tror, ​​at CONTINUE HANDLER må være det sidste, der erklæres.

Igen:markøren og dynamisk SQL funktioner er KUN tilgængelige i sammenhæng med et MySQL-lagret program, såsom en lagret procedure. Eksemplet, jeg gav ovenfor, var kun eksemplet med body af en procedure.

For at få dette oprettet som en lagret procedure, skal det indarbejdes som en del af noget som dette:

DELIMITER $$

DROP PROCEDURE IF EXISTS myproc $$

CREATE PROCEDURE myproc 
NOT DETERMINISTIC
MODIFIES SQL DATA
BEGIN

   -- procedure body goes here

END$$

DELIMITER ;
 

Forhåbentlig forklarer det det eksempel, jeg gav lidt mere detaljeret.



  1. Behandling af en stor mængde data effektivt med MySQL og PHP

  2. Sådan opretter du en unik begrænsning på flere kolonner i SQL Server - SQL Server / TSQL vejledning del 96

  3. Hvordan indsætter jeg flere poster i én databasetur ved hjælp af PDO?

  4. MySQL bruger ikke indekser, når der forespørges over BIT-feltet ved hjælp af bitvise funktioner