Postgres 9.6 - расширение дерева - (пере) построить дерево на столе

В следующей таблице (отображенной моделью Django) я пытаюсь написать функцию для построения дерева, используя ltree расширение:

    Column     |           Type           | Collation | Nullable | Default
---------------+--------------------------+-----------+----------+---------
created       | timestamp with time zone |           | not null |
modified      | timestamp with time zone |           | not null |
guid          | uuid                     |           | not null |
comp_user_id  | character varying(150)   |           | not null |
comp_username | character varying(150)   |           | not null |
comp_email    | character varying(254)   |           |          |
id            | integer                  |           | not null |
path          | ltree                    |           |          |
first_name    | character varying(150)   |           |          |
last_name     | character varying(150)   |           |          |
manager_id    | integer                  |           |          |
user_id       | integer                  |           |          |
Indexes:
    "corp_companyeuserprofile_pkey" PRIMARY KEY, btree (id)
    "corp_companyeuserprofile_user_id_key" UNIQUE CONSTRAINT, btree (user_id)
    "corp_companyeuserprofile_comp_email_6f7503d7" btree (comp_email)
    "corp_companyeuserprofile_comp_email_6f7503d7_like" btree (comp_email varchar_pattern_ops)
    "corp_companyeuserprofile_comp_user_id_328cb82e" btree (comp_user_id)
    "corp_companyeuserprofile_comp_user_id_328cb82e_like" btree (comp_user_id varchar_pattern_ops)
    "corp_companyeuserprofile_comp_username_9eec69b2" btree (comp_username)
    "corp_companyeuserprofile_comp_username_9eec69b2_like" btree (comp_username varchar_pattern_ops)
    "corp_companyeuserprofile_guid_e3160b25" btree (guid)
    "corp_companyeuserprofile_manager_id_2491a6e2" btree (manager_id)
    "cup_path_btree_idx" btree (path)
    "cup_path_gist_idx" gist (path)
Check constraints:
    "check_no_recursion" CHECK (index(path, id::text::ltree) = (nlevel(path) - 1))
Foreign-key constraints:
    "corp_comp_manager_id_2491a6e2_fk_cornersto" FOREIGN KEY (manager_id) REFERENCES corp_companyeuserprofile(id) DEFERRABLE INITIALLY DEFERRED
    "corp_comp_user_id_39765502_fk_users_use" FOREIGN KEY (user_id) REFERENCES users_user(id) DEFERRABLE INITIALLY DEFERRED
Referenced by:
    TABLE "corp_companyeuserprofile" CONSTRAINT "corp_comp_manager_id_2491a6e2_fk_cornersto" FOREIGN KEY (manager_id) REFERENCES corp_companyeuserprofile(id) DEFERRABLE INITIALLY DEFERRED
Triggers:
    cup_path_after_trg AFTER UPDATE ON corp_companyeuserprofile FOR EACH ROW WHEN (new.path IS DISTINCT FROM old.path) EXECUTE PROCEDURE _update_descendants_manager_path()
    cup_path_insert_trg BEFORE INSERT ON corp_companyeuserprofile FOR EACH ROW EXECUTE PROCEDURE _update_manager_path()
    cup_path_update_trg_two BEFORE UPDATE ON corp_companyeuserprofile FOR EACH ROW EXECUTE PROCEDURE _update_manager_path()

Я сначала нашел и пример на github, который я адаптировал, который использует триггеры до / после вставок и обновлений:

-- function to calculate the path of any given manager
CREATE OR REPLACE FUNCTION _update_manager_path() RETURNS TRIGGER AS
$$
BEGIN
    IF NEW.manager_id IS NULL THEN
        NEW.path = NEW.id::text::ltree;
    ELSE
        SELECT path || NEW.id::text
        FROM corp_companyuserprofile
        WHERE NEW.manager_id IS NULL or id = NEW.manager_id
        INTO NEW.path;
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- function to update the path of the descendants of a c.u.p.
CREATE OR REPLACE FUNCTION _update_descendants_manager_path() RETURNS TRIGGER AS
$$
BEGIN
    UPDATE corp_companyuserprofile
    SET path = NEW.path || subpath(corp_companyuserprofile.path, nlevel(OLD.path))
    WHERE corp_companyuserprofile.path <@ OLD.path AND id != NEW.id;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;


-- calculate the path every time we insert a new c.u.p.
DROP TRIGGER IF EXISTS cup_path_insert_trg ON corp_companyuserprofile;
CREATE TRIGGER cup_path_insert_trg
            BEFORE INSERT ON corp_companyuserprofile
            FOR EACH ROW
            EXECUTE PROCEDURE _update_manager_path();

-- calculate the path when updating the manager or the comp_user_id
DROP TRIGGER IF EXISTS cup_path_update_trg ON corp_companyuserprofile;
CREATE TRIGGER cup_path_update_trg
            BEFORE UPDATE ON corp_companyuserprofile
            FOR EACH ROW
            WHEN (OLD.manager_id IS DISTINCT FROM NEW.manager_id
                    OR OLD.comp_user_id IS DISTINCT FROM NEW.comp_user_id)
            EXECUTE PROCEDURE _update_descendants_manager_path();

-- if the path was updated, update the path of the descendants
DROP TRIGGER IF EXISTS cup_path_after_trg ON corp_companyuserprofile;
CREATE TRIGGER cup_path_after_trg
            AFTER UPDATE ON corp_companyuserprofile
            FOR EACH ROW
            WHEN (NEW.path IS DISTINCT FROM OLD.path)
            EXECUTE PROCEDURE _update_descendants_manager_path();

Однако это не работает, поскольку столбец пути обновляется только значением id той же записи.

Кроме того, эти триггеры очень сильно замедляют (как и следовало ожидать) операцию upsert.

Поскольку эта таблица должна обновляться после импорта данных, поэтому после масштабного восстановления в идеале я хотел бы, чтобы после ее завершения была вызвана функция, чтобы (заново) построить столбец "путь" для каждой записи, что означает восстановить дерево.

Любое предложение для достижения или фрагменты о том, как это сделать? не найти какой-либо действительный пример "перестроить дерево в postgres" в официальных документах.

Спасибо

0 ответов

Другие вопросы по тегам