我有多个表B,C,D ......彼此引用表A.
我们来看看A和B:
CREATE TABLE A (
ID int PRIMARY KEY
, TYPE enum_type
);
CREATE TABLE B (
A_ID int REFERENCES A(ID)
);
当表B引用时,是否可以检查A总是具有一个特定的TYPE?
有可能以某种方式限制?或者我必须使用功能?或者最好的方法是什么?
如果你想用FK约束强制引用完整性,你必须冗余地在表type
中包含b
,在UNIQUE
上添加一个(冗余的)a(id, type)
约束,并在multicolumn FK constraint上创建一个(a_id, type) REFERENCES a(id, type)
。
相关(带代码):
使用table partitioning可以构建不太严格的设计。有一个主表a
与分区键type
上的列表分区。因此,您将拥有一个所有类型为“X”的分区a_x
和一个所有类型为“Y”的分区a_y
。然后为相应的分区创建FK约束,您可以确定类型。
我建议在Postgres 11或更高版本中列出分区。相关(使用列表分区的代码示例):
请注意,您不能对主表具有FK约束。 The manual:
不支持引用分区表的外键。
由于冗余列和冗余索引,FK解决方案占用更多磁盘空间。通过分区OTOH,根据您的Postgres版本和所选的分区方法,您必须处理该特定设置的limitations / caveats。
您发布了另一个基于触发器的解决方案,它不会占用额外的空间,也不会使关系设计复杂化。但它更容易打破/规避。不像FK解决方案那样防弹。无论哪种方式,这是您的想法的改进版本:
CREATE TABLE a (
id int PRIMARY KEY,
type enum_type NOT NULL -- ! NOT NULL or adapt the check in the trigger
);
CREATE UNIQUE INDEX a_type_id_idx ON a(type, id); -- ! see below
CREATE TABLE b (
a_id int REFERENCES a(id)
);
CREATE FUNCTION type_check() -- ! rewritten
RETURNS trigger AS
$type_check$
BEGIN
IF EXISTS (
SELECT
FROM a
WHERE a.id = NEW.a_id
AND a.type = TG_ARGV[0]) THEN
-- do nothing, all good
ELSE
RAISE EXCEPTION 'Type does not match table type!';
END IF;
RETURN NEW;
END
$type_check$ LANGUAGE plpgsql;
CREATE TRIGGER tg_check_type_b
BEFORE INSERT OR UPDATE ON b
FOR EACH ROW EXECUTE PROCEDURE type_check('X');
a(type, id)
上的索引允许仅针对频繁查找的索引扫描。索引表达式按此顺序排列。原因如下:
Is a composite index also good for queries on the first field?IF var_type <> var_allowed_type
检查过,但a.type
没有定义NOT NULL
。这将允许与NULL
不匹配。IF EXISTS ...
替换变量,使用赋值的附加查询应该大大加快 - 并且还通过反转逻辑捕获与NULL
的潜在不匹配。
PostgreSQL IF statementCHECK
constraint更快,更骇客。让NOT VALID
在很大程度上是合法的。代码示例和基本原理的相关案例:
Erwins Option实际上对我有用,但由于我有很多表和多个字段,所以我选择了不同的选项导致表中的开销:
CREATE TABLE A (
ID int PRIMARY KEY,
TYPE enum_type
);
CREATE TABLE B (
A_ID int REFERENCES A(ID)
);
CREATE FUNCTION type_check() RETURNS trigger AS $type_check$
DECLARE
var_type enum_type;
var_allowed_type enum_type;
BEGIN
SELECT TYPE FROM A INTO var_type WHERE ID = NEW.A_ID;
var_allowed_type := TG_ARGV[0];
--Check that TYPE is the same as table
IF var_type <> var_allowed_type THEN
RAISE EXCEPTION 'Type does not match table type';
END IF;
RETURN NEW;
END;
$type_check$ LANGUAGE plpgsql;
CREATE TRIGGER tg_check_type_B
BEFORE INSERT OR UPDATE ON B
FOR EACH ROW EXECUTE PROCEDURE type_check('X');
您可以为x y条件添加检查约束