Postgresql, поддерживая иерархические данные с помощью триггеров

У меня есть список смежности таблицы счета, с колоннами идентификатор, код, имя и 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. Но для UPDATEs возникает ошибка: "Утверждение UPDATE в таблице" account "должно обновить 1 строку (строки), 0 совпало". У меня есть сомнения в части "UPDATE public.account...". Может кто-нибудь помочь мне исправить вышеупомянутый триггер?

1
21 янв. '14 в 5:31
источник поделиться
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);
0
21 янв. '14 в 8:04
источник

Посмотрите другие вопросы по меткам или Задайте вопрос