mysql 递归查询理解

发布时间 2024-01-12 21:04:16作者: mofy

mysql支持递归查询在8.0版本,以前的不知道支不支持,在官网查询的是文档是8.0版本的。

这里以树形结构来做个备注,以备之后查询理解使用

以官网例子说明:

mysql> SELECT * FROM employees ORDER BY id;
+------+---------+------------+
| id   | name    | manager_id |
+------+---------+------------+
|   29 | Pedro   |        198 |
|   72 | Pierre  |         29 |
|  123 | Adil    |        692 |
|  198 | John    |        333 |
|  333 | Yasmina |       NULL |
|  692 | Tarek   |        333 |
| 4610 | Sarah   |         29 |
+------+---------+------------+

官方给出的例子如下:

WITH RECURSIVE employee_paths (id, name, path) AS
(
  SELECT id, name, CAST(id AS CHAR(200))
    FROM employees
    WHERE manager_id IS NULL
  UNION ALL
  SELECT e.id, e.name, CONCAT(ep.path, ',', e.id)
    FROM employee_paths AS ep JOIN employees AS e
      ON ep.id = e.manager_id
)
SELECT * FROM employee_paths ORDER BY path;

查询结果如下:

+------+---------+-----------------+
| id   | name    | path            |
+------+---------+-----------------+
|  333 | Yasmina | 333             |
|  198 | John    | 333,198         |
|   29 | Pedro   | 333,198,29      |
| 4610 | Sarah   | 333,198,29,4610 |
|   72 | Pierre  | 333,198,29,72   |
|  692 | Tarek   | 333,692         |
|  123 | Adil    | 333,692,123     |
+------+---------+-----------------+

生成的过程如下:

1.根据with递归语句中的第一个select语句,产生初始行(即第一行),即上述结果中id为333的那行,同时确定path的长度为char(200),即生成表的格式和列名都是由第一行确定的,如果with后面没有传递列名的话。

 2.之后的每行生成都是由with语句的第二个select语句,第二个select语句查询原始表employees,查询manage_id为第一行生成的id为333的员工,并根据第二个select语句将原始表的id,name,path显示出来,即生成结果集中的第二行,id为198的那条数据

3.之后重复执行第二步,直到根据上一个查询结果,在原始表中找不到manage_id为123的结果,即退出递归查询。

如果要查询筛选结果,在select语句中增加一个where筛选语句即可。

mysql> WITH RECURSIVE ...
       ...
       SELECT * FROM employees_extended
       WHERE id IN (692, 4610)
       ORDER BY path;
+------+-------+-----------------+
| id   | name  | path            |
+------+-------+-----------------+
| 4610 | Sarah | 333,198,29,4610 |
|  692 | Tarek | 333,692         |
+------+-------+-----------------+

 

 关于with语句的理解,因为在使用过程中关于第二条语句select后面的列名是使用e还是ep,还有on后面的条件是e.id还是ep.id比较迷惑,故做个备注如下

WITH RECURSIVE employee_paths (id, name, path) AS
(
  SELECT id, name, CAST(id AS CHAR(200))
//非递归select生成初始列,即第一列,同时确定列名和列格式
FROM employees WHERE manager_id IS NULL UNION ALL SELECT e.id, e.name, CONCAT(ep.path, ',', e.id) FROM employee_paths AS ep
// 递归select生成第二列和之后的所有列,根据select后面的字段来计算生成之后的每条数据
// ep即第二步生成查询中需要的的上一条语句生成结果 // 以生成表第二行数据为例,第二步会生成第二行,生成过程中的ep即为第一条数据,生成过程中的e即为查询表数据即employees JOIN employees AS e // 生成的第二条数据,需要查询表中的e.manager_id等于第一条数据中的ep.id ON ep.id = e.manager_id ) SELECT * FROM employee_paths ORDER BY path;

 

官方文档资料链接:https://dev.mysql.com/doc/refman/8.0/en/with.html#common-table-expressions-recursive