问题描述
我目前正在 Postgresql 中寻找一种方法,我可以对给定的表限制有效外键的数量 可通过定义条件获得。
所以剧情是这样的
https://dbfiddle.uk/?rdbms=postgres_11&fiddle=4dbe279906dc881598b7e72093534ce7
A、B、C 都代表不同的实体,每个具体的条目都列在 x_entry 表中。
表 A、B、C 都代表给定条目的版本,以及它们在多长时间内有效。
我想确保
表 A 总是有 B 和 C 的外键
其中时间跨度 A 的条件 A.B.C == A.C
。
如图所示
2000-3000 范围内 Ainit
的条件被违反,因为时间范围 binit
中的 2100-3000
有一个指向 cinot
的外键
其中 ainit 将其外键从 cinit 更改为 cinat
违反了 A.B.C == A.C
但是我如何设置这样的条件限制,以防止外键不违反这样的特定条件。 ? 甚至有可能吗?
解决方法
我目前正在 PostgreSQL 中寻找一种方法 给定表,限制可用的有效外键数量 定义条件。
你不能那样做。
外键约束基于唯一性强制参照完整性,例如“table_a.id == table_b.a_id”。您不能过滤重叠的日期范围或类似的内容。
我可以从根本上想到一种方法来完成您的要求:触发器*
这并不太复杂,您在每个表上添加“BEFORE INSERT/DELETE/UPDATE”触发器,拒绝所有违反规则的插入/更新/删除。
如果你可以重组你的表格,它会变得更容易,你只需要两个表格:
entry
id
type ("a","b","c")
name ("cinit",etc.)
PK entry(id,type)
entry_version
id
entry_id
type ("a","c")
valid_period (tsrange)
PK entry_version(id)
FK entry_version(entry_id,type) -> entry(id,type)
现在您只需要在 entry_version 上执行规则的插入/更新/删除触发器。
您可能想要研究的内容:
EXCLUDE 约束 例如,您可以确保不能将日期范围重叠的两行放在同一个表中。
表分区您可以为每个有效期或其他内容设置不同的分区。 (不过对你来说可能是死胡同)。
找出将日期范围转换为表格中的行并使用外键引用它们的某种方法。
*是的,也有 RULE:s 但那是专家级
,我结束了对表 A 和表 B 的检查约束 两者都检查这个功能
CREATE OR REPLACE FUNCTION lookup_filtering_check(
intermediate_table regclass,intermediate_table_column_name text,timespan tsrange,lookup_value uuid,entity_id uuid,OUT result boolean
)
LANGUAGE plpgsql AS
$$
BEGIN
EXECUTE
FORMAT(
'SELECT ('
'NOT EXISTS('
'SELECT FROM %1$I '
'WHERE %1$I.valid && %3$L '
'AND %1$I.id = %5$L '
'AND %1$I.%2$I<> %4$L '
')'
')',intermediate_table,intermediate_table_column_name,timespan,lookup_value,entity_id)
USING intermediate_table,id
INTO result;
END
$$;
在表 A 和表 B 中。
我确实看到了一些性能问题,因为它必须检查每一行。