按年龄和CTE对父母和子女进行亚目排列

时间:2017-10-10 19:29:50

标签: mysql postgresql recursion common-table-expression

在试图找到解决这个问题的合适解决方案并几乎完全拔掉我的头发后,我决定前来寻求帮助。

问题:
我有一个包含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

2 个答案:

答案 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子句。