web-dev-qa-db-ja.com

インデックスをあるテーブルから別のテーブルにコピーする

CREATE TABLE table1_swap LIKE table1を使用してswapテーブルを作成する一連のETLジョブがあります。 table1_swapの入力を高速化するために、インデックスを含めません。しかし、読み込みが完了したら、新しく作成されたテーブルにそれらのインデックスを再適用する必要があります。これらのインデックスはこれらのETLジョブのスコープ外で作成されるため、必要がない場合はCREATE INDEX呼び出しをハードコーディングする必要はありません。

あるテーブルから別のテーブルに一連のインデックスを「転送」または「コピー」することは可能ですか?

6
Kyle Decot

Postgresシステムカタログから各インデックスの完全なDDLステートメントを取得できます。

次は、単一のテーブルのすべてのCREATEインデックスステートメントを取得します。

select pg_get_indexdef(idx.oid)||';'
from pg_index ind
  join pg_class idx on idx.oid = ind.indexrelid
  join pg_class tbl on tbl.oid = ind.indrelid
  left join pg_namespace ns on ns.oid = tbl.relnamespace
where tbl.relname = 'your_table_name'
   and ns.nspname = 'your_table_schema';

出力をスクリプトにスプールし、データをコピーしてテーブル名を交換した後で実行できます。

基本

すべてのインデックス定義を取得するには、システムカタログ情報関数 pg_get_indexdef(index_oid) のように使用できます @ a_horseはすでに提供されています のように。

ただし、regclassパラメータを使用すると、クエリをかなり簡単にすることができます。 tblスキーマのpublicというテーブルの場合:

_SELECT pg_get_indexdef(indexrelid) || ';' AS idx
FROM   pg_index
WHERE  indrelid = 'public.tbl'::regclass;  -- optionally schema-qualified
_

これには、allインデックスが含まれます:PK、部分的、機能的、一意、特別な演算子クラスなど。

regclassの詳細:

DDLステートメントを準備する

まだこれで作業できません。古いテーブル名を新しいものに置き換える必要があります-文字列の誤検出(テーブル名と一致する列名など)を置き換えたくない場合:

Postgresのデフォルトの命名規則を使用している場合、インデックス定義は次のようになります。

_CREATE INDEX tbl_tbl_id_idx ON tbl USING btree (tbl_id);

CREATE INDEX tbl_people_gin_idx ON tbl
USING gin (((data -> 'people'::text)) jsonb_path_ops);

CREATE INDEX tbl_comecol_nonull_idx ON tbl
USING btree (somecol) WHERE (cutblade IS NOT NULL);_

元のテーブル名をすべて太字にしました。置き換えたくないthree誤検知(列名またはインデックス名の一部)に注意してください。

このクエリは問題なく動作するはずですが、-verifyこれは実際のインデックスに対して自分で実行することです!

_SELECT regexp_replace(regexp_replace(
          pg_get_indexdef(indexrelid)
        , 'tbl', 'tbl1')
        , ' ON tbl ', ' ON tbl1 ')
    || ';' AS idx
FROM   pg_index
WHERE  indrelid = 'public.tbl'::regclass;
_

最初のreplace()は、名前の最初の一致のみを置き換えます(特に指示がない限り)。 2番目のregexp_preplace()はより具体的で、実際のテーブル名のみを置き換えます。

与えられた例では、次のようになります。

_CREATE INDEX tbl1_tbl_id_idx ON tbl1 USING btree (tbl_id);

CREATE INDEX tbl1_people_gin_idx ON tbl1
USING gin (((data -> 'people'::text)) jsonb_path_ops);

CREATE INDEX tbl1_somecol_nonull_idx ON tbl1
USING btree (somecol) WHERE (cutblade IS NOT NULL);_

ただし、非標準のテーブル名、スキーマ、または_search_path_設定についてはまだ考慮していません。これらはすべて以下の関数に組み込まれています。

完全自動化

命名方式に自信がある場合は、次のことができます完全に自動化

