【问题标题】:Postgresql, maintaining hierarchical data with triggersPostgresql,使用触发器维护分层数据
【发布时间】:2014-02-10 10:49:41
【问题描述】:

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

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,会引发错误:“表 'account' 上的 UPDATE 语句预期更新 1 行;匹配了 0 行。”。我对“更新 public.account ...”部分有疑问。有人可以帮我纠正上述触发器吗?

【问题讨论】:

    标签: postgresql triggers plpgsql hierarchical-data


    【解决方案1】:

    好吧,在上面的代码中,更新部分更新了所有记录,包括触发触发器的记录(并发执行?)。这似乎行不通。所以我不得不发出 2 个不同的声明:

    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);
    

    【讨论】:

      猜你喜欢
      • 2018-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多