背景
日常开发中经常会有那种 阶梯式 数据,比如做地图、菜单,裂变给上级、上上级分红等等这样的需求的时候
你需要找个一个对象的 上级,上上级,上上上级
建了一张很容易理解阶级的表,一目了然
很多时候我们的需求就是,通过任何一个人的id,找到它所有的上级,比如我们想通过儿子的id,找他的"族谱"
我们的确是可以用 递归 的思想去做这个事,用代码可以轻易地实现。与此同时我们也会发现,连接数据库的次数,也和我们递归的次数相同,这还是耗费资源的
还有没有其它的方法呢?这也是曾经一个面试官问我的问题,现在想来,当时的确没回答好,今天它来了
with recursive
它是什么?
MySQL with Recursive是一种基于递归思想的MySQL查询方式,可以实现对数据的递归查询和处理,返回符合条件的数据。在MySQL 8.0版本中,该功能被正式引入。
语法:
WITH RECURSIVE temp(column_list) AS (
SELECT query_list
UNION [ALL]
SELECT query_list
FROM table_name
WHERE condition
)
SELECT * FROM temp
解释:
WITH RECURSIVE:
表示要使用递归查询的方式处理数据
temp:
给这个临时的递归表取个名字,可以在初始查询和递归查询中引用
column_list:
表示 temp 查询表中包含的列名,列名之间用逗号分隔
UNION ALL
将两个查询结果集进行联合
以上面那张表为例,我想找到儿子的 “族谱”
WITH recursive temp AS (
SELECT
id,
`name`,
parent_id
FROM
person
WHERE
id = 3 UNION ALL
SELECT
p.id,
p.`name`,
p.parent_id
FROM
person p,
temp
WHERE
p.id = temp.parent_id
) SELECT
*
FROM
temp
我们把儿子的 id 作为条件,查询来的结果作为递归的条件,去从它自身开始,递归 向上 找它的 父亲,以及父亲的父亲。这就是这句 SQL的翻译
这是我们想要的结果:
如果想通过 “顶级父类”,查看它下面的 子集也可以,我们稍微改动一下我们SQL
WITH recursive temp AS (
SELECT
id,
`name`,
parent_id
FROM
person
WHERE
id = 1 UNION ALL
SELECT
p.id,
p.`name`,
p.parent_id
FROM
person p,
temp
WHERE
p.parent_id = temp.id
) SELECT
*
FROM
temp
思考第一个问题:
就是我们每次查询这个链条的时候只能是保证单边查询,要是能向两边延展查询就好了,比如我们输入 父亲的id,最终的结果是:父亲、儿子、爷爷、都能找到,而不是每次得到是向上或者向下单边结果集
思考第二个问题:
既然这里也是递归,那么层级要是有一定深度的情况,SQL的速度怎么样?
我将数据扩大了10倍,增加到了30多条,相较于之前的3条,执行时间还是没怎么增加,多次执行,取平均值,都在350 ms左右