將索引從一張表複製到另一張表
我有一系列 ETL 作業,我在其中
swap
使用CREATE TABLE table1_swap LIKE table1
. 為了使填充table1_swap
更快,我不包括索引。但是,當我完成載入後,我需要將這些索引重新應用到新填充的表中。這些索引是在這些 ETL 作業的範圍之外創建的,因此如果不需要,我寧願不必對CREATE INDEX
呼叫進行硬編碼。是否可以將一組索引從一個表“轉移”或“複製”到另一個表?
您可以從 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';
您可以將輸出假離線到腳本中,並在複製數據並交換錶名後執行它。
基本
要獲取所有索引定義,您可以使用 @a_horse 已經提供的系統目錄
pg_get_indexdef(index_oid)
資訊功能。
regclass
但是,使用參數查詢可以相當簡單。對於架構中呼叫的表tbl
:public
SELECT pg_get_indexdef(indexrelid) || ';' AS idx FROM pg_index WHERE indrelid = 'public.tbl'::regclass; -- optionally schema-qualified
這包括所有索引: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 (cu**tbl**ade IS NOT NULL);
我將原始表名的每一次出現都加粗。請注意我們不想替換的三個誤報(列名或索引名的一部分)。
此查詢應該可以完美執行 - 但請自己對照您的實際索引進行驗證!
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()
僅替換名稱中的第一個匹配項(除非另有說明)。第二個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 = '';
這也適用於需要雙引號和不同模式的非標準表名。
為了強制使用模式限定的表名,我在函式 ( ) 中重置了 search_path
SET 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展示了正在使用的函式。