sql父子查询

分析sql过程

启发:https://blog.csdn.net/weixin_43703769/article/details/109254934

预热

  1. 分析下面的sql的执行流程
SELECT @ids AS _ids,
		( SELECT @ids := GROUP_CONCAT( id ) FROM chat WHERE FIND_IN_SET( pid, @ids ) ) AS cids,
		@l := @l + 1 AS LEVEL
FROM
    chat, ( SELECT @ids := '1', @l := 0 ) b
WHERE
    @ids IS NOT NULL 

这段SQL语句是一个递归查询,用于在表格 chat
中查找具有父子关系的记录,并计算每个记录的层级(LEVEL)。这里使用了用户自定义的变量(@ids和@l)来执行递归操作。以下是这段SQL语句的执行流程:

  1. FROM 子句:首先,SQL引擎处理 FROM 子句,它从 chat 表中检索数据,并将其与一个子查询((SELECT @ids := ‘1’, @l := 0)
    )联接在一起。这个子查询初始化了两个用户自定义变量,@ids设置为 ‘1’,@l设置为 0。
  2. WHERE 子句:WHERE 子句中的条件 @ids IS NOT NULL 用于控制递归的终止条件。只要 @ids 不为 NULL,查询就会继续执行。
  3. SELECT 子句:SELECT 子句包含了三个部分:
  • @ids AS _ids:将当前的 @ids 值赋给 _ids。
  • (SELECT @ids := GROUP_CONCAT( id ) FROM chat WHERE FIND_IN_SET( pid, @ids ) ) AS cids:在这一部分,GROUP_CONCAT 函数用于将与
    @ids 中的 pid 值相关的 id 值连接成一个逗号分隔的字符串。然后,这个字符串被赋给 @ids,以便在下一次迭代中使用。这将更新
    @ids 变量以包含新的 id 值。
  • @l := @l + 1 AS LEVEL:将 @l 增加 1,并将结果赋给 LEVEL 变量,用于跟踪当前记录的层级。
  1. 执行递归:在每次迭代中,@ids 的值都会更新,通过查找满足 FIND_IN_SET(pid, @ids) 条件的记录,将与 @ids 中的 pid
    值相关的子记录添加到结果集中。这个过程将一直进行,直到不再有满足条件的记录,此时 @ids 变量将为 NULL,查询结束。
  2. 结果集:查询的结果集将包括 _ids(包含每个记录的初始 id 值)、cids(包含每个记录的关联 id 值列表),以及 LEVEL(每个记录的层级)。
    这个SQL查询执行了一种递归操作,用于查找具有父子关系的记录,并构建一个层次结构。在每次迭代中,通过更新 @ids 变量,它查找与当前
    @ids 中的 pid 值相关的子记录。这允许你在结果中获得每个记录的层级信息。

用于分析的数据

chat_summary表数据

idpidbranchask
100苏州有几个区0
211苏州有几个湖1
320苏州有几个县跟着几个湖1来的
412苏州有几个湖2
513苏州有几个湖3
650苏州有哪些美食,跟着有几个湖3来的
760有哪些景区,跟着美食来的

sql查询


create table ttt as
SELECT DISTINCT c1.LEVEL - 1 LEVEL,
                c2.id,
                c2.pid,
                c2.ask
FROM (SELECT @ids AS _ids,
		( SELECT @ids := GROUP_CONCAT( id ) FROM chat WHERE FIND_IN_SET( pid, @ids ) ) AS cids,
		@l := @l + 1 AS LEVEL
      FROM
          chat, ( SELECT @ids := '1', @l := 0 ) b
      WHERE
          @ids IS NOT NULL) c1,
     chat_summary c2
WHERE FIND_IN_SET(c2.id, c1._ids)
ORDER BY LEVEL,
         id

查询ttt表结果如下:
LEVELidpidask
010苏州有几个区0
121苏州有几个湖1
141苏州有几个湖2
151苏州有几个湖3
232苏州有几个县跟着几个湖1来的
265苏州有哪些美食,跟着有几个湖3来的
376有哪些景区,跟着美食来的

sql查询

select max(id), ask, level
from ttt
group by level
查询结果如下:
max(id)asklevel
1苏州有几个区0
5苏州有几个湖1
6苏州有几个县跟着几个湖1来的2
7有哪些景区,跟着美食来的3

此时已经符合预期!!!

sql查询

select find_in_set("abc", "123,abc,456")
结果如下:
a
2

探寻测试之路

tes表内容如下

