Не, не в едно изявление.
За да получите имената на всички тези таблици, които съдържат колона с име Foo
:
SELECT table_schema, table_name
FROM information_schema.columns
WHERE column_name = 'Foo'
Тогава ще ви е необходим израз UPDATE за всяка таблица. (Възможно е да актуализирате множество таблици в един израз, но това ще трябва да бъде (ненужно) кръстосано свързване.) По-добре е всяка таблица да се прави отделно.
Можете да използвате динамичен SQL за изпълнение на операторите UPDATE в съхранена програма в MySQL (напр. PROCEDURE)
DECLARE sql VARCHAR(2000);
SET sql = 'UPDATE db.tbl SET Foo = 0';
PREPARE stmt FROM sql;
EXECUTE stmt;
DEALLOCATE stmt;
Ако декларирате курсор за избора от information_schema.tables, можете да използвате цикъл на курсора, за да обработите динамичен UPDATE
изявление за всяко върнато име на таблица.
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;
(Това е само грубо очертание на пример, не е проверен или тестван синтаксис.)
ПОСЛЕДВАНЕ
Някои кратки бележки за някои идеи, които вероятно са били премълчавани в отговора по-горе.
За да получите имената на таблиците, съдържащи колона Foo
, можем да изпълним заявка от information_schema.columns
маса. (Това е една от таблиците, предоставени в MySQL information_schema
база данни.)
Тъй като може да имаме таблици в множество бази данни, table_name не е достатъчно за идентифициране на таблица; трябва да знаем в коя база данни се намира таблицата. Вместо да се бъркаме с "use db
" преди да изпълним UPDATE
, можем просто да направим справка с таблицата UPDATE db.mytable SET Foo...
.
Можем да използваме нашата заявка за information_schema.columns
за да продължим и да наредим заедно (свържем) частите, които трябва да създадем за оператор UPDATE, и да накараме SELECT да върне действителните оператори, които ще трябва да изпълним, за да актуализираме колона Foo
, основно това:
UPDATE `mydatabase`.`mytable` SET `Foo` = 0
Но ние искаме да заменим стойностите от table_schema
и table_name
на мястото на mydatabase
и mytable
. Ако изпълним това SELECT
SELECT 'UPDATE `mydatabase`.`mytable` SET `Foo` = 0' AS sql
Това ни връща един ред, съдържащ една колона (колоната се нарича sql
, но името на колоната не е важно за нас). Стойността на колоната ще бъде просто низ. Но низът, който получаваме обратно, е (надяваме се) SQL оператор, който можем да изпълним.
Ще получим същото, ако счупим тази струна на парчета и използваме CONCAT, за да ги нанижем обратно заедно за нас, напр.
SELECT CONCAT('UPDATE `','mydatabase','`.`','mytable','` SET `Foo` = 0') AS sql
Можем да използваме тази заявка като модел за израза, който искаме да изпълним срещу information_schema.columns
. Ще заменим 'mydatabase'
и 'mytable'
с препратки към колони от information_schema.columns
таблица, която ни дава базата данни и 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'
Има някои бази данни, които определено не искате да актуализирате... mysql
, information_schema
, performance_schema
. Имаме нужда или от белия списък на базите данни, съдържащи таблицата, която искаме да актуализираме
AND c.table_schema IN ('mydatabase','anotherdatabase')
-или - трябва да поставим в черен списък базите данни, които определено не искаме да актуализираме
AND c.table_schema NOT IN ('mysql','information_schema','performance_schema')
Можем да изпълним тази заявка (можем да добавим ORDER BY
ако искаме редовете да бъдат върнати в определен ред) и това, което получаваме, е списък, съдържащ изразите, които искаме да изпълним. Ако запазим този набор от низове като обикновен текстов файл (с изключение на заглавния ред и допълнителното форматиране), като добавим точка и запетая в края на всеки ред, ще имаме файл, който можем да изпълним от mysql>
клиент на командния ред.
(Ако някое от горните е объркващо, уведомете ме.)
Следващата част е малко по-сложна. Останалата част от това се занимава с алтернатива на запазване на изхода от SELECT като обикновен текстов файл и изпълнение на операторите от mysql
клиент на командния ред.
MySQL предоставя средство/функция, която ни позволява да изпълняваме основно всякак низ като SQL израз, в контекста на съхранена програма в MySQL (например съхранена процедура. Функцията, която ще използваме, се нарича динамичен SQL .
За да използвате динамичен SQL , ние използваме изразите PREPARE
, EXECUTE
и DEALLOCATE PREPARE
. (Освобождаването не е строго необходимо, MySQL ще почисти вместо нас, ако не го използваме, но мисля, че все пак е добра практика да го направим.)
Отново динамичен SQL е наличен САМО в контекста на MySQL съхранена програма. За да направим това, трябва да имаме низ, съдържащ SQL израза, който искаме да изпълним. Като прост пример, да кажем, че имаме това:
DECLARE str VARCHAR(2000);
SET str = 'UPDATE mytable SET mycol = 0 WHERE mycol < 0';
За да получите съдържанието на str
оценено и изпълнено като SQL оператор, основният контур е:
PREPARE stmt FROM str;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Следващата сложна част е да поставим това заедно със заявката, която изпълняваме, за да получим стойност на низ, която искаме да изпълним като SQL изрази. За да направим това, съставяме цикъл на курсора. Основният план за това е да вземем нашия оператор SELECT:
SELECT bah FROM humbug
И го превърнете в дефиниция на курсора:
DECLARE mycursor FOR SELECT bah FROM humbug ;
Това, което искаме, е да изпълним това и да преминем през редовете, които връща. За да изпълним оператора и да подготвим набор от резултати, ние "отваряме" курсора
OPEN mycursor;
Когато приключим с него, ще издадем "затваряне", за да освободим набора от резултати, така че MySQL сървърът да знае, че вече не се нуждаем от него, и може да почисти и освободи ресурсите, разпределени за това.
CLOSE mycursor;
Но преди да затворим курсора, искаме да „превъртим“ през набора от резултати, да извлечем всеки ред и да направим нещо с реда. Изявлението, което използваме, за да получим следващия ред от набора от резултати в променлива на процедурата, е:
FETCH mycursor INTO some_variable;
Преди да можем да извлечем редове в променливи, трябва да дефинираме променливите, напр.
DECLARE some_variable VARCHAR(2000);
Тъй като нашият курсор (извлечение SELECT) връща само една колона, имаме нужда само от една променлива. Ако имахме повече колони, ще ни трябва променлива за всяка колона.
В крайна сметка ще извлечем последния ред от набора от резултати. Когато се опитаме да извлечем следващия, MySQL ще изведе грешка.
Други езици за програмиране биха ни позволили просто да направим while
цикъл и нека да извлечем редовете и да излезем от цикъла, когато ги обработим всички. MySQL е по-таен. За да направите цикъл:
mylabel: LOOP
-- do something
END LOOP mylabel;
Това само по себе си създава много фин безкраен цикъл, защото този цикъл няма "изход". За щастие MySQL ни дава LEAVE
израз като начин за излизане от цикъл. Обикновено не искаме да излизаме от цикъла първия път, когато влезем в него, така че обикновено има някакъв условен тест, който използваме, за да определим дали сме готови и дали трябва да излезем от цикъла, или не сме готови и трябва да заобиколим цикълът отново.
mylabel: LOOP
-- do something useful
IF some_condition THEN
LEAVE mylabel;
END IF;
END LOOP mylabel;
В нашия случай искаме да преминем през всички редове в набора от резултати, така че ще поставим FETCH
a първото изявление вътре в цикъла (нещо полезно, което искаме да направим).
За да получим връзка между грешката, която MySQL хвърля, когато се опитваме да извлечем покрай последния ред в набора от резултати, и условния тест, трябва да определим дали трябва да напуснем...
MySQL ни предоставя начин да дефинираме CONTINUE HANDLER
(някое изявление, което искаме да бъде изпълнено), когато се изведе грешката...
DECLARE CONTINUE HANDLER FOR NOT FOUND
Действието, което искаме да извършим, е да зададем променлива на TRUE.
SET done = TRUE;
Преди да можем да стартираме SET, трябва да дефинираме променливата:
DECLARE done TINYINT(1) DEFAULT FALSE;
С това можем да променим нашия LOOP, за да проверим дали done
променливата е зададена на TRUE, като условие за излизане, така че нашият цикъл изглежда така:
mylabel: LOOP
FETCH mycursor INTO some_variable;
IF done THEN
LEAVE mylabel;
END IF;
-- do something with the row
END LOOP mylabel;
"Направете нещо с реда" е мястото, където искаме да вземем съдържанието на some_variable
и направи нещо полезно с него. Нашият курсор ни връща низ, който искаме да изпълним като SQL оператор. И MySQL ни дава динамичния SQL функция, която можем да използваме за това.
ЗАБЕЛЕЖКА:MySQL има правила за реда на изразите в процедурата. Например DECLARE
изявление трябва да дойде в началото. И мисля, че манипулаторът CONTINUE HANDLER трябва да бъде последното декларирано нещо.
Отново:Курсорът и динамичен SQL функциите са налични САМО в контекста на MySQL съхранена програма, като например съхранена процедура. Примерът, който дадох по-горе, беше само примерът за тялото на процедура.
За да създадете това като съхранена процедура, ще трябва да бъде включено като част от нещо подобно:
DELIMITER $$
DROP PROCEDURE IF EXISTS myproc $$
CREATE PROCEDURE myproc
NOT DETERMINISTIC
MODIFIES SQL DATA
BEGIN
-- procedure body goes here
END$$
DELIMITER ;
Надяваме се, че това обяснява примера, който дадох малко по-подробно.