如何在 postgres 中自动交换 table 名称及其引用而不会出现任何问题?
How can I atomically swap table names and its references in postgres without any issues?
我想交换两个 table 的名字。表 A <> 表 B。也自动避免 read/writes 的任何问题。我知道我可以在交易中做到这一点。
我正在使用 CREATE TABLE TableB (LIKE TableA INCLUDING ALL);
创建 table。然后我还将 FK 从 A
复制到 B
。然后我正在做一个 INSERT INTO TABLEA....
来复制数据(与此无关)。完成所有这些后,我使用 ALTER TABLE RENAME
重命名 table 以在事务中交换名称 TableA
<> TableB
。所以 TableA
变成 TableA_Old
而 TableB
变成新的 TableA
(下面的例子)
但是,这不会更新其他 table 中对这个新 table 的引用,例如 TableC
仍然持有针对 TableA_Old
的 FK。当我在新重命名的 table TableA
(之前是 TableB
)上执行 \d+
时,我没有看到引用。他们仍然指向 TableA_Old
.
这是一个例子
我创建表A
CREATE TABLE TableA (
id serial PRIMARY KEY
);
testdb=> \d TableA
Table "public.tablea"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('tablea_id_seq'::regclass)
Indexes:
"tablea_pkey" PRIMARY KEY, btree (id)
我参照TableA创建TableC
CREATE TABLE TableC(
id serial PRIMARY KEY,
table_a_id INT,
CONSTRAINT table_a_table_c_fk
FOREIGN KEY(table_a_id)
REFERENCES TableA(id)
);
\d TableC
Table "public.tablec"
Column | Type | Collation | Nullable | Default
------------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('tablec_id_seq'::regclass)
table_a_id | integer | | |
Indexes:
"tablec_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"table_a_table_c_fk" FOREIGN KEY (table_a_id) REFERENCES tablea(id)
可以参考一下
现在我使用函数
创建看起来像 TableA
的 TableB
create or replace function create_table_like(source_table text, new_table text)
returns void language plpgsql
as $$
declare
rec record;
begin
execute format(
'create table %s (like %s including all)',
new_table, source_table);
for rec in
select oid, conname
from pg_constraint
where contype = 'f'
and conrelid = source_table::regclass
loop
execute format(
'alter table %s add constraint %s %s',
new_table,
replace(rec.conname, source_table, new_table),
pg_get_constraintdef(rec.oid));
end loop;
end $$;
select create_table_like('TableA', 'TableB');
\d TableB
testdb=> select create_table_like('TableA', 'TableB');
create_table_like
-------------------
(1 row)
testdb=> \d TableB
Table "public.tableb"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('tablea_id_seq'::regclass)
Indexes:
"tableb_pkey" PRIMARY KEY, btree (id)
现在我重命名它们
BEGIN;
ALTER TABLE TableA RENAME to TableA_OLD;
ALTER TABLE TableB RENAME to TableA;
COMMIT;
现在看结构的时候,参考的还是TableA_OLD
from TableC
testdb=> \d TableA
Table "public.tablea"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('tablea_id_seq'::regclass)
Indexes:
"tableb_pkey" PRIMARY KEY, btree (id)
testdb=> \d TableB
testdb=> \d TableA_old
Table "public.tablea_old"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('tablea_id_seq'::regclass)
Indexes:
"tablea_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE "tablec" CONSTRAINT "table_a_table_c_fk" FOREIGN KEY (table_a_id) REFERENCES tablea_old(id)
而TableC
指向旧的table - REFERENCES tablea_old(id)
testdb=> \d TableC
Table "public.tablec"
Column | Type | Collation | Nullable | Default
------------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('tablec_id_seq'::regclass)
table_a_id | integer | | |
Indexes:
"tablec_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"table_a_table_c_fk" FOREIGN KEY (table_a_id) REFERENCES tablea_old(id)
有没有一种安全的方法可以做到这一点而无需再次删除和重新创建约束?
我知道我还可以更新 pg_class
中的 relfilenode
指针并交换它们。但是,这是有风险的,因为 TableB
的架构可能略有不同。所以,我想知道我是否可以更新系统目录中的其他一些 table 或使用不需要删除约束的 DDL。
一个对象的真实身份是它的对象 ID,一个用于在外键约束中引用对象的数字,其他内部数据库很重要(大多数函数体除外)。重命名对象不会改变其身份。您必须删除并重新创建外键约束。
如果您使用 rename table
命令,那么将自动重命名所有依赖对象、外键、链接到此 table 的引用。例如:
ALTER TABLE public.table1 RENAME TO table2;
我想交换两个 table 的名字。表 A <> 表 B。也自动避免 read/writes 的任何问题。我知道我可以在交易中做到这一点。
我正在使用 CREATE TABLE TableB (LIKE TableA INCLUDING ALL);
创建 table。然后我还将 FK 从 A
复制到 B
。然后我正在做一个 INSERT INTO TABLEA....
来复制数据(与此无关)。完成所有这些后,我使用 ALTER TABLE RENAME
重命名 table 以在事务中交换名称 TableA
<> TableB
。所以 TableA
变成 TableA_Old
而 TableB
变成新的 TableA
(下面的例子)
但是,这不会更新其他 table 中对这个新 table 的引用,例如 TableC
仍然持有针对 TableA_Old
的 FK。当我在新重命名的 table TableA
(之前是 TableB
)上执行 \d+
时,我没有看到引用。他们仍然指向 TableA_Old
.
这是一个例子
我创建表A
CREATE TABLE TableA (
id serial PRIMARY KEY
);
testdb=> \d TableA
Table "public.tablea"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('tablea_id_seq'::regclass)
Indexes:
"tablea_pkey" PRIMARY KEY, btree (id)
我参照TableA创建TableC
CREATE TABLE TableC(
id serial PRIMARY KEY,
table_a_id INT,
CONSTRAINT table_a_table_c_fk
FOREIGN KEY(table_a_id)
REFERENCES TableA(id)
);
\d TableC
Table "public.tablec"
Column | Type | Collation | Nullable | Default
------------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('tablec_id_seq'::regclass)
table_a_id | integer | | |
Indexes:
"tablec_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"table_a_table_c_fk" FOREIGN KEY (table_a_id) REFERENCES tablea(id)
可以参考一下
现在我使用函数
创建看起来像TableA
的 TableB
create or replace function create_table_like(source_table text, new_table text)
returns void language plpgsql
as $$
declare
rec record;
begin
execute format(
'create table %s (like %s including all)',
new_table, source_table);
for rec in
select oid, conname
from pg_constraint
where contype = 'f'
and conrelid = source_table::regclass
loop
execute format(
'alter table %s add constraint %s %s',
new_table,
replace(rec.conname, source_table, new_table),
pg_get_constraintdef(rec.oid));
end loop;
end $$;
select create_table_like('TableA', 'TableB');
\d TableB
testdb=> select create_table_like('TableA', 'TableB');
create_table_like
-------------------
(1 row)
testdb=> \d TableB
Table "public.tableb"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('tablea_id_seq'::regclass)
Indexes:
"tableb_pkey" PRIMARY KEY, btree (id)
现在我重命名它们
BEGIN;
ALTER TABLE TableA RENAME to TableA_OLD;
ALTER TABLE TableB RENAME to TableA;
COMMIT;
现在看结构的时候,参考的还是TableA_OLD
from TableC
testdb=> \d TableA
Table "public.tablea"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('tablea_id_seq'::regclass)
Indexes:
"tableb_pkey" PRIMARY KEY, btree (id)
testdb=> \d TableB
testdb=> \d TableA_old
Table "public.tablea_old"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('tablea_id_seq'::regclass)
Indexes:
"tablea_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE "tablec" CONSTRAINT "table_a_table_c_fk" FOREIGN KEY (table_a_id) REFERENCES tablea_old(id)
而TableC
指向旧的table - REFERENCES tablea_old(id)
testdb=> \d TableC
Table "public.tablec"
Column | Type | Collation | Nullable | Default
------------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('tablec_id_seq'::regclass)
table_a_id | integer | | |
Indexes:
"tablec_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"table_a_table_c_fk" FOREIGN KEY (table_a_id) REFERENCES tablea_old(id)
有没有一种安全的方法可以做到这一点而无需再次删除和重新创建约束?
我知道我还可以更新 pg_class
中的 relfilenode
指针并交换它们。但是,这是有风险的,因为 TableB
的架构可能略有不同。所以,我想知道我是否可以更新系统目录中的其他一些 table 或使用不需要删除约束的 DDL。
一个对象的真实身份是它的对象 ID,一个用于在外键约束中引用对象的数字,其他内部数据库很重要(大多数函数体除外)。重命名对象不会改变其身份。您必须删除并重新创建外键约束。
如果您使用 rename table
命令,那么将自动重命名所有依赖对象、外键、链接到此 table 的引用。例如:
ALTER TABLE public.table1 RENAME TO table2;