我有一个MySQL表,如下所示:
id | name | parent_id
19 | category1 | 0
20 | category2 | 19
21 | category3 | 20
22 | category4 | 21
......
现在,我想要一个MySQL查询,我只提供id [例如说'id = 19']然后我应该得到它的所有子ID [即结果应该有ids '20,21,22'] ....而且,孩子的等级不知道它可以变化....
另外,我已经有了使用for循环的解决方案.....如果可能的话,让我知道如何使用单个MySQL查询来实现相同的功能 .
14 回答
如果您使用的是MySql 8,则使用递归
with
子句:parent_id = 19
中指定的值应设置为要选择所有后代的父级的id
.在MySql之前8
对于不支持公用表表达式(最高版本为5.7)的MySql版本,您可以使用以下查询来实现此目的:
这是fiddle .
这里,
@pv := '19'
中指定的值应设置为要选择所有后代的父级的id
.如果父母有多个孩子,这也可以 . 但是,要求每条记录满足条件
parent_id < id
,否则结果将不完整 .此查询使用特定的MySql语法:在执行期间分配和修改变量 . 对执行顺序做了一些假设:
首先评估
from
子句 . 这就是@pv
被初始化的地方 .where
子句按照从from
别名中检索的顺序对每条记录进行评估 . 因此,这是一个条件,只包括父项已被识别为在后代树中的记录(主要父项的所有后代逐渐添加到@pv
) .此
where
子句中的条件按顺序进行评估,一旦总结果确定,评估就会中断 . 因此,第二个条件必须位于第二位,因为它将id
添加到父列表,并且只有在id
传递第一个条件时才会发生这种情况 . 仅调用length
函数以确保此条件始终为true,即使pv
字符串由于某种原因会产生假值 .总而言之,人们可能会发现这些假设风险太大而无法依赖 - 它们没有文件保证,即使它一致地工作,当您将此查询用作视图或子视图时,评估顺序在理论上仍然可能会发生变化查询更大的查询 .
另请注意,对于非常大的数据集,此解决方案可能会变慢,因为
find_in_set
操作不是在列表中查找数字的最理想方式,当然不是在列表中达到与数字相同数量级的大小记录的返回 .Alternative 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+,Teradata,MariaDB 10.2.2+) . 截至version 8.0, also MySql supports it . 有关要使用的语法,请参阅此答案的顶部 .
某些数据库具有用于分层查找的替代非标准语法,例如Oracle和DB2数据库上可用的CONNECT BY子句 .
MySql 5.7版不提供这样的功能 . 当您的数据库引擎提供此语法时,那肯定是最佳选择 . 如果没有,那么还要考虑以下备选方案 .
Alternative 2: Path-style Identifiers
如果要分配包含分层信息的
id
值,事情会变得容易得多:路径 . 例如,在您的情况下,这可能如下所示:然后你的
select
看起来像这样:Alternative 3: Repeated Self-joins
如果您知道层次树可以变深的上限,则可以使用标准
sql
,如下所示:看到这个fiddle
where
条件指定要检索其后代的父项 . 您可以根据需要使用更多级别扩展此查询 .来自博客 Managing Hierarchical Data in MySQL
表结构
查询:
产量
大多数用户曾经在SQL数据库中处理过分层数据,毫无疑问,他们了解到分层数据的管理不是关系数据库的用途 . 关系数据库的表不是分层的(如XML),而只是一个平面列表 . 分层数据具有父子关系,这种关系不是自然的在关系数据库表中表示 . Read more
有关详细信息,请参阅博客 .
EDIT:
输出:
参考:How to do the Recursive SELECT query in Mysql?
这里有另一个问题做同样的事情
Mysql select recursive get all child with multiple level
查询将是:
试试这些:
表定义:
实验行:
递归存储过程:
存储过程的包装函数:
选择示例:
输出:
过滤具有特定路径的行:
输出:
我想出的最佳方法是
使用lineage存储\ sort \ trace树 . 这绰绰有余,阅读速度比其他任何方法快数千倍 . 它也允许保持该模式,即使DB将改变(因为任何数据库将允许使用该模式)
使用确定特定ID的谱系的函数 .
根据需要使用它(在选择中,或在CUD操作中,甚至通过作业) .
谱系方法描述 . 可以在任何地方找到,例如Here或here . 至于功能 - that是什么让我感动 .
最终 - 获得了或多或少的简单,相对快速和简单的解决方案 .
功能的身体
然后你就是
希望它有助于某人:)
如果您需要快速读取速度,最好的选择是使用闭包表 . 闭包表包含每个祖先/后代对的行 . 所以在你的例子中,闭包表看起来像
拥有此表后,分层查询变得非常简单快捷 . 获得所有类别20的后代:
当然,每当你使用像这样的非规范化数据时,都会有一个很大的缺点 . 您需要在类别表旁边维护闭包表 . 最好的方法可能是使用触发器,但正确跟踪闭包表的插入/更新/删除有点复杂 . 与任何事情一样,您需要查看您的要求并确定最适合您的方法 .
Edit :有关更多选项,请参阅问题What are the options for storing hierarchical data in a relational database? . 针对不同情况有不同的最佳解决方案 .
您可以使用递归查询(性能上的YMMV)轻松地在其他数据库中执行此操作 .
另一种方法是存储两个额外的数据位,左右值 . 左侧和右侧值来自您正在表示的树结构的预先遍历遍历 .
这称为Modified Preorder Tree Traversal,允许您运行简单查询以立即获取所有父值 . 它也被称为“嵌套集” .
简单查询列出第一次递归的子项:
结果:
...左连接:
@tincot的解决方案列出所有孩子的:
使用Sql Fiddle在线测试并查看所有结果 .
http://sqlfiddle.com/#!9/a318e3/4/0
它有点棘手,检查它是否适合你
SQL小提琴链接http://www.sqlfiddle.com/#!2/e3cdf/2
适当地替换您的字段和表名称 .
只需使用BlueM/tree php类在mysql中创建自关系表的树 .
以下是使用BlueM / tree的示例:
我发现它更容易:
1)创建一个函数,检查项是否在另一个项的父层次结构中的任何位置 . 像这样的东西(我不会写这个函数,用WHILE做它):
在你的例子中
2)使用子选择,如下所示:
这里没有提到的东西,虽然有点类似于接受的答案的第二个替代方案但是大层次查询和简单(插入更新删除)项目的不同且低成本,将为每个项目添加持久路径列 .
一些像:
例:
Optimise the path length and ORDER BY path using base36 encoding instead real numeric path id
https://en.wikipedia.org/wiki/Base36
通过使用固定长度和填充到编码的id来抑制斜杠'/'分隔符
详细的优化说明如下:https://bojanz.wordpress.com/2014/04/25/storing-hierarchical-data-materialized-path/
TODO
Build 一个功能或程序来分裂后遗症的祖先一个项目
这对我有用,希望这也适合你 . 它将为您提供任何特定菜单的记录设置Root to Child . 根据您的要求更改字段名称 .
我已经为你查了一下 . 这将为您提供单个查询的递归类别:
这是fiddle .