我想通过使所有表UNLOGGED
来减少本地测试执行时间。我想编写sql脚本,它将在所有转换后运行并使它们UNLOGGED
。但是我发现了问题 - 表与FK
相互关联,因此postgresql禁止使表UNLOGGED
(通过ALTER
(,如果它与尚未UNLOGGED
的其他表相关。
有没有更好的方法,然后按正确的顺序列出所有ALTER
- 我有超过 150 张桌子?例如,在数据库级别应用它。
恐怕你必须按照正确的顺序改变它们。您可以先选择 https://www.postgresql.org/docs/current/static/catalog-pg-constraint.html 和循环来引用表,然后再更改其余部分:
begin;
do
$$
declare
_r record;
_t text;
begin
for _r in (
select relname,conrelid
from pg_constraint
join pg_class c on c.oid = conrelid
where confkey is not null
order by conrelid desc
-- Order by oid with logic that you should start from latest added objects to earliest - of course it does not garantee anything
) loop
_t := format('alter table %I set unlogged',_r.relname);
raise info '%',_t;
execute _t;
end loop;
for _r in (select tablename from pg_tables where tablename like 's%' and schemaname = 'public') loop
_t := format('alter table %I set unlogged',_r.tablename);
raise info '%',_t;
execute _t;
end loop;
end;
$$
;
rollback;
无论如何,如果您有递归 FK,它将失败:
t=# create table s134(i int primary key, e int);
CREATE TABLE
t=# create table s135(i int references s134(i), e int primary key);
CREATE TABLE
t=# alter table s134 add constraint c1 foreign key (e) references s135(e);
ALTER TABLE
t=# alter table s134 set unlogged;
ERROR: could not change table "s134" to unlogged because it references logged table "s135"
t=# alter table s135 set unlogged;
ERROR: could not change table "s135" to unlogged because it references logged table "s134"
但我相信你无论如何都不会做到这一点。
另外不要忘记,在不干净关机或失败后,未记录的表将被截断。
最后,您说"毕竟转换后" - 如果您创建,转换等,也许您应该创建未记录的它们?..
我会删除并重新创建所有外键。您可以自动执行此操作。
以下查询将为所有外键生成必要的 DDL 语句。您需要将其输出保存到一个文件中,稍后可以使用该文件还原所有外键。
select format('alter table %I.%I add constraint %I ', ns.nspname, tb.relname, conname)||
pg_get_constraintdef(c.oid, true)||';' as ddl
from pg_constraint c
join pg_class tb on tb.oid = c.conrelid
join pg_namespace ns on ns.oid = tb.relnamespace
where ns.nspname not in ('pg_catalog', 'information_schema')
and ns.nspname not like 'pg_temp%'
and c.contype in ('f')
然后生成一个脚本来删除所有约束:
select format('alter table %I.%I drop constraint %I cascade;', ns.nspname, tb.relname, c.conname) as ddl
from pg_constraint c
join pg_class tb on tb.oid = c.conrelid
join pg_namespace ns on ns.oid = tb.relnamespace
where ns.nspname not in ('pg_catalog', 'information_schema')
and ns.nspname not like 'pg_temp%'
and c.contype in ('f');
当然,在删除 FK 之前,您必须运行查询以生成 FK ;)
我对接受的答案有困难,因为它依赖于启发式(可能不适用(来正确排序。我使用(有点冗长的(递归查询重新设计了建议的答案,该查询允许人们获得所需的确切顺序。我没有用递归 FK 测试过它,我怀疑递归查询不会终止。请注意,我已将查询限制为应用于public
架构中的表,并根据您自己的需要进行修改。
取消记录所有表:
do
$$
declare
_r record;
_t text;
begin
for _r in (
WITH RECURSIVE constraints AS (
SELECT
tc.table_schema
, tc.table_name
, kcu.column_name
, ccu.table_name AS foreign_table_name
FROM
information_schema.table_constraints AS tc
LEFT OUTER JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
AND tc.table_schema = kcu.table_schema
LEFT OUTER JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
AND ccu.table_schema = tc.table_schema
WHERE (tc.constraint_type IS NULL OR tc.constraint_type = 'FOREIGN KEY'))
, tables_and_constraints AS (
SELECT
t.table_schema
, t.table_name
, c.foreign_table_name
FROM information_schema.tables AS t
LEFT JOIN constraints AS c USING (table_name)
WHERE t.table_type <> 'VIEW')
, dependent_table_constraints AS (
SELECT tc.table_schema
, tc.table_name
, tc.foreign_table_name
, 0 AS depth
FROM tables_and_constraints AS tc
WHERE foreign_table_name IS NULL
UNION
SELECT tc.table_schema
, tc.table_name
, tc.foreign_table_name
, dtc.depth + 1
FROM tables_and_constraints AS tc
INNER JOIN dependent_table_constraints AS dtc ON tc.foreign_table_name = dtc.table_name
) SELECT table_name, max(depth) as depth FROM dependent_table_constraints
WHERE table_schema = 'public'
GROUP BY table_name
ORDER BY depth DESC
) loop
_t := format('ALTER TABLE %I SET UNLOGGED',_r.table_name);
raise info '%',_t;
execute _t;
end loop;
end;
$$
;
重新记录所有表:
do
$$
declare
_r record;
_t text;
begin
for _r in (
WITH RECURSIVE constraints AS (
SELECT
tc.table_schema
, tc.table_name
, kcu.column_name
, ccu.table_name AS foreign_table_name
FROM
information_schema.table_constraints AS tc
LEFT OUTER JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
AND tc.table_schema = kcu.table_schema
LEFT OUTER JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
AND ccu.table_schema = tc.table_schema
WHERE (tc.constraint_type IS NULL OR tc.constraint_type = 'FOREIGN KEY'))
, tables_and_constraints AS (
SELECT
t.table_schema
, t.table_name
, c.foreign_table_name
FROM information_schema.tables AS t
LEFT JOIN constraints AS c USING (table_name)
WHERE t.table_type <> 'VIEW')
, dependent_table_constraints AS (
SELECT tc.table_schema
, tc.table_name
, tc.foreign_table_name
, 0 AS depth
FROM tables_and_constraints AS tc
WHERE foreign_table_name IS NULL
UNION
SELECT tc.table_schema
, tc.table_name
, tc.foreign_table_name
, dtc.depth + 1
FROM tables_and_constraints AS tc
INNER JOIN dependent_table_constraints AS dtc ON tc.foreign_table_name = dtc.table_name
) SELECT table_name, max(depth) as depth FROM dependent_table_constraints
WHERE table_schema = 'public'
GROUP BY table_name
ORDER BY depth ASC
) loop
_t := format('ALTER TABLE %I SET LOGGED',_r.table_name);
raise info '%',_t;
execute _t;
end loop;
end;
$$
;