Jef*_*f G 5 postgresql foreign-keys database-partitioning postgresql-9.3
我有一个分区表(称为 A),其串行主键被另一个表(称为 B)引用。我知道我实际上无法创建从一个到另一个的外键(因为我不知道数据实际存储在哪个分区),因此,我尝试使用 check 来模拟外键的行为限制。像下面这样:
CREATE TABLE A (
MyKey SERIAL PRIMARY KEY
);
CREATE TABLE B (
AKey INT, -- Should have: REFERENCES A (MyKey),
-- but can't due to Postgres limitations
);
CREATE TABLE APart1 (
Field1 INT,
PRIMARY KEY (MyKey)
) INHERITS (A);
CREATE TABLE APart2 (
Field2 INT,
PRIMARY KEY (MyKey)
) INHERITS (A);
CREATE FUNCTION ValidateKeyInA(aKey INT) RETURNS BOOL AS $$
BEGIN
PERFORM * FROM A WHERE MyKey = aKey;
IF FOUND THEN
RETURN TRUE;
END IF;
RETURN FALSE;
END;
$$ LANGUAGE PLPGSQL;
ALTER TABLE B ADD CHECK (ValidateKeyInA(AKey));
WITH aKey AS (INSERT INTO APart1 (Field1) VALUES (1) RETURNING MyKey)
INSERT INTO B (AKey) SELECT * FROM aKey;
WITH aKey AS (INSERT INTO APart2 (Field2) VALUES (2) RETURNING MyKey)
INSERT INTO B (AKey) SELECT * FROM aKey;
Run Code Online (Sandbox Code Playgroud)
这工作得很好,直到我转储并恢复数据库。此时,Postgres 不知道表 B 依赖于表 A(及其分区)中的数据,而 B 恰好在表 A 之前被转储。我尝试将“DEFERRABLE”关键字添加到我所在的行添加约束,但 Postgres 不支持可延迟检查约束。
我建议的方法是将我的检查约束转换为约束触发器,我可以推迟它,然后在事务中导入我的数据库转储。有更直接的方法吗?例如,我是否可以通过某种方式告诉 Postgres 在表 A 及其所有分区都已转储之前不要转储表 B(例如,将 B 的依赖项添加到 A 的分区)?我应该使用其他一些模式来代替?谢谢。
@TommasoDiBucchianico 给出的两个选项都是有效的方法,但由于以下陷阱,我仍然想要不同的方法:
选项#1:重命名表,使它们的字母顺序与加载表的顺序相匹配。
这个被避免了,因为 1)它依赖于 pg_dump 的一个未记录的功能,2)它迫使我为每个表提供不太理想的名称。
选项#2:提供一个文本文件,其中包含表,按照我希望 pg_restore 加载的顺序排列。
我真的很喜欢这个选项,但缺点是每当表被重命名、添加或删除时,就必须有人手动修改文本文件来重新定义顺序。
我没有尝试重新排序数据,而是决定将所有有问题的检查约束转换为约束触发器。检查约束是预数据,而约束触发器是后数据。这意味着在加载所有数据之前不会添加约束触发器,这不需要按任何特定顺序加载数据即可工作。下面显示了我如何修改原始帖子中的示例以使用约束触发器:
CREATE TABLE A (
MyKey SERIAL PRIMARY KEY
);
CREATE TABLE B (
AKey INT
);
CREATE TABLE APart1 (
Field1 INT,
PRIMARY KEY (MyKey)
) INHERITS (A);
CREATE TABLE APart2 (
Field2 INT,
PRIMARY KEY (MyKey)
) INHERITS (A);
CREATE FUNCTION ValidateKeyInA() RETURNS TRIGGER AS $$
BEGIN
PERFORM * FROM A WHERE MyKey = NEW.AKey;
IF NOT FOUND THEN
RAISE EXCEPTION '%: AKey not found in A', TG_NAME;
END IF;
RETURN NEW;
END;
$$ LANGUAGE PLPGSQL;
CREATE CONSTRAINT TRIGGER "ValidateTableB"
AFTER INSERT OR UPDATE ON B FROM A
FOR EACH ROW EXECUTE PROCEDURE ValidateKeyInA();
WITH aKey AS (INSERT INTO APart1 (Field1) VALUES (1) RETURNING MyKey)
INSERT INTO B (AKey) SELECT * FROM aKey;
WITH aKey AS (INSERT INTO APart2 (Field2) VALUES (2) RETURNING MyKey)
INSERT INTO B (AKey) SELECT * FROM aKey;
Run Code Online (Sandbox Code Playgroud)