验证树的最佳方法是建模邻接列表,TRIGGER与CHECK约束

时间:2017-06-13 21:40:48

标签: sql postgresql database-design

我正在使用PostgreSQL使用邻接列表方法对树数据结构进行建模。我希望能够在每个叶子上存储额外的数据,以指示该树属于哪个组:

CREATE TABLE groups (
  group_id integer PRIMARY KEY
);

CREATE TABLE leafs (
  leaf_id   integer PRIMARY KEY,
  parent_id integer REFERENCES leafs ON DELETE CASCADE ON UPDATE CASCADE,

  group_id integer REFERENCES groups ON DELETE CASCADE ON UPDATE CASCADE NOT NULL
);

我还想确保每个叶子只能连接到同一个组。看起来这可以通过创建TRIGGERCHECK约束来完成。我有两个问题:

  1. 处理此特定情况TRIGGERCHECK约束的最有效/正确方法是什么? (以及在这两者之间进行选择的规则是什么)

  2. 是否有更好的方法来强制实施此模型的一致性(或者可能是对这些树组进行建模的替代方法)。

  3. 谢谢,

    TRIGGER版的代码:

    CREATE OR REPLACE FUNCTION after_leaf_update()
      RETURNS trigger AS
    $$
    BEGIN
    IF (NEW.parent_id IS NOT NULL) AND (SELECT group_id FROM leafs WHERE leaf_id=NEW.parent_id) <> NEW.group_id THEN
        RAISE EXCEPTION 'group_id of node/leaf does not match!!!';
    END IF;
    RETURN NEW;
    END;
    $$
    LANGUAGE 'plpgsql';
    
    
    CREATE TRIGGER leafs_consistency_check
      BEFORE INSERT OR UPDATE
      ON leafs
      FOR EACH ROW
      EXECUTE PROCEDURE after_leaf_update();
    

    CHECK约束版本的代码:

    CREATE OR REPLACE FUNCTION leafs_consistency_check_constraint(prn_id integer, grp_id integer) RETURNS BOOL AS
    $$
    BEGIN
        IF (prn_id IS NOT NULL) AND (SELECT group_id FROM leafs WHERE leaf_id=prn_id) <> grp_id THEN
        RAISE EXCEPTION 'CHECK: group_id of node/leaf does not match!!!';
    END IF;
    RETURN TRUE;
    
    END;
    $$ LANGUAGE plpgsql;
    
    ALTER TABLE leafs ADD constraint group_id_constraint check ( leafs_consistency_check_constraint(parent_id, group_id) );
    

1 个答案:

答案 0 :(得分:1)

使用触发器。

一般来说,Postgres开发人员不建议在CHECK约束中放置运行查询的函数。除此之外,在某些情况下,对于查询,可能会多次检查CHECK约束,这是一个相当昂贵的检查。更重要的是,对于行可见性和触发器的类似注意事项,有很多仔细的工程设计,对于约束条件而言根本就不存在。

我还建议您考虑另一种方法:如果使用BEFORE触发器,忽略用户输入,并自动为新节点提供与其父节点相同的group_id,而不是抛出错误?

或者甚至更好,因为group_ids与父母有关系而不与子女有关,如何将group_ids放在一个单独的表中,该表只链接到父节点?