いいえ、単一のステートメントではありません。
という名前の列を含むすべてのテーブルの名前を取得するにはFoo
:
SELECT table_schema, table_name
FROM information_schema.columns
WHERE column_name = 'Foo'
次に、テーブルごとに UPDATE ステートメントが必要になります。(単一のステートメントで複数のテーブルを更新することは可能ですが、それには (不要な) クロス結合が必要です。) 各テーブルを個別に実行することをお勧めします。
動的 SQL を使用して、MySQL ストアド プログラム (PROCEDURE など) で UPDATE ステートメントを実行できます。
DECLARE sql VARCHAR(2000);
SET sql = 'UPDATE db.tbl SET Foo = 0';
PREPARE stmt FROM sql;
EXECUTE stmt;
DEALLOCATE stmt;
select from information_schema.tables のカーソルを宣言すると、カーソル ループを使用して、UPDATE
返された各 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;
(これは例の大まかな概要であり、構文のチェックやテストは行われていません。)
ファローアップ
上記の回答でおそらく見過ごされたいくつかのアイデアについての簡単なメモ。
column を含むテーブルの名前を取得するには、テーブルFoo
からクエリを実行できinformation_schema.columns
ます。(これは、MySQL データベースで提供されるテーブルの 1 つです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
これにより、1 つの列を含む 1 つの行が返されます (列の名前はたまたまsql
ですが、列の名前は重要ではありません)。列の値は単なる文字列になります。しかし、返される文字列は、たまたま実行可能な SQL ステートメントであることが期待されます。
その文字列をバラバラに分解し、CONCAT を使用してそれらをつなぎ合わせた場合、同じ結果が得られます。たとえば、
SELECT CONCAT('UPDATE `','mydatabase','`.`','mytable','` SET `Foo` = 0') AS sql
そのクエリを、実行したいステートメントのモデルとして使用できますinformation_schema.columns
。'mydatabase'
andを、データベースと table_name を提供'mytable'
するテーブルの列への参照に置き換えます。information_schema.columns
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 は、MySQL ストアド プログラム (ストアド プロシージャなど) のコンテキストで、基本的に任意の文字列を SQL ステートメントとして実行できる機能/機能を提供します。使用する機能は、動的 SQLと呼ばれます。
動的 SQLを使用するには、ステートメント と を使用PREPARE
しEXECUTE
ますDEALLOCATE PREPARE
。(deallocate は厳密には必要ではありません。使用しない場合は 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;
処理が終了したら、"close" を発行して結果セットを解放します。これにより、MySQL サーバーはそれがもう必要ないことを認識し、割り当てられたリソースをクリーンアップして解放することができます。
CLOSE mycursor;
ただし、カーソルを閉じる前に、結果セットを「ループ」して各行をフェッチし、その行で何かを行いたいと考えています。結果セットからプロシージャ変数に次の行を取得するために使用するステートメントは次のとおりです。
FETCH mycursor INTO some_variable;
行を変数にフェッチする前に、変数を定義する必要があります。
DECLARE some_variable VARCHAR(2000);
カーソル (SELECT ステートメント) は 1 つの列のみを返すため、必要な変数は 1 つだけです。より多くの列がある場合は、各列に変数が必要になります。
最終的に、結果セットから最後の行を取得します。次のものを取得しようとすると、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
、最初のステートメントをループ内に配置します (実行したい便利なこと)。
結果セットの最後の行を超えてフェッチしようとしたときに MySQL がスローするエラーと条件付きテストとの間のリンクを取得するには、終了する必要があるかどうかを判断する必要があります...
CONTINUE HANDLER
MySQL は、エラーがスローされたときに (実行したいステートメント) を定義する方法を提供します...
DECLARE CONTINUE HANDLER FOR NOT FOUND
実行したいアクションは、変数を TRUE に設定することです。
SET done = TRUE;
SET を実行する前に、変数を定義する必要があります。
DECLARE done TINYINT(1) DEFAULT FALSE;
これで、変数が TRUE に設定されているかどうかを終了条件としてテストするように LOOP を変更できるdone
ため、ループは次のようになります。
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 ;
うまくいけば、これで私が挙げた例をもう少し詳しく説明できます。