_CREATE OR REPLACE FUNCTION f_copy_idx(_tbl text, _tbl1 text
                                    , _sch text = 'public', _sch1 text = 'public')
  RETURNS void AS
$func$
DECLARE
   _full_tbl  text := format('%I.%I', _sch, _tbl);
   _full_tbl1 text := format('%I.%I', _sch1, _tbl1);
BEGIN
   -- RAISE NOTICE '%', -- for testing
   EXECUTE
   (SELECT string_agg(
              regexp_replace(regexp_replace(
                 pg_get_indexdef(indexrelid)
               , _tbl,  _tbl1)
               , ' ON ' || _full_tbl || ' ',  ' ON ' || _full_tbl1 || ' ')
            , '; ') AS idx
    FROM   pg_index
    WHERE  indrelid = _full_tbl::regclass);
END
$func$ LANGUAGE plpgsql SET search_path = '';
_

これは、二重引用符と異なるスキーマが必要な非標準のテーブル名にも対応しています。

スキーマで修飾されたテーブル名を強制するには、関数(_SET search_path = ''_)内のsearch_pathをリセットします。これにより、設定に依存しない信頼性の高い結果が得られます。

スキーマパラメータ_'public'_および__sch_のデフォルト値として__sch1_を追加しました。したがって、両方のテーブルがパブリックスキーマにある場合は、スキーマパラメータを単純に無視できます。

コール:

_SELECT f_copy_idx('mytbl', 'newtbl');  -- all in schema public
_

または、二重引用符と異なるスキーマを必要とするテーブル名の場合:

_SELECT f_copy_idx('old_TBL', 'table', 'public', 'New_SCHEmA');
_

SQL Fiddle 使用中の関数を示します。

3

それらを「コピー」することはできませんが、データベースのメタデータを使用して動的にDDLを生成し、それらを作成することができます。

SELECT 'CREATE ' 
            || CASE
                WHEN i.indisunique THEN 'UNIQUE '
                ELSE ''
                END
            || 'INDEX '
            || nr.nspname
            || '.'
            || c2.relname
            || '_swap ON '
            || nr.nspname
            || '.'
            || c.relname
            || '_swap ( '
            || split_part ( split_part ( pg_catalog.pg_get_indexdef ( i.indexrelid, 0, true ), '(', 2 ), ')', 1 )
            || ' ) '
    FROM pg_catalog.pg_class c
    JOIN pg_catalog.pg_index i
        ON ( c.oid = i.indrelid )
    JOIN pg_catalog.pg_class c2
        ON ( i.indexrelid = c2.oid )
    JOIN pg_namespace nr
        ON ( nr.oid = c.relnamespace )
    WHERE nr.nspname = 'schema for table1'
        AND c.relname = 'table1' ;
2
gsiems

カイル、

恐らく、PostgreSQLのストレージ構造により、インデックスの単純なコピーは不可能です。 PostgreSQLは heap-structured tables と呼ばれるストレージメソッドを使用し、テーブルへのデータの挿入には保証された順序や位置はありません。

PostgreSQLが1つのテーブルレコードから別のテーブルレコードに、すべての行のすべてのバージョン、削除された行に関する情報などを含む完全なテーブルをコピーする場合、インデックスもコピーできると思います。

現在のPostgreSQLの動作の場合、あるテーブルから別のレプリカテーブルにデータをコピーすると、論理的一貫性のみが保証されます。つまり、元のテーブルは新しいテーブルに存在し、データは行レベルでのみ一致する行になります。

物理的整合性に関して、元のテーブルのディスクにデータが物理的に格納される方法が、レプリカテーブルのデータ。

これが重要な理由は、インデックスがテーブル内のデータに関するいくつかの情報を[〜#〜] ctid [〜# 〜]データを含む行。

端的に言えば、創造的なハッキングを除いて、これらのインデックスを新しいテーブルで再構築する必要があるというのが常識だと思います。申し訳ありませんが、良いニュースはありません。

0
Chris