如何通过约束强制执行引用的行也具有给定的类型

问题描述 投票:1回答:3

我有多个表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?

  • 表B引用的A中的每一行都必须有TYPE'X'
  • 表C引用的A中的每一行都必须有TYPE'Y'
  • 表D引用的A中的每一行都必须有TYPE'Z'
  • 等等

有可能以某种方式限制?或者我必须使用功能?或者最好的方法是什么?

sql postgresql database-design foreign-keys constraints
3个回答
2
投票

1. FK constraint

如果你想用FK约束强制引用完整性,你必须冗余地在表type中包含b,在UNIQUE上添加一个(冗余的)a(id, type)约束,并在multicolumn FK constraint上创建一个(a_id, type) REFERENCES a(id, type)

相关(带代码):

2. Partitioning

使用table partitioning可以构建不太严格的设计。有一个主表a与分区键type上的列表分区。因此,您将拥有一个所有类型为“X”的分区a_x和一个所有类型为“Y”的分区a_y。然后为相应的分区创建FK约束,您可以确定类型。

我建议在Postgres 11或更高版本中列出分区。相关(使用列表分区的代码示例):

请注意,您不能对主表具有FK约束。 The manual

不支持引用分区表的外键。

由于冗余列和冗余索引,FK解决方案占用更多磁盘空间。通过分区OTOH,根据您的Postgres版本和所选的分区方法,您必须处理该特定设置的limitations / caveats

3. Trigger

您发布了另一个基于触发器的解决方案,它不会占用额外的空间,也不会使关系设计复杂化。但它更容易打破/规避。不像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不匹配。
  • 在plpgsql中,赋值相对较高。我简化了功能,使其快速。 IF EXISTS ...替换变量,使用赋值的附加查询应该大大加快 - 并且还通过反转逻辑捕获与NULL的潜在不匹配。 PostgreSQL IF statement

4. Abuse a CHECK constraint

更快,更骇客。让NOT VALID在很大程度上是合法的。代码示例和基本原理的相关案例:


1
投票

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');

0
投票

您可以为x y条件添加检查约束

© www.soinside.com 2019 - 2024. All rights reserved.