idnameinterests
1AliceSports,Travel
2BobMusic,Reading
3CharlieSports,Cooking,Music
4DavidTravel,Cooking

sql查询

SELECT *
FROM tes
WHERE FIND_IN_SET('Music', interests) > 0;

查询结果如下:

idnameinterests
2BobMusic, Reading
3CharlieSports, Cooking, Music

sql查询


SELECT @ids AS _ids,
		( SELECT @ids := GROUP_CONCAT( id ) FROM chat WHERE FIND_IN_SET( pid, @ids ) ) AS cids,
		@l := @l + 1 AS LEVEL
FROM
    chat, ( SELECT @ids := '1', @l := 0 ) b
WHERE
    @ids IS NOT NULL 

查询结果如下:

_idscidsLEVEL
12,4,51
2,4,53,62
3,673
7NULL4

sql查询

SELECT @ids := '0', @l := 0

查询结果如下:

@ids := ‘0’@l := 0
00

最终优化sql


SELECT d.*
FROM (
         SELECT
             @recursion_condition_var AS parent_id,
             @recursion_condition_var := (
            SELECT @parent_id := MAX(id)
            FROM chat_summary
            WHERE pid = @parent_id
        ) AS max_child_id,
        @level := @level + 1 AS LEVEL
         FROM chat_summary a,
             (SELECT @parent_id := 1, @level := 0, @recursion_condition_var := @parent_id) b
         WHERE @parent_id IS NOT NULL AND @recursion_condition_var IS NOT NULL
     ) c
         LEFT JOIN chat_summary d ON parent_id = d.id;

最终结果

idpidbranchask
100苏州有几个区0
513苏州有几个湖3
650苏州有哪些美食,跟着有几个湖3来的
760有哪些景区,跟着美食来的

补充

准备下列数据tmp表

idpidbranchask
100苏州有几个区0
211苏州有几个湖1
320苏州有几个县跟着几个湖1来的
412苏州有几个湖2
513苏州有几个湖3
650苏州有哪些美食,跟着有几个湖3来的
760有哪些景区,跟着美食来的

有下列sql语句

SELECT a.id, @ids AS _ids,
		( SELECT @ids := GROUP_CONCAT( id ) FROM tmp WHERE FIND_IN_SET( pid, @ids ) ) AS cids,
		@l := @l + 1 AS LEVEL
FROM
    tmp a, ( SELECT @ids := '1', @l := 0 ) b
WHERE
    @ids IS NOT NULL

执行过程

  1. 先执行子查询,SELECT @ids := ‘1’, @l := 0,也就是确认了表b的所有内容,此时b表中只有一条记录,即
@ids := ‘1’@l := 0
10
  1. 此时b表中只有一条记录,from的结果是是a和b的自然连接结果,所以确认了总的查询结果,就是7条,即由a的行数决定总的查询结果行数,
    执行执行SELECT @ids := GROUP_CONCAT( id ) FROM tmp WHERE FIND_IN_SET( pid, @ids ),把pid为1的行id的值拼接起来,即2,4,5,此时@ids的值变成了2,4,5,
    a表的第一行与b的第一行(b始终只有一行)进行自然连接的结果是
idpidbranchask@ids@l
100苏州有几个区02,4,51
211苏州有几个湖1待定值待定值
320苏州有几个县跟着几个湖1来的待定值待定值
412苏州有几个湖2待定值待定值
513苏州有几个湖3待定值待定值
650苏州有哪些美食,跟着有几个湖3来的待定值待定值
760有哪些景区,跟着美食来的待定值待定值
  1. 重复以上步骤,a表的第二行与b的第一行进行自然连接,结果是
idpidbranchask@ids@l
100苏州有几个区02,4,51
211苏州有几个湖13,62
  1. 让a的每一行与b的每一行进行自然连接,结果是
idpidbranchask_idscidsLEVEL
100苏州有几个区012,4,51
211苏州有几个湖12,4,53,62
320苏州有几个县跟着几个湖1来的3,673
412苏州有几个湖27NULL4
513苏州有几个湖3NULLNULL5
650苏州有哪些美食,跟着有几个湖3来的NULLNULL6
760有哪些景区,跟着美食来的NULLNULL7
  1. 加上where条件后,筛选出_ids不为null的值
idpidbranchask_idscidsLEVEL
100苏州有几个区012,4,51
211苏州有几个湖12,4,53,62
320苏州有几个县跟着几个湖1来的3,673
412苏州有几个湖27NULL4
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

micro_cloud_fly

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值