1

階層データ テーブルに関するいくつかの記事を読んだ後、次のようなテーブルにたどり着きました: 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) );

略語が更新された場合、一連の行で実行される関数を変更するにはどうすればよいですか (つまり、略語の名前を変更するか、parent_id を変更します)?

Postgresql v9.1 を使用しています。

前もって感謝します。

4

1 に答える 1

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;

このようにして、ノードのすべての直接の子を更新するだけで、子の子などに対してトリガーが再度起動します。

親のノードがノードの子孫ではないかどうかを確認することを忘れないでください。こうすることで、無限ループが作成されます。

よろしくお願いします

于 2012-06-28T14:52:21.073 に答える