sql - 无循环依赖的依赖树的PostgreSQL设计

标签 sql postgresql database-design tree circular-dependency

我有一个表,称之为 EVENTS,其中每一行都可以依赖于表中的 0 个或多个其他行。我需要一种表示这种关系的方法,它还可以防止循环依赖(即,一组事件导致返回同一组中的事件)。

我目前有一个 EVENTS 外部的链接表,称之为 EVENTS_DEP。该表将相关行链接到它们所依赖的行,并允许对一行有多个依赖项。我如何使用这样的表来防止循环依赖?

注意:如果仅通过数据库设计(而不是脚本、触发器等)就可以做到这一点,那将是理想的。

此外,如果这只能使用触发器来完成,请告诉我它应该运行在哪种触发器上(即在什么事件上)(也许是在插入时?)。

最佳答案

INSERT 触发器来检查这个。

假设表结构如下

CREATE TABLE event (
    id bigserial PRIMARY KEY,
    foo varchar
);

CREATE TABLE event_deps (
    parent bigint REFERENCES event(id),
    child bigint REFERENCES event(id),

    PRIMARY KEY (parent, child),
    CHECK (parent <> child)
);

将需要以下 INSERT 触发器

CREATE FUNCTION deps_insert_trigger_func() RETURNS trigger AS $BODY$
    DECLARE
        results bigint;
    BEGIN
        WITH RECURSIVE p(id) AS (
            SELECT parent
                FROM event_deps
                WHERE child=NEW.parent
            UNION
            SELECT parent
                FROM p, event_deps d
                WHERE p.id = d.child
            )
        SELECT * INTO results
        FROM p
        WHERE id=NEW.child;

        IF FOUND THEN 
            RAISE EXCEPTION 'Circular dependencies are not allowed.';
        END IF;
        RETURN NEW;
    END;
$BODY$ LANGUAGE plpgsql;

CREATE TRIGGER before_insert_event_deps_trg BEFORE INSERT ON event_deps
    FOR EACH ROW
    EXECUTE PROCEDURE deps_insert_trigger_func();

它所做的是,当在父 A 和子 B 之间添加新链接时,它使用 A WITH RECURSIVE 查询来查找 A 的所有祖先。B 不应该是其中之一。

UPDATE 触发器更难,因为当触发器执行到旧链接仍然存在时,INSERT 触发器的测试在用于 UPDATE 时可能会给出误报。

因此对于 UPDATE,我们需要添加一些额外的条件来隐藏旧数据。

CREATE FUNCTION deps_update_trigger_func() RETURNS trigger AS $BODY$
    DECLARE
        results bigint;
    BEGIN
        WITH RECURSIVE p(id) AS (
            SELECT parent
                FROM event_deps
                WHERE child=NEW.parent
                    AND NOT (child = OLD.child AND parent = OLD.parent) -- hide old row
            UNION
            SELECT parent
                FROM p, event_deps d
                WHERE p.id = d.child
                    AND NOT (child = OLD.child AND parent = OLD.parent) -- hide old row
            )
        SELECT * INTO results
        FROM p
        WHERE id=NEW.child;

        IF FOUND THEN 
            RAISE EXCEPTION 'Circular dependencies are not allowed.';
        END IF;
        RETURN NEW;
    END;
$BODY$ LANGUAGE plpgsql;

CREATE TRIGGER before_update_event_deps_trg BEFORE UPDATE ON event_deps
    FOR EACH ROW
    EXECUTE PROCEDURE deps_update_trigger_func();

关于sql - 无循环依赖的依赖树的PostgreSQL设计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11698263/

相关文章:

mysql - 按连接表中匹配实例的 n_ammounts 进行排序

mysql - 是否可以添加具有其他列平均值的列?

javascript - 如何在sequelize中获取多对多关系的中间表的数据

asp.net - 规范化数据库所需的建议

SQL-Server:将列定义为互斥

sql - 在PostgreSQL中,如何获取所有登录 session 的用户,并在访问数据库时获取他们的IP地址和查询?

ajax - 处理数据库中的 AJAX 并发问题

sql - postgresql - 查看模式权限

postgresql - 有没有办法声明防止重复的 PostgreSQL 数组列?

database-design - 在数据库中释放库存的最佳实践