Dav*_*ard 9 database postgresql tree hierarchy
我正在使用PostgreSQL的Ltree模块来存储分层数据.我希望检索按特定列排序的完整层次结构.
请考虑下表:
votes | path | ...
-------+-------+-----
1 | 1 | ...
2 | 1.1 | ...
4 | 1.2 | ...
1 | 1.2.1 | ...
3 | 2 | ...
1 | 2.1 | ...
2 | 2.1.1 | ...
4 | 2.1.2 | ...
... | ... | ...
Run Code Online (Sandbox Code Playgroud)
在我当前的实现中,我将查询数据库SELECT * FROM comments ORDER BY path,它将返回整个树:
Node 1
-- Node 1.1
-- Node 1.2
---- Node 1.2.1
Node 2
-- Node 2.1
---- Node 2.1.1
---- Node 2.1.2
Run Code Online (Sandbox Code Playgroud)
但是,我想排序votes(而不是id,按path数量排序).每个深度级别都需要独立排序,正确的树形结构保持不变.会返回以下内容的东西:
Node 2
-- Node 2.1
---- Node 2.1.2
---- Node 2.1.1
Node 1
-- Node 1.2
---- Node 1.2.1
-- Node 1.1
Run Code Online (Sandbox Code Playgroud)
Postgres' WITH RECURSIVE可能合适,但我不确定.有任何想法吗?
Erw*_*ter 12
你走在正确的轨道上WITH RECURSIVE.
WITH RECURSIVE t AS (
SELECT t.votes
, t.path
, 1::int AS lvl
, to_char(t2.votes, 'FM0000000') AS sort
FROM tbl t
JOIN tbl t2 ON t2.path = subltree(t.path, 0, 1)
UNION ALL
SELECT t.votes
, t.path
, t.lvl + 1
, t.sort || to_char(t2.votes, 'FM0000000')
FROM t
JOIN tbl t2 ON t2.path = subltree(t.path, 0, t.lvl + 1)
WHERE nlevel(t.path) > t.lvl
)
SELECT votes, path, max(sort) AS sort
FROM t
GROUP BY 1, 2
ORDER BY max(sort), path;
Run Code Online (Sandbox Code Playgroud)
关键部分是用值替换路径的每个级别votes.因此,我们最后组装了一列ORDER BY.这是必要的,因为路径具有未知深度,我们无法通过静态SQL中的未知数量的表达式进行排序.
为了获得稳定的排序,我转换votes为使用前导零的字符串to_char().我在演示中使用七位数,适用于低于10.000.000的投票值.根据您的最高投票数量进行调整.
在最后SELECT我排除所有中间状态以消除重复.只剩下最后一步了max(sort).
这适用于带有递归CTE的标准SQL,但对于大型树来说效率不高.plpgsql函数以递归方式更新临时表中的排序路径而不创建临时欺骗可能会表现得更好.
仅适用于安装的ltree模块.函数subltree(...)和nlevel(.)以及ltree日期类型不是标准PostgreSQL的一部分.
我的测试设置,方便您查看:
CREATE TEMP TABLE tbl(votes int, path ltree);
INSERT INTO tbl VALUES
(1, '1')
, (2, '1.1')
, (4, '1.2')
, (1, '1.2.1')
, (3, '2')
, (1, '2.1')
, (2, '2.1.1')
, (4, '2.1.2')
, (1, '2.1.3')
, (2, '3')
, (17, '3.3')
, (99, '3.2')
, (10, '3.1.1')
, (2345, '3.1.2')
, (1, '3.1.3');
Run Code Online (Sandbox Code Playgroud)
大树应该更快.
CREATE OR REPLACE FUNCTION f_sorted_ltree()
RETURNS TABLE(votes int, path ltree) AS
$func$
DECLARE
lvl integer := 0;
BEGIN
CREATE TEMP TABLE t ON COMMIT DROP AS
SELECT tbl.votes
, tbl.path
, ''::text AS sort
, nlevel(tbl.path) AS depth
FROM tbl;
-- CREATE INDEX t_path_idx ON t (path); -- beneficial for huge trees
-- CREATE INDEX t_path_idx ON t (depth);
LOOP
lvl := lvl + 1;
UPDATE t SET sort = t.sort || to_char(v.votes, 'FM0000000')
FROM (
SELECT t2.votes, t2.path
FROM t t2
WHERE t2.depth = lvl
) v
WHERE v.path = subltree(t.path, 0 ,lvl);
EXIT WHEN NOT FOUND;
END LOOP;
-- Return sorted rows
RETURN QUERY
SELECT t.votes, t.path
FROM t
ORDER BY t.sort;
END
$func$ LANGUAGE plpgsql VOLATILE;
Run Code Online (Sandbox Code Playgroud)
呼叫:
SELECT * FROM f_sorted_ltree();
Run Code Online (Sandbox Code Playgroud)
请阅读有关设置temp_buffers的手册.
我很感兴趣,它可以更快地显示你的真人生活数据.
| 归档时间: |
|
| 查看次数: |
2490 次 |
| 最近记录: |