2

列名 Foo を含むデータベース内のすべてのテーブルを検索し、その値を 0 に更新したいのですが、このようなことを考えていましたが、そのコードに UPDATE を配置する方法がわかりません。 MySQLデータベース内のイベントに関するこのステートメント、私はWAMPを使用しています。基本的には、手動で行う必要なく、すべての「Foo」列を0に設定するイベントを毎日実行することです

SELECT TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE column_name LIKE 'Foo'
4

2 に答える 2

5

いいえ、単一のステートメントではありません。

という名前の列を含むすべてのテーブルの名前を取得するには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を使用するには、ステートメント と を使用PREPAREEXECUTEますDEALLOCATE PREPARE。(deallocate は厳密には必要ではありません。使用しない場合は MySQL がクリーンアップしてくれますが、とにかく実行することをお勧めします。)

繰り返しになりますが、動的 SQLは、MySQL ストアド プログラムのコンテキストでのみ使用できます。これを行うには、実行したい SQL ステートメントを含む文字列が必要です。簡単な例として、これがあったとしましょう:

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

評価されて実行されたの内容をstrSQL ステートメントとして取得するための基本的な概要は次のとおりです。

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 HANDLERMySQL は、エラーがスローされたときに (実行したいステートメント) を定義する方法を提供します...

 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 ;

うまくいけば、これで私が挙げた例をもう少し詳しく説明できます。

于 2015-05-31T05:26:14.110 に答える