触发更新以更新所有子节点

时间:2012-06-12 16:44:40

标签: sql function postgresql triggers plpgsql

在阅读了几个分层数据表的文章之后,我已经进入了一个看起来像这样的表: LOCAL:

|id|name        |parent_id|abbreviature | path        |
------------------------------------------------------- 
|1 |"Shoping"   |NULL     | "Sh"        | "Sh"        |
|2 |"Building A"|1        | "A"         | "Sh.A"      |
|3 |"Building B"|1        | "B"         | "Sh.B"      |
|4 |"Building C"|1        | "C"         | "Sh.C"      |
|5 |"Floor -1"  |2        | "-1"        | "Sh.A.-1"   |
|6 |"Floor 0"   |2        | "0"         | "Sh.A.0"    |
|7 |"Floor 1"   |2        | "1"         | "Sh.A.1"    |
|8 |"Floor 2"   |2        | "2"         | "Sh.A.2"    |
|9 |"Room 101"  |7        | "101"       | "Sh.A.1.101"|
|10|"Hospital"  |NULL     | "Hosp"      | "Hosp"      | 
|11|"Secretary" |10       | "Secrt"     | "Secrt"     |

等等。这样,选择节点的所有后代特别容易。

我创建了这个函数来生成路径:

CREATE OR REPLACE FUNCTION teste1_trig () RETURNS TRIGGER AS '
  DECLARE
        dot    varchar := ''.'';
        npath local.path%TYPE;
  BEGIN
    IF NEW.parent_id IS NULL THEN
        NEW.path:=NEW.abbreviature;
        RETURN NEW;
    ELSEIF NEW.parent_id IS NOT NULL THEN
        SELECT path INTO npath FROM local WHERE id=NEW.parent_id;
        NEW.path:=npath||dot||NEW.abbreviature;
        RETURN NEW;
    END IF;
  END;' LANGUAGE 'plpgsql';

此功能由以下触发器激活:

CREATE TRIGGER trigger_teste
  BEFORE INSERT OR UPDATE
  ON local
  FOR EACH ROW
  EXECUTE PROCEDURE teste1_trig();

除非我从一个有孩子的节点更新一个缩略图,孩子们仍然保留旧路径,否则每件事都能完美无瑕。

我想更改函数以更新当前行中的所有子项。 以下是选择所有子项的查询:

SELECT id,name,parent_id FROM local WHERE id IN ( 
(WITH RECURSIVE parent AS
(
    SELECT id, parent_id  from local WHERE id = id_from_the_modified_node
    UNION ALL 
    SELECT t.id, t.parent_id FROM parent
    INNER JOIN local t ON parent.id =  t.parent_id
)

SELECT id FROM  parent
WHERE id <> id_from_the_modified_node) );

如果更新了abbreviature(即更改缩写词的名称或更改parent_id),如何更改要在一系列行中执行的功能?

我正在使用Postgresql v9.1。

提前谢谢。

1 个答案:

答案 0 :(得分:1)

我已经找到了答案,我认为这是最好的情况。如果我错了,请纠正我!

DECLARE 
id_upd INT[];
...
BEGIN
...

SELECT array ( SELECT id FROM local WHERE parent_id=NEW.id ) into id_upd; --- select direct childs
    IF id_upd IS NOT NULL THEN              
        FOREACH i IN ARRAY id_upd LOOP      
           UPDATE local SET path=NEW.path||'.'||abbreviature where id=i; --- update them
        END loop;       
    END IF;

END;

这样我只更新节点的所有直接子节点,触发器再次为子节点的子节点触发,等等。

不要忘记检查父节点是否不是节点的后代,这样你就会创建一个无限循环。

最好的问候