在试图找到解决这个问题的合适解决方案并几乎完全拔掉我的头发后,我决定前来寻求帮助。
问题:
我有一个包含4列的表格,如下所示:
id | family_id | parent_id | age |
-------------------------------------
1 | 1 | 0 | 45 |
2 | 1 | 7 | 23 |
3 | 1 | 0 | 59 |
4 | 1 | 5 | 12 |
5 | 1 | 1 | 27 |
6 | 1 | 7 | 18 |
7 | 1 | 1 | 30 |
8 | 1 | 1 | 32 |
9 | 1 | 6 | 9 |
使用CTE我找出孩子是父母的人,并通过链接到父母的路径对他们进行排序。像这样:
WITH RECURSIVE cte (id, path, family_id, parent_id, age) AS (
SELECT id, array[id] AS path, family_id, parent_id, age
FROM test
WHERE parent_id=0
AND family_id=1
UNION ALL
SELECT test.id,
cte.path || test.id,
test.family_id,
test.parent_id,
test.age
FROM test
JOIN cte ON test.parent_id = cte.id
)
SELECT id, path, family_id, parent_id, age
FROM cte
ORDER BY path;
结果,一个由父级及其子级排序的漂亮表。
id | path |family_id | parent_id | age |
-------------------------------------------------
1 | 1 | 1 | 0 | 45 |
2 | 1,2 | 1 | 1 | 30 |
3 | 1,3 | 1 | 1 | 27 |
4 | 1,4 | 1 | 1 | 32 |
5 | 5 | 1 | 0 | 59 |
6 | 5,6 | 1 | 5 | 12 |
7 | 5,6,7 | 1 | 6 | 9 |
8 | 5,6,7,8 | 1 | 7 | 18 |
9 | 5,6,7,9 | 1 | 7 | 23 |
最终结果应该是这样的:
id | path |family_id | parent_id | age |
-------------------------------------------------
1 | 5 | 1 | 0 | 59 |
2 | 5,6 | 1 | 5 | 12 |
3 | 5,6,7 | 1 | 6 | 9 |
4 | 5,6,7,8 | 1 | 7 | 23 |
5 | 5,6,7,9 | 1 | 7 | 18 |
6 | 1 | 1 | 0 | 45 |
7 | 1,2 | 1 | 1 | 32 |
8 | 1,3 | 1 | 1 | 30 |
9 | 1,4 | 1 | 1 | 27 |
此解决方案无效:
ORDER BY path, age;
<小时/> SQL Fiddle中提供了一个演示: SQLFIDDLE DEMO
答案 0 :(得分:1)
这个不是那么优雅,但似乎有效:
JSON
http://sqlfiddle.com/#!17/328ac/109
使用WITH RECURSIVE cte (id, path, family_id, parent_id, age, sort_col) AS (
SELECT id, array[id] AS path, family_id, parent_id, age, array[age, -id]
FROM test
WHERE parent_id=0
AND family_id=1
UNION ALL
SELECT test.id,
cte.path || test.id,
test.family_id,
test.parent_id,
test.age,
cte.sort_col || test.age || -test.id
FROM test
JOIN cte ON test.parent_id = cte.id
)
SELECT id, path, family_id, parent_id, age, array_append(sort_col, 999)
FROM cte
ORDER BY array_append(sort_col, 999) desc, path;
创建一个列(数组),但使用path
代替age
。对于路径id
,此列将为5,6,7
。对于关系,您还应该附加59,12,9
(或id
- 取决于您是希望较低还是较高的一个出现。现在列将是-id
。最后一步:在主语句中附加59,-5,12,-6,9,-7
- 这样父节点将始终位于子节点之前(假设没有人比那个( - :)更早。
结果是:
999
请注意,我仅出于演示原因选择id | path | family_id | parent_id | age | array_append
---|---------|-----------|-----------|-----|---------------------------
5 | 5 | 1 | 0 | 59 | 59,-5,999
6 | 5,6 | 1 | 5 | 12 | 59,-5,12,-6,999
7 | 5,6,7 | 1 | 6 | 9 | 59,-5,12,-6,9,-7,999
9 | 5,6,7,9 | 1 | 7 | 23 | 59,-5,12,-6,9,-7,23,-9,999
8 | 5,6,7,8 | 1 | 7 | 18 | 59,-5,12,-6,9,-7,18,-8,999
1 | 1 | 1 | 0 | 45 | 45,-1,999
4 | 1,4 | 1 | 1 | 32 | 45,-1,32,-4,999
2 | 1,2 | 1 | 1 | 30 | 45,-1,30,-2,999
3 | 1,3 | 1 | 1 | 27 | 45,-1,27,-3,999
。您可以从SELECT子句中删除它。
答案 1 :(得分:0)
WITH RECURSIVE cte (id, path, family_id, parent_id, age, parent_path, root_age) AS (
SELECT id, array[id] AS path, family_id, parent_id, age,
NULL::integer[] as parent_path, age as root_age
FROM test
WHERE parent_id=0
AND family_id=1
UNION ALL
SELECT test.id,
cte.path || test.id,
test.family_id,
test.parent_id,
test.age,
cte.path AS parent_path,
cte.root_age AS root_age
FROM test
JOIN cte ON test.parent_id = cte.id
)
SELECT id, path, family_id, parent_id, age, parent_path, root_age
FROM cte
ORDER BY root_age DESC, coalesce(parent_path, path), parent_path IS NULL DESC, age DESC;
对不起我的原始答案。
对于解决方案,需要一个没有自我ID的路径。这是&#34; parent_path&#34;领域。 使用此列,排序现在很简单。 例外是没有parent_path的行,即root,其中parent_path为null。要对此进行排序,需要coalesce()和parent_path为null desc。
要订购根元素,您需要一个&#34; root_age&#34;领域。这应该启动ORDER BY子句。