Mar*_*tus 22 postgresql database-design postgresql-9.1 pg-dump check-constraints
我已经获得了我的 PostgreSQL 数据库的转储:
pg_dump -U user-name -d db-name -f dumpfile
Run Code Online (Sandbox Code Playgroud)
然后我继续在另一个数据库中恢复:
psql X -U postgres -d db-name-b -f dumpfile
Run Code Online (Sandbox Code Playgroud)
我的问题是数据库包含引用约束、检查和触发器,其中一些(特别是检查)在恢复过程中失败,因为信息没有按照会导致这些检查得到遵守的顺序加载。例如,在表中插入一行可能与CHECK调用一个plpgsql函数相关联,该函数检查某个条件是否在某个其他不相关的表中成立。如果后者没有psql在前者之前加载,则会发生错误。
下面是一个 SSCCE,它产生了这样一个一旦转储pg_dump就无法恢复的数据库:
CREATE OR REPLACE FUNCTION fail_if_b_empty () RETURNS BOOLEAN AS $$
SELECT EXISTS (SELECT 1 FROM b)
$$ LANGUAGE SQL;
CREATE TABLE IF NOT EXISTS a (
i INTEGER NOT NULL
);
INSERT INTO a(i) VALUES (0),(1);
CREATE TABLE IF NOT EXISTS b (
i INTEGER NOT NULL
);
INSERT INTO b(i) VALUES (0);
ALTER TABLE a ADD CONSTRAINT a_constr_1 CHECK (fail_if_b_empty());
Run Code Online (Sandbox Code Playgroud)
有没有办法在转储恢复期间禁用(从命令行)所有此类约束并在之后再次启用它们?我正在运行 PostgreSQL 9.1。
Erw*_*ter 22
因此,您在CHECK约束中查找其他表。
CHECK约束应该运行IMMUTABLE检查。一次通过OK的应该随时通过。这就是CHECKSQL 标准中定义约束的方式。这也是手册中进行此限制的原因:
目前,
CHECK表达式不能包含子查询,也不能引用当前行列以外的变量。
尽管如此,CHECK约束中的表达式仍然可以使用函数,甚至是用户定义的函数。那些应该是IMMUTABLE,但是 Postgres 目前没有强制执行这个。根据pgsql-hackers 上的相关讨论,一个原因是允许引用当前时间,这不是IMMUTABLE本质上的。
但是您正在查找另一个表的行,这完全违反了CHECK约束的工作方式。我对pg_dump未能提供这一点并不感到惊讶。
将您在另一个表中的检查移动到触发器(这是正确的工具),它应该可以与现代版本的 Postgres 一起使用。
虽然上述内容适用于任何版本的 Postgres,但 Postgres 9.2 引入了一些工具来帮助您解决问题:
--exclude-table-data一个简单的解决方案是为违规表转储没有数据的数据库:
--exclude-table-data=my_schema.my_tbl
Run Code Online (Sandbox Code Playgroud)
然后在转储结束时仅附加此表的数据:
--data-only --table=my_schema.my_tbl
Run Code Online (Sandbox Code Playgroud)
但是,同一张表上的其他约束可能会出现复杂情况。有一个更好的解决方案:
NOT VALID在 Postgres 9.1 之前,NOT VALID修改器仅可用于 FK 约束。这被扩展到CHECKPostgres 9.2 中的约束。手册:
如果约束被标记为
NOT VALID,则跳过可能冗长的初始检查以验证表中的所有行是否满足约束。该约束仍将针对后续插入或更新强制执行 [...]
一个普通的 Postgres 转储文件由三个“部分”组成:
pre_datadatapost-dataPostgres 9.2 还引入了一个选项,可以使用 单独转储部分-- section=sectionname,但这对解决手头的问题没有帮助。
这就是有趣的地方。手册:
后数据项包括索引、触发器、规则和约束的定义,而 不是经过验证的检查约束。预数据项包括所有其他数据定义项。
大胆强调我的。
您可以将违规CHECK约束更改为NOT VALID,从而将约束移动到该post-data部分。删除并重新创建:
ALTER TABLE a
DROP CONSTRAINT a_constr_1
, ADD CONSTRAINT a_constr_1 CHECK (fail_if_b_empty()) NOT VALID;
Run Code Online (Sandbox Code Playgroud)
单个语句最快,并排除并发事务的竞争条件。(单个事务中的两个命令也可以工作。)
这应该可以解决您的问题。您甚至可以将约束保留在该状态,因为这更好地反映了它的实际作用:检查新行,但不对现有数据提供任何保证。NOT VALID检查约束没有任何问题。如果您愿意,可以稍后对其进行验证:
ALTER TABLE a VALIDATE CONSTRAINT a_constr_1;
Run Code Online (Sandbox Code Playgroud)
但随后你又回到了原状。
| 归档时间: |
|
| 查看次数: |
44097 次 |
| 最近记录: |