对于 MySQL 8+: 使用递归 with 语法。
对于 MySQL 5.x: 使用内联变量、路径 ID 或自连接。
MySQL 8+
with recursive cte (id, name, parent_id) as (
select id,
name,
parent_id
from products
where parent_id = 19
union all
select p.id,
p.name,
p.parent_id
from products p
inner join cte
on p.parent_id = cte.id
)
select * from cte;
parent_id = 19 中指定的值应设置为要选择其所有后代的父级的id。
MySQL 5.x
对于不支持公用表表达式的 MySQL 版本(最高 5.7 版),您可以通过以下查询实现此目的:
select id,
name,
parent_id
from (select * from products
order by parent_id, id) products_sorted,
(select @pv := '19') initialisation
where find_in_set(parent_id, @pv)
and length(@pv := concat(@pv, ',', id))
这是fiddle。
这里,@pv := '19' 中指定的值应设置为要选择其所有后代的父级的id。
如果父母有 多个 孩子,这也适用。但要求每条记录都满足条件parent_id < id,否则结果不完整。
查询中的变量赋值
此查询使用特定的 MySQL 语法:在执行期间分配和修改变量。对执行顺序做了一些假设:
- 首先评估
from 子句。这就是 @pv 被初始化的地方。
-
where 子句按照从 from 别名中检索的顺序对每条记录进行评估。因此,这里设置了一个条件,只包括其父级已被识别为在后代树中的记录(主要父级的所有后代逐渐添加到@pv)。
- 此
where 子句中的条件按顺序评估,一旦确定总结果,评估就会中断。因此,第二个条件必须排在第二位,因为它将id 添加到父列表中,并且只有当id 通过第一个条件时才会发生这种情况。 length 函数仅被调用以确保此条件始终为真,即使 pv 字符串由于某种原因会产生虚假值。
总而言之,人们可能会发现依赖这些假设太冒险了。 documentation 警告:
您可能会得到您期望的结果,但这不能保证 [...] 涉及用户变量的表达式的求值顺序是未定义的。
因此,即使它与上述查询一致,评估顺序仍可能发生变化,例如当您添加条件或将此查询用作较大查询中的视图或子查询时。 will be removed in a future MySQL release:
以前的 MySQL 版本可以在 SET 以外的语句中为用户变量赋值。 MySQL 8.0 支持此功能以实现向后兼容性,但可能会在 MySQL 的未来版本中删除。
如上所述,从 MySQL 8.0 开始,您应该使用递归 with 语法。
效率
对于非常大的数据集,此解决方案可能会变慢,因为find_in_set 操作不是在列表中查找数字的最理想方法,当然不是在达到与返回的记录数。
备选方案 1:with recursive、connect by
越来越多的数据库为递归查询实现SQL:1999 ISO standard WITH [RECURSIVE] syntax(例如Postgres 8.4+、SQL Server 2005+、DB2、Oracle 11gR2+、SQLite 3.8.4+、Firebird 2.1+、H2、HyperSQL 2.1.0+、@ 987654336@,MariaDB 10.2.2+)。截至version 8.0, also MySQL supports it。请参阅此答案的顶部以了解要使用的语法。
某些数据库具有用于分层查找的替代非标准语法,例如 Oracle、DB2、Informix、CUBRID 和其他数据库上可用的 CONNECT BY 子句。
MySQL 5.7 版不提供这样的功能。如果您的数据库引擎提供了这种语法,或者您可以迁移到提供这种语法的引擎,那么这无疑是最好的选择。如果不是,那么还要考虑以下替代方案。
备选方案 2:路径样式标识符
如果您分配包含分层信息的id 值,事情会变得容易得多:路径。例如,在您的情况下,这可能如下所示:
| ID |
NAME |
| 19 |
category1 |
| 19/1 |
category2 |
| 19/1/1 |
category3 |
| 19/1/1/1 |
category4 |
那么您的select 将如下所示:
select id,
name
from products
where id like '19/%'
备选方案 3:重复自联接
如果您知道层次结构树的深度上限,则可以使用标准的sql 查询,如下所示:
select p6.parent_id as parent6_id,
p5.parent_id as parent5_id,
p4.parent_id as parent4_id,
p3.parent_id as parent3_id,
p2.parent_id as parent2_id,
p1.parent_id as parent_id,
p1.id as product_id,
p1.name
from products p1
left join products p2 on p2.id = p1.parent_id
left join products p3 on p3.id = p2.parent_id
left join products p4 on p4.id = p3.parent_id
left join products p5 on p5.id = p4.parent_id
left join products p6 on p6.id = p5.parent_id
where 19 in (p1.parent_id,
p2.parent_id,
p3.parent_id,
p4.parent_id,
p5.parent_id,
p6.parent_id)
order by 1, 2, 3, 4, 5, 6, 7;
看到这个fiddle
where 条件指定您要检索其后代的父代。您可以根据需要将此查询扩展为更多级别。