【问题标题】:How to sort parents and all siblings using CTE for adjacency list?如何使用 CTE 对邻接列表的父母和所有兄弟姐妹进行排序?
【发布时间】:2021-06-24 09:02:52
【问题描述】:

这是我的桌子:

CREATE TABLE IF NOT EXISTS NODE 
(
    UUID VARCHAR NOT NULL, 
    PARENT_UUID VARCHAR NULL,
    NAME VARCHAR NOT NULL, 
    PRIMARY KEY (UUID)
);

这是我的测试数据:

INSERT INTO node (uuid, parent_uuid, name)
VALUES 
('dfca05bc-551d-4e3d-87aa-7dd7d29539f6', null, 'Computers'),
('ff83eb99-ea2c-4d11-8ebe-4600445a3bda', null, 'Food'),
('405f0267-fa22-4cac-a397-c430be221828', null, 'Drinks'),
('e79ecefa-c3e2-400f-aab4-2d28fcd3a832', 'dfca05bc-551d-4e3d-87aa-7dd7d29539f6', 'Monitors'),
('61a94b77-56c2-48ff-b869-39305648a25c', 'dfca05bc-551d-4e3d-87aa-7dd7d29539f6', 'System blocks'),
('5d88a9b7-7ffe-45e5-b35f-9350072ed619', 'dfca05bc-551d-4e3d-87aa-7dd7d29539f6', 'Mother boards'),
('5994c39d-c4ea-454c-ae57-118392b93f66', 'dfca05bc-551d-4e3d-87aa-7dd7d29539f6', 'Different'),
('d1c994fe-f3ec-40ed-aace-5221c026c0ea', '5994c39d-c4ea-454c-ae57-118392b93f66', 'Keyboards'),
('7757aa9b-abee-4d30-89d3-79f77613b5e8', '5994c39d-c4ea-454c-ae57-118392b93f66', 'Mice'),
('33d93c3a-1c2d-44b9-8fac-3f83074104a5', '5994c39d-c4ea-454c-ae57-118392b93f66', 'Joysticks'),
('f13bb023-47b2-473a-83b0-4223ff6e28b9', 'e79ecefa-c3e2-400f-aab4-2d28fcd3a832', 'Size 14'),
('312a4e56-71ef-4372-a556-17ace15197e6', 'e79ecefa-c3e2-400f-aab4-2d28fcd3a832', 'Size 15'),
('c525374c-6a06-46e4-98c8-bc669e811e22', 'e79ecefa-c3e2-400f-aab4-2d28fcd3a832', 'Size 16');

我想得到 a) 所有父母和他们的兄弟姐妹 b) 节点的所有兄弟姐妹。例如,如果我选择了 Joysticks 节点,那么我想得到

  • 电脑
    • 不同
      • 操纵杆
      • 键盘
      • 老鼠
    • 监视器
    • 主板
    • 系统块
  • 饮料
  • 食物

如您所见,未选择属于监视器的大小 14、15、16 节点。

到目前为止,我有以下查询:

WITH RECURSIVE theparents (uuid, parent_uuid, name, level) AS (
        SELECT uuid, parent_uuid, name, 0 AS level, 
        FROM node 
        WHERE uuid = '33d93c3a-1c2d-44b9-8fac-3f83074104a5'

        UNION ALL

        SELECT a.uuid, a.parent_uuid, a.name, b.level + 1 as level
        FROM node a
        INNER JOIN theparents b ON b.parent_uuid = a.uuid
    ),
thesiblings AS (
    SELECT a.uuid, a.parent_uuid, a.name, b.level as level
    FROM node a
    INNER JOIN theparents b ON b.parent_uuid = a.parent_uuid OR (b.parent_uuid IS NULL AND a.parent_uuid IS NULL)
)
SELECT * FROM thesiblings;

此查询选择所有节点,但不进行排序。是否可以使用 sql 对它们进行排序,或者只能手动完成?

【问题讨论】:

  • 您可以向上或向下递归,但不能同时进行。 (除非你可以嵌套递归 CTE)

标签: sql tree h2 common-table-expression adjacency-list


【解决方案1】:

您可以将tree 加入node。唯一的问题是保持它根据树遍历顺序排序。试试这个,在 MySql 8.0 中测试过

编辑

现在按基于名称的路径排序,20 是表中的最大名称长度

WITH RECURSIVE tree (uuid, parent_uuid, name, level, path) AS 
(
    SELECT uuid, parent_uuid, name, 0 level, cast(Rpad(name, 20, ' ') as char(200)) path
    FROM nodes 
    WHERE uuid = '33d93c3a-1c2d-44b9-8fac-3f83074104a5'

    UNION ALL

    SELECT a.uuid, a.parent_uuid, a.name, level-1,  concat(Rpad(a.name, 20, ' '),'>', path)
    FROM nodes a
    INNER JOIN tree b ON b.parent_uuid = a.uuid
)
select uuid, name, level /*, path */
from (
   select n.uuid, n.name
      , max(-level) over() + level + 1  level
      , concat(substring(first_value(path) over(order by level), 1, (20+1) * (max(-level) over() + level +1 )), n.name) path
   from tree t
   join nodes n on n.parent_uuid = t.uuid 
       -- no children for starting node
       and t.level <> 0
   
   union all
   --  roots
   select uuid, name, 0, Rpad(name, 20, ' ')
   from nodes
   where parent_uuid is null 
) t
order by path

db<>fiddle

【讨论】:

  • 在我的示例中,所有兄弟姐妹都按名称排序。但是我在您的解决方案中看不到这种排序。您的代码中的排序似乎很混乱。你能解释一下吗?
  • 您可以使用基于名称的路径而不是我的基于 uuid 的路径,前提是名称是唯一的。为简单起见,您可以将名称填充为固定长度,并在我的查询中使用此长度而不是 length(n.uuid)
  • 非常感谢。很不错。如果可以取消“20”限制。我的意思是,最好有一个更通用的解决方案,它没有任何名称限制(可以使用任何名称)
  • 您可以使用模式字典中的列长度。不熟悉h2实现,抱歉。
  • 我的意思是应该有任何限制,而不是来自模式字典,没有手动设置->没有它们的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-21
  • 2015-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多