我有一个问题,我有一个员工与每个经理的自引用表,有时由于人为错误,它会创建一个无限循环。
CREATE TABLE employees (
id serial PRIMARY KEY,
name varchar(255) NOT NULL,
job varchar(255),
manager_id int,
FOREIGN KEY (manager_id) REFERENCES employees (id) ON DELETE CASCADE);
下面是我用来测试无限循环的行。 ID 1 到> ID 5 到> ID 7 到> ID 1.....
INSERT INTO employees (id,name,manager_id,job)VALUES
(1, 'Max', 5, 'CEO'),
(2, 'Jeremy', 5, 'Junior Developer'),
(3, 'Allen', 5, 'Intern'),
(4, 'Lucas', 5, 'Developer'),
(5, 'John', 7, 'Manager'),
(6, 'Creed', 7, 'DevOps Engineer'),
(7, 'Anna', 1, 'VP'),
(8, 'Logan', 1, 'Manager'),
(9, 'Ted', 8, 'Data Analyst'),
(10, 'Jensen', 8, 'Developer');
我尝试在更新后创建触发器,但由于某种原因它不起作用。
create or replace function if_atu_employee() returns trigger as
$$
DECLARE
hier RECORD;
begin
IF NEW.manager_id IS NULL THEN
return NEW;
END IF;
WITH RECURSIVE managers AS (
SELECT id, name, manager_id, job
FROM employees
WHERE id = NEW.id
UNION ALL
SELECT e.id, e.name, e.manager_id, e.job
FROM employees e
JOIN managers ON e.manager_id = managers.id
)
cycle ID set is_cycle using path
SELECT INTO hier id FROM managers WHERE is_cycle limit 1;
IF hier IS NOT NULL THEN
UPDATE employees SET manager_id = NULL WHERE id = NEW.id;
END IF;
return NEW;
end;
$$ language plpgsql;
create trigger if_tr_employee after update on employees for each row execute procedure if_atu_employee();
有人知道我在函数中做错了什么吗?
这个似乎适用于插入和更新。
CREATE OR REPLACE FUNCTION tr_check_loop()
RETURNS TRIGGER
LANGUAGE plpgsql
AS
$$
DECLARE
_id INT;
BEGIN
WITH RECURSIVE managers AS (
SELECT id
, name
, ARRAY[manager_id] manager_ids
, job
FROM employees
WHERE id = NEW.id
UNION ALL
SELECT e.id
, e.name
, managers.manager_ids || e.manager_id,
e.job
FROM employees e
JOIN managers ON e.manager_id = managers.id
)
SELECT NEW.id
INTO _id
FROM managers
WHERE ARRAY(SELECT e FROM unnest(manager_ids) u(e) ORDER BY e )
<>
ARRAY(SELECT DISTINCT e FROM unnest(manager_ids) u(e) ORDER BY e )
LIMIT 1;
IF FOUND THEN
RAISE EXCEPTION 'Loop detected for id %', _id;
END IF;
RETURN NEW;
END;
$$;
CREATE TRIGGER tr_loop AFTER INSERT OR UPDATE ON employees
FOR EACH ROW EXECUTE FUNCTION tr_check_loop();
检查包含路径的数组是否存在非不同值应该可以检测到循环。而且它确实只需要一个循环,因此一旦发现问题就会停止。它还引发了一个异常,因为代码永远不会知道谁是正确的经理。我不建议设置默认 NULL,因为您可能需要几周甚至几个月的时间才能发现您的数据已被破坏。