web-dev-qa-db-ja.com

SQLを使用してpostgresテーブルを完全にコピーする

免責事項:この質問はスタックオーバーフローの質問 ここ に似ていますが、これらの答えはどれも私の問題には機能しません。後で説明します。

多くの列にインデックスが付けられているpostgresで、大きなテーブル(〜40M行、100 +列)をコピーしようとしています。現在、私は次のSQLを使用しています。

_CREATE TABLE <tablename>_copy (LIKE <tablename> INCLUDING ALL);
INSERT INTO <tablename>_copy SELECT * FROM <tablename>;
_

この方法には2つの問題があります。

  1. データを取り込む前にインデックスを追加するため、インデックスなしでテーブルを作成し、すべてのデータをコピーした後にインデックスを作成するよりもはるかに時間がかかります。
  2. これは、「シリアル」スタイルの列を適切にコピーしません。新しいテーブルに新しい「カウンター」を設定する代わりに、新しいテーブルの列のデフォルト値を過去のテーブルのカウンターに設定します。つまり、行が追加されても増加しません。

テーブルサイズにより、インデックス作成はリアルタイムの問題になります。また、ファイルにダンプしてから再取り込みすることもできません。また、コマンドラインの利点もありません。 SQLでこれを行う必要があります。

私がやりたいのは、いくつかの奇跡のコマンドで正確なコピーを直接作成するか、それが不可能な場合は、インデックスなしですべての制約付きでテーブルをコピーし、それらが「精神」の制約であることを確認することですSERIAL列の新しいカウンター)。次に、すべてのデータを_SELECT *_でコピーしてから、すべてのインデックスをコピーします。

ソース

  1. データベースコピーに関するスタックオーバーフローの質問 :これは、3つの理由で私が求めているものではありません

    • コマンドラインオプション_pg_dump -t x2 | sed 's/x2/x3/g' | psql_を使用し、この設定ではコマンドラインにアクセスできません
    • 遅いデータ取り込み前にインデックスを作成します
    • default nextval('x1_id_seq'::regclass)による証拠としてシリアル列を正しく更新しません
  2. postgresテーブルのシーケンス値をリセットする方法 :これは素晴らしいですが、残念ながら非常に手作業です。

64
Erik

残念ながら、このような作業を手動で行う必要があります。しかし、それはすべてpsqlのようなものから行うことができます。最初のコマンドは非常に簡単です:

select * into newtable from oldtable

これにより、インデックスではなくoldtableのデータでnewtableが作成されます。次に、独自にインデックスやシーケンスなどを作成する必要があります。次のコマンドを使用して、テーブルのすべてのインデックスのリストを取得できます。

select indexdef from pg_indexes where tablename='oldtable';

次に、psql -Eを実行してdbにアクセスし、\ dを使用して古いテーブルを調べます。次に、これらの2つのクエリをマングルして、シーケンスに関する情報を取得できます。

SELECT c.oid,
  n.nspname,
  c.relname
FROM pg_catalog.pg_class c
     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname ~ '^(oldtable)$'
  AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY 2, 3;

SELECT a.attname,
  pg_catalog.format_type(a.atttypid, a.atttypmod),
  (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)
   FROM pg_catalog.pg_attrdef d
   WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef),
  a.attnotnull, a.attnum
FROM pg_catalog.pg_attribute a
WHERE a.attrelid = '74359' AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum;

上記の74359を、前のクエリから取得したOIDに置き換えます。

57
Scott Marlowe

PostgreSQLの_create table as_機能が、OPが探していた答えになる可能性があります。

https://www.postgresql.org/docs/9.5/static/sql-createtableas.html

_create table my_table_copy as
  select * from my_table
_

これにより、データを含む同一のテーブルが作成されます。

_with no data_を追加すると、データなしでスキーマがコピーされます。

_create table my_table_copy as
  select * from my_table
with no data
_

これにより、すべてのデータを含むテーブルが作成されますが、インデックスやトリガーなどは含まれません。


create table my_table_copy (like my_table including all)

構文のようなテーブルの作成には、すべてのトリガー、インデックス、制約などが含まれますが、データは含まれません。

53
Phill

最も近い「奇跡のコマンド」は次のようなものです

pg_dump -t tablename | sed -r 's/\btablename\b/tablename_copy/' | psql -f -

特に、テーブルデータを読み込んだ後にインデックスを作成します。

ただし、シーケンスはリセットされません。自分でスクリプトを作成する必要があります。

14

テーブル構造とデータの両方を含むテーブルを完全にコピーするには、次のステートメントを使用します。

CREATE TABLE new_table AS 
TABLE existing_table;

データなしでテーブル構造をコピーするには、次のようにCREATE TABLEステートメントにWITH NO DATA句を追加します。

CREATE TABLE new_table AS 
TABLE existing_table 
WITH NO DATA;

既存のテーブルから部分的なデータを含むテーブルをコピーするには、次のステートメントを使用します。

CREATE TABLE new_table AS 
SELECT
*
FROM
    existing_table
WHERE
    condition;
6

警告:

ソーステーブルの名前を置き換えるためにpg_dumpとあらゆる種類の正規表現を使用するすべての回答は、本当に危険です。置換しようとしている部分文字列がデータに含まれている場合はどうなりますか?データを変更することになります!

2パスソリューションを提案します。

  1. いくつかのデータ固有の正規表現を使用して、ダンプからデータ行を削除します
  2. 残りの行で検索と置換を実行します

以下は、Rubyで書かれた例です。

Ruby -pe 'gsub(/(members?)/, "\\1_copy_20130320") unless $_ =~ /^\d+\t.*(?:t|f)$/' < members-production-20130320.sql > copy_members_table-20130320.sql

上記では、「members」テーブルを「members_copy_20130320」にコピーしようとしています。データ固有の正規表現は/^\d+\t.*(?:t|f)$/

上記のタイプのソリューションは、私にとっては有効です。買い手責任負担...

編集:

OK、正規表現を嫌う人のための擬似シェル構文の別の方法を次に示します。

  1. pg_dump -s -t mytable mydb> mytable_schema.sql
  2. mytable_schema.sqlでの検索と置換のテーブル名> mytable_copy_schema.sql
  3. psql -f mytable_copy_schema.sql mydb

  4. pg_dump -a -t mytable mydb> mytable_data.sql

  5. データセクションの前にあるいくつかのSQLステートメントの「mytable」を置き換えます
  6. psql -f mytable_data.sql mydb
2
Tomek

どうやらテーブルを「再構築」したいようです。テーブルを再構築するだけで、コピーしない場合は、代わりにCLUSTERを使用する必要があります。

SELECT count(*) FROM table; -- make a seq scan to make sure the table is at least
                            -- decently cached
CLUSTER someindex ON table;

インデックスを選択し、クエリに適したインデックスを選択してください。他のインデックスが適切でない場合は、常に主キーを使用できます。

テーブルが大きすぎてキャッシュできない場合、CLUSTERは遅くなります。

0
peufeu