Postgresql,使用触发器维护分层数据

时间:2014-01-21 02:31:17

标签: postgresql triggers plpgsql hierarchical-data

我有邻接列表帐户,列 id 代码名称 PARENT_ID 即可。 为了使排序和显示更容易,我添加了两列:深度路径(物化路径)。我知道,postgresql有物化路径的专用数据类型,但我想使用更通用的方法,而不是特定于postgresql。我还在设计中应用了几条规则:
1)代码最长可达10个字符
2)最大深度为9;所以root帐户可以有最多8级的子帐户 3)设置后, parent_id 永远不会更改,因此无需将树的分支移动到树的另一部分。
4)路径是帐户的物化路径,最长可达90个字符;它是通过连接帐户代码构建的,右边填充长度为10个字符;例如,像'10000______10001______' 因此,要自动维护深度路径列,我为帐户表创建了触发器和触发器功能:

CREATE FUNCTION public.fn_account_set_hierarchy()
RETURNS TRIGGER AS $$
DECLARE d INTEGER; p CHARACTER VARYING;
BEGIN
    IF TG_OP = 'INSERT' THEN
    IF NEW.parent_id IS NULL THEN
        NEW.depth := 1;
        NEW.path := rpad(NEW.code, 10);
    ELSE
        BEGIN
        SELECT depth, path INTO d, p 
                    FROM public.account 
                    WHERE id = NEW.parent_id;
        NEW.depth := d + 1;
        NEW.path := p || rpad(NEW.code, 10);
        END;
    END IF;
    ELSE
    IF NEW.code IS DISTINCT FROM OLD.code THEN
        UPDATE public.account 
                SET path = OVERLAY(path PLACING rpad(NEW.code, 10) 
                                   FROM (OLD.depth - 1) * 10 + 1 FOR 10)
        WHERE SUBSTRING(path FROM (OLD.depth - 1) * 10 + 1 FOR 10) = 
                                                            rpad(OLD.code, 10);
    END IF;
    END IF;
    RETURN NEW;
END$$
LANGUAGE plpgsql

CREATE TRIGGER tg_account_set_hierarchy
BEFORE INSERT OR UPDATE ON public.account
FOR EACH ROW
EXECUTE PROCEDURE public.fn_account_set_hierarchy();

以上似乎适用于INSERT。但是对于UPDATE,会抛出一个错误:“表'帐户'上的UPDATE语句'预计会更新1行;匹配的是0。”我对“更新public.account ...”部分表示怀疑。有人可以帮我纠正上述触发器吗?

1 个答案:

答案 0 :(得分:0)

那么,在上面的代码中,更新部件会更新触发器触发的所有记录,包括记录(并发执行?)。这似乎不起作用。所以我不得不发出两个不同的陈述:

UPDATE {0}.{1} SET path = OVERLAY(path PLACING rpad(NEW.code, 10) 
FROM (OLD.depth - 1) * 10 + 1 FOR 10)
WHERE SUBSTRING(path FROM (OLD.depth - 1) * 10 + 1 FOR 10) = rpad(OLD.code, 10) 
              AND id <> NEW.id;
NEW.path = OVERLAY(OLD.path PLACING rpad(NEW.code, 10) 
                       FROM (OLD.depth - 1) * 10 + 1 FOR 10);