web-dev-qa-db-ja.com

Postgresで複数のテーブルを一度に更新するにはどうすればよいですか?

まったく同じ構造のテーブルがいくつかあり、すべてのテーブルの値を更新する必要があります。

そのために、次のスクリプトを作成してみました。

DO
$do$
DECLARE
  i pg_tables%rowtype;
BEGIN
FOR i IN SELECT * FROM pg_catalog.pg_tables where schemaname like 'public' and tablename like '%_knex_migrations'
LOOP
    UPDATE i.tablename SET name = replace(name, '.js', '.ts');
END LOOP;
END
$do$;

i.tablenameは正しい値(チェックするためにtmpテーブルに挿入)を持っていますが、更新は失敗します。

name: error
length: 223
severity: ERROR
code: 42P01
internalPosition: 8
internalQuery: UPDATE i."tablename" SET name = replace(name, '.js', '.ts')
where: PL/pgSQL function inline_code_block line 7 at SQL statement
file: parse_relation.c
line: 965
routine: parserOpenTable

ただプラグインi.tablenameUPDATEステートメントが機能しません。

それを機能させる方法はありますか?または、すべてのテーブルを一度に更新する簡単な方法は?

3
BrunoLM

あなたは実際に近いです。最初にいくつかのテストデータを作成します。

_CREATE TABLE foo_knex_migrations ( name )
  AS VALUES ('test.js'),('test2.js'),('bicycles');
CREATE TABLE bar_knex_migrations AS TABLE foo_knex_migrations;
CREATE TABLE baz_knex_migrations AS TABLE foo_knex_migrations;
_

次に、_EXECUTE..._と共に_%I_ FORMAT() を使用します。

_DO
$do$
  DECLARE
    i pg_tables%rowtype;
  BEGIN
  FOR i IN SELECT * FROM pg_catalog.pg_tables where schemaname like 'public' and tablename like '%_knex_migrations'
  LOOP
    EXECUTE FORMAT(
      $$
        UPDATE %I
        SET name = replace(name, '.js', '.ts');
      $$,
      i.tablename
    );
  END LOOP;
  END
$do$;


TABLE baz_knex_migrations ;
   name   
----------
 test.ts
 test2.ts
 bicycles
(3 rows)

test=# TABLE foo_knex_migrations ;
   name   
----------
 test.ts
 test2.ts
 bicycles
(3 rows)
_

補足として、

  1. 一般に、このような単純なもののために標準化されている_information_schema_を使用する必要があり、速度は重要ではありません。
  2. UPDATE句を追加して、WHEREを実行する必要があるかどうかを確認する必要があります。それ以外の場合は、テーブルを無料で書き換えます。
  3. SQLでは、このような命名規則は使用しません。その必要はありません。 _knex_migrations_に複数のテーブルがある場合は、すべてのテーブルの命名規則に基づいてカタログを検索するのではなく、_CREATE SCHEMA knex_migrations_にテーブルを格納することを検討してください。
3
Evan Carroll

基本的に機能するソリューションをコメントとして投稿しました。

ただし、弱点は残ります。UPDATEのテーブル名をスキーマで修飾せず、スキーマが現在の検索パスの最初にない場合、間違ったテーブルが更新される可能性があります。代わりに検討してください:

DO
$do$
DECLARE
   sch text := 'public';  -- your schema here
   tbl text;
BEGIN
   FOR tbl IN
      SELECT tablename FROM pg_catalog.pg_tables
      WHERE  schemaname = sch
      AND    tablename LIKE '%_knex_migrations'
   LOOP
      EXECUTE format($$UPDATE %I.%I SET name = replace(name, '.js', '.ts')$$, sch, tbl); 
   END LOOP;
END
$do$;

関連:

1