我有一个表,称之为 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/