mysql - 检索所有子类别 - sql 查询

标签 mysql sql recursive-query

我将类别存储在一个表中。

child 的数量没有限制。

我想为提供的类别 ID 获取所有链接的子类别:

获取层次结构的原因是我需要为新创建或更新的每个类别更新路径字段。我需要维护路径字段

表名:类别

id  parentId    name    path                         
A1   null       Cat 1   Cat 1                   
A2   A1         Cat 2   Cat 1 > Cat 2           
A3   A2         Cat 3   Cat 1 > Cat 2 > Cat 3   
A4   null       Cat A   Cat A                   
A5   A4         Cat B   Cat A > Cat B           

现在我想获取 id: 1

的层次结构

到目前为止我尝试过的是:

with recursive cte (id, name, parentId) AS (
    select
        id,
        name,
        parentId
    from
        categories
    where
        parentId = 'A1'
    union
    all
    select
        c.id,
        c.name,
        c.parentId
    from
        categories c
        inner join cte on c.parentId = cte.id
)
select
    *
from
    cte;

上面的查询返回:

[
    {
        id: A1,
        parentId: null,
        name: Cat 1,
        path: Cat 1
    },
    {
        id: A2,
        parentId: A1,
        name: Cat 2,
        path: Cat 1 > Cat 2
    }
]

但我想要这个:

[
    {
        id: A2,
        parentId: A1,
        name: Cat 2,
        path: Cat 1 > Cat 2
    },
    {
        id: A3,
        parentId: A2,
        name: Cat 3,
        path: Cat 1 > Cat 2 > Cat 3
    }
]

如果我提供 id: 2,那么我期望:

[
    {
        id: A3,
        parentId: A2,
        name: Cat 3,
        path: Cat 1 > Cat 2 > Cat 3
    }
]

我在查询中做错了什么,任何人都可以识别吗?

这是重现的场景:https://dbfiddle.uk/Beefs-UH

重要说明: 主键即 id 是唯一标识符字符串而不是整数。所以记录不能按id排序。

最佳答案

要获取上层结构,请尝试以下操作:

with recursive cte (id, name, parentId, path, ord) as 
(
  select id, name, parentId, path, 1 as ord
  from  categories
  where id = 'A2'
  union all
  select c.id, c.name, c.parentId, c.path, t.ord+1
  from categories c join cte t
  on t.parentId = c.id 
)
select * from cte
order by ord desc;

在这里,我们按生成的 'ord' 值对结果进行排序,您不需要按 id 排序。

要获得向下层次结构,请使用以下内容:

with recursive cte (id, name, parentId, path, ord) as 
(
  select id, name, parentId, path, 1 as ord
  from  categories
  where id = 'A2'
  union all
  select c.id, c.name, c.parentId, c.path, t.ord+1
  from categories c join cte t
  on c.parentId = t.id 
)
select * from cte
order by ord;

要获得完整的层次结构(向上和向下),您可以合并两个递归查询的结果,如下所示:

with recursive cte(id, name, parentId, path, ord) as 
(
  select id, name, parentId, path, 1 as ord
  from  categories
  where id = 'A2'
  union all
  select c.id, c.name, c.parentId, c.path, t.ord+1
  from categories c join cte t
  on t.parentId = c.id 
),
cte2(id, name, parentId, path, ord) as 
(
  select id, name, parentId, path,0 as ord 
  from  categories
  where id = 'A2'
  union all
  select c.id, c.name, c.parentId, c.path, ord-1
  from categories c join cte2 t
  on c.parentId = t.id 
)
select id, name, parentId, path from
(
  select *  from cte
  union all
  select *  from cte2
) T
where ord <> 0 /*to avoid id = 'A2' duplication (one from cte and one from cte2)*/
order by ord desc

由于您要存储每个 ID 的完整路径,您可以尝试使用以下自连接的另一种方法:

对于上层:

select C1.id, C1.parentId, C1.name, C1.path
from categories C1 join categories C2
on C2.path like CONCAT('%', C1.name, '%')
where C2.id='A2'
order by length(C1.path)

这里,我们使用length(path)对结果进行排序,这样可以保证最上层的父节点先出现,最低的子节点最后出现。

对于向下层次结构,在 C1.path 上使用 ,如 CONCAT('%', C2.name, '%')

对于完整的层次结构,在 C1.path 上使用 like CONCAT('%', C2.name, '%') 或 C2.path like CONCAT('%', C1.name, '%').

查看 demo on MySQL .

查看 demo on SQL Server (稍作修改)。

关于mysql - 检索所有子类别 - sql 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74404771/

相关文章:

PHP 在类中安装方法需要数据库表/列

java - Java Netbeans中的JFrame中的数据未更新

c# - mysql和mssql之间来回发送数据

MySql 在一个查询中获取逗号分隔的主键

mysql - 你如何用sql语句计算树的深度?

sql - 首次记录后 31 天内的群组记录

PHP将mysql查询字符串转换为mm :ss time format

PHPExcelReader 无法写入数据库

mysql - 复合 Mysql join sql 中每组所需的最大 n

sql - 使用递归公用表表达式从两个表中查找连续编号