gpt4 book ai didi

sql - 使用 PostgreSQL ltree 对节点进行排序

转载 作者:行者123 更新时间:2023-11-29 14:32:19 43 4
gpt4 key购买 nike

假设我使用 ltree 存储了一棵树:

   id   |   path   |   sort   
------------------------------
0 |0 |1
1 |0.1 |2
2 |0.1.2 |3
3 |0.1.3 |1
4 |0.1.4 |2
5 |0.5 |3
6 |0.6 |1

我想选择节点以便:

  1. 子节点出现在父节点之后;
  2. 具有较小“排序”值的兄弟节点首先出现;

像这样:

   id   |   path   |   sort   
------------------------------
0 |0 |1
6 |0.6 |1
1 |0.1 |2
3 |0.1.3 |1
4 |0.1.4 |2
2 |0.1.2 |3
5 |0.5 |3

第一个要求可以通过 ORDER BY path 实现,但我不知道如何实现第二个,这是否可能?

最佳答案

我正在用第二个 ltree sort_path 和一些触发器来解决这个问题。

最终,您将在 sort_path 树上排序,其值基于所有祖先的排序列的 lpad 加上当前行的排序列的 lpad。

   id   |   path   |   rank   |  sort_path
--------------------------------------------
0 |0 |1 | 0001
6 |0.6 |1 | 0001.0001
1 |0.1 |2 | 0001.0002
3 |0.1.3 |1 | 0001.0002.0001
4 |0.1.4 |2 | 0001.0002.0002
2 |0.1.2 |3 | 0001.0002.0003
5 |0.5 |3 | 0001.0003

顺便说一句,即使您拥有的简单路径排序也不是很正确,一旦您遇到两位数的路径段,您就会遇到麻烦,因为路径排序是基于字母的,而不是数字。

请注意,完整的解决方案包括一个触发器,当任何父节点行的排序值发生更改时,它会重新计算所有后代的 sort_path 值。

示例实现:

CREATE EXTENSION IF NOT EXISTS ltree;

CREATE TABLE tree_nodes (path LTREE, rank INT, sort_path LTREE);

CREATE OR REPLACE FUNCTION calc_sort_path(tree_path LTREE, sibling_rank INT) RETURNS LTREE AS $$
DECLARE
sort_ranks TEXT[];
sort_path LTREE;
ancestor RECORD;
BEGIN
-- Default to the segment text (prepended with underscore).
-- If some ancestors are missing, this ensures the children will still sort together.
FOR iterator IN 1..NLEVEL(tree_path) LOOP
sort_ranks[iterator] := '_' || SUBPATH(tree_path, iterator-1, 1)::TEXT;
END LOOP;
-- Format a sort rank path segment for each ancestor.
FOR ancestor IN
SELECT NLEVEL(tree_nodes.path) AS level, tree_nodes.rank FROM tree_nodes
WHERE tree_nodes.path @> tree_path AND tree_nodes.path != tree_path
LOOP
sort_ranks[ancestor.level] := LPAD(ancestor.rank::TEXT, 4, '0');
END LOOP;
-- Format a final sort rank path segment for this leaf node.
sort_ranks[NLEVEL(tree_path)] := LPAD(sibling_rank::TEXT, 4, '0');
-- Convert array to LTREE path.
SELECT STRING_AGG(padded_rank, '.')::LTREE INTO sort_path FROM
(SELECT UNNEST(sort_ranks) AS padded_rank) path_ranks;

RETURN sort_path;
END
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION update_sort_paths() RETURNS trigger AS $$
DECLARE
has_changed BOOLEAN;
BEGIN
has_changed := TG_OP = 'UPDATE' AND (OLD.path IS DISTINCT FROM NEW.path OR OLD.rank IS DISTINCT FROM NEW.rank);
IF (TG_OP = 'DELETE' OR has_changed) THEN
UPDATE tree_nodes SET sort_path = calc_sort_path(path, rank) WHERE OLD.path @> path;
END IF;
IF (TG_OP = 'INSERT' OR has_changed) THEN
UPDATE tree_nodes SET sort_path = calc_sort_path(path, rank) WHERE NEW.path @> path;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS on_rank_change ON tree_nodes;
CREATE TRIGGER on_rank_change AFTER INSERT OR UPDATE OR DELETE ON tree_nodes
FOR EACH ROW EXECUTE PROCEDURE update_sort_paths();

关于sql - 使用 PostgreSQL ltree 对节点进行排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49850810/

43 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com