Ne, ani v jediném prohlášení.
Chcete-li získat názvy všech tabulek, které obsahují sloupec s názvem Foo
:
SELECT table_schema, table_name
FROM information_schema.columns
WHERE column_name = 'Foo'
Pak byste pro každou tabulku potřebovali příkaz UPDATE. (Je možné provést aktualizaci více tabulek v jednom příkazu, ale to by muselo být (zbytečné) křížové spojení.) Je lepší provádět každou tabulku zvlášť.
Dynamické SQL můžete použít k provádění příkazů UPDATE v programu uloženém v MySQL (např. PROCEDURE)
DECLARE sql VARCHAR(2000);
SET sql = 'UPDATE db.tbl SET Foo = 0';
PREPARE stmt FROM sql;
EXECUTE stmt;
DEALLOCATE stmt;
Pokud deklarujete kurzor pro výběr z information_schema.tables, můžete použít kurzorovou smyčku ke zpracování dynamického UPDATE
pro každý vrácený název_tabulky.
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;
(Toto je pouze hrubý nástin příkladu, syntaxe nebyla ověřena ani testována.)
NÁSLEDOVAT
Několik krátkých poznámek k některým nápadům, které byly pravděpodobně přehlédnuty v odpovědi výše.
Chcete-li získat názvy tabulek obsahujících sloupec Foo
, můžeme spustit dotaz z information_schema.columns
stůl. (To je jedna z tabulek poskytnutých v MySQL information_schema
databáze.)
Protože můžeme mít tabulky ve více databázích, název_tabulky nestačí k identifikaci tabulky; potřebujeme vědět, v jaké databázi je tabulka. Spíše než šmejdit s "use db
", než spustíme UPDATE
, můžeme pouze odkazovat na tabulku UPDATE db.mytable SET Foo...
.
Můžeme použít náš dotaz information_schema.columns
abychom mohli pokračovat a navázali dohromady (zřetězení) části, které potřebujeme vytvořit pro příkaz UPDATE, a aby SELECT vrátil skutečné příkazy, které bychom museli spustit, abychom aktualizovali sloupec Foo
, v podstatě toto:
UPDATE `mydatabase`.`mytable` SET `Foo` = 0
Ale chceme dosadit hodnoty z table_schema
a table_name
místo mydatabase
a mytable
. Pokud spustíme tento SELECT
SELECT 'UPDATE `mydatabase`.`mytable` SET `Foo` = 0' AS sql
To nám vrátí jeden řádek obsahující jeden sloupec (sloupec se náhodou jmenuje sql
, ale název sloupce pro nás není důležitý). Hodnota sloupce bude pouze řetězec. Ale řetězec, který dostaneme zpět, je (doufáme) SQL příkaz, který bychom mohli spustit.
Totéž bychom dostali, kdybychom ten provázek rozlámali na kousky a použili CONCAT, abychom je spojili, např.
SELECT CONCAT('UPDATE `','mydatabase','`.`','mytable','` SET `Foo` = 0') AS sql
Tento dotaz můžeme použít jako model pro příkaz, který chceme spustit proti information_schema.columns
. Nahradíme 'mydatabase'
a 'mytable'
s odkazy na sloupce z information_schema.columns
tabulku, která nám poskytne databázi a název_tabulky.
SELECT CONCAT('UPDATE `',c.table_schema,'`.`',c.table_name,'` SET `Foo` = 0') AS sql
FROM information_schema.columns
WHERE c.column_name = 'Foo'
Některé databáze rozhodně ne děláme chcete aktualizovat... mysql
, information_schema
, performance_schema
. Buď potřebujeme na seznam povolených databází obsahujících tabulku, kterou chceme aktualizovat
AND c.table_schema IN ('mydatabase','anotherdatabase')
-nebo - musíme zakázat databáze, které rozhodně nechceme aktualizovat
AND c.table_schema NOT IN ('mysql','information_schema','performance_schema')
Můžeme spustit tento dotaz (mohli bychom přidat ORDER BY
pokud chceme, aby se řádky vrátily v určitém pořadí) a dostaneme zpět seznam obsahující příkazy, které chceme spustit. Pokud bychom tuto sadu řetězců uložili jako prostý textový soubor (bez řádku záhlaví a zvláštního formátování) a přidali středník na konec každého řádku, měli bychom soubor, který bychom mohli spustit z mysql>
klient příkazového řádku.
(Pokud je něco z výše uvedeného matoucí, dejte mi vědět.)
Další část je trochu složitější. Zbytek se zabývá alternativou k uložení výstupu z SELECT jako prostý textový soubor a provádění příkazů z mysql
klient příkazového řádku.
MySQL poskytuje zařízení/funkci, která nám umožňuje spouštět v podstatě jakékoli řetězec jako příkaz SQL v kontextu uloženého programu MySQL (například uložené procedury. Funkce, kterou budeme používat, se nazývá dynamický SQL .
Chcete-li použít dynamické SQL , používáme příkazy PREPARE
, EXECUTE
a DEALLOCATE PREPARE
. (Deallocate není nezbytně nutné, MySQL za nás vyčistí, pokud jej nepoužíváme, ale myslím, že je dobré to udělat i tak.)
Opět dynamické SQL je k dispozici POUZE v kontextu programu uloženého v MySQL. K tomu potřebujeme mít řetězec obsahující SQL příkaz, který chceme provést. Jako jednoduchý příklad řekněme, že jsme měli toto:
DECLARE str VARCHAR(2000);
SET str = 'UPDATE mytable SET mycol = 0 WHERE mycol < 0';
Chcete-li získat obsah str
vyhodnocen a proveden jako SQL příkaz, základní osnova je:
PREPARE stmt FROM str;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Další komplikovanou částí je dát to dohromady s dotazem, který spouštíme, abychom získali hodnotu řetězce, kterou chceme provést jako příkazy SQL. K tomu jsme dali dohromady kurzorovou smyčku. Základní osnovou pro to je vzít náš příkaz SELECT:
SELECT bah FROM humbug
A přeměňte to na definici kurzoru:
DECLARE mycursor FOR SELECT bah FROM humbug ;
Chceme to provést a procházet řádky, které vrací. Chcete-li provést příkaz a připravit sadu výsledků, "otevřeme" kurzor
OPEN mycursor;
Když s tím skončíme, vydáme "uzavřít" a uvolníme sadu výsledků, aby server MySQL věděl, že už ji nepotřebujeme, a může vyčistit a uvolnit zdroje, které jsou k tomu přiděleny.
CLOSE mycursor;
Ale než zavřeme kurzor, chceme "procházet" sadou výsledků, načíst každý řádek a něco s řádkem udělat. Příkaz, který používáme k získání dalšího řádku ze sady výsledků do proměnné procedury, je:
FETCH mycursor INTO some_variable;
Než budeme moci načíst řádky do proměnných, musíme proměnné definovat, např.
DECLARE some_variable VARCHAR(2000);
Protože náš kurzor (příkaz SELECT) vrací pouze jeden sloupec, potřebujeme pouze jednu proměnnou. Pokud bychom měli více sloupců, potřebovali bychom pro každý sloupec proměnnou.
Nakonec získáme poslední řádek ze sady výsledků. Když se pokusíme načíst další, MySQL vyvolá chybu.
Jiné programovací jazyky by nám umožnily udělat while
smyčku a nechejte nás načíst řádky a opustit smyčku, až je všechny zpracujeme. MySQL je tajemnější. Chcete-li udělat smyčku:
mylabel: LOOP
-- do something
END LOOP mylabel;
To samo o sobě vytváří velmi jemnou nekonečnou smyčku, protože tato smyčka nemá "exit". Naštěstí nám MySQL dává možnost LEAVE
příkaz jako způsob, jak opustit smyčku. Obvykle nechceme opustit smyčku, když do ní poprvé vstoupíme, takže obvykle existuje nějaký podmíněný test, který používáme k určení, zda jsme skončili a měli bychom smyčku opustit, nebo jsme neskončili a měli bychom ji obejít. smyčka znovu.
mylabel: LOOP
-- do something useful
IF some_condition THEN
LEAVE mylabel;
END IF;
END LOOP mylabel;
V našem případě chceme procházet všechny řádky v sadě výsledků, takže vložíme FETCH
a první příkaz uvnitř smyčky (něco užitečného, co chceme udělat).
Abychom získali spojení mezi chybou, kterou MySQL vyvolá, když se pokoušíme načíst za poslední řádek v sadě výsledků, a podmíněným testem, musíme určit, zda bychom měli odejít...
MySQL nám poskytuje způsob, jak definovat CONTINUE HANDLER
(nějaký příkaz, který chceme provést), když je vyvolána chyba...
DECLARE CONTINUE HANDLER FOR NOT FOUND
Akce, kterou chceme provést, je nastavit proměnnou na hodnotu TRUE.
SET done = TRUE;
Než budeme moci spustit SET, musíme definovat proměnnou:
DECLARE done TINYINT(1) DEFAULT FALSE;
Díky tomu můžeme změnit naši LOOP, abychom otestovali, zda je done
proměnná je nastavena na hodnotu TRUE jako výstupní podmínka, takže naše smyčka vypadá asi takto:
mylabel: LOOP
FETCH mycursor INTO some_variable;
IF done THEN
LEAVE mylabel;
END IF;
-- do something with the row
END LOOP mylabel;
"Udělejte něco s řádkem" je místo, kde chceme převzít obsah some_variable
a udělat s tím něco užitečného. Náš kurzor nám vrací řetězec, který chceme provést jako příkaz SQL. A MySQL nám poskytuje dynamické SQL funkce, kterou k tomu můžeme použít.
POZNÁMKA:MySQL má pravidla pro pořadí příkazů v proceduře. Například DECLARE
prohlášení musí přijít na začátku. A myslím, že POKRAČOVACÍ HANDLER musí být to poslední, co bylo deklarováno.
Znovu:kurzor a dynamické SQL funkce jsou k dispozici POUZE v kontextu uloženého programu MySQL, jako je uložená procedura. Příklad, který jsem uvedl výše, byl pouze příkladem těla postupu.
Aby se to vytvořilo jako uložená procedura, muselo by to být začleněno jako součást něčeho takového:
DELIMITER $$
DROP PROCEDURE IF EXISTS myproc $$
CREATE PROCEDURE myproc
NOT DETERMINISTIC
MODIFIES SQL DATA
BEGIN
-- procedure body goes here
END$$
DELIMITER ;
Doufejme, že to vysvětluje příklad, který jsem uvedl, trochu podrobněji.