分析sql过程
启发:https://blog.csdn.net/weixin_43703769/article/details/109254934
预热
分析下面的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语句的执行流程:
FROM 子句:首先,SQL引擎处理 FROM 子句,它从 chat 表中检索数据,并将其与一个子查询((SELECT @ids := ‘1’, @l := 0) )联接在一起。这个子查询初始化了两个用户自定义变量,@ids设置为 ‘1’,@l设置为 0。 WHERE 子句:WHERE 子句中的条件 @ids IS NOT NULL 用于控制递归的终止条件。只要 @ids 不为 NULL,查询就会继续执行。 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 变量,用于跟踪当前记录的层级。
执行递归:在每次迭代中,@ids 的值都会更新,通过查找满足 FIND_IN_SET(pid, @ids) 条件的记录,将与 @ids 中的 pid 值相关的子记录添加到结果集中。这个过程将一直进行,直到不再有满足条件的记录,此时 @ids 变量将为 NULL,查询结束。 结果集:查询的结果集将包括 _ids(包含每个记录的初始 id 值)、cids(包含每个记录的关联 id 值列表),以及 LEVEL(每个记录的层级)。 这个SQL查询执行了一种递归操作,用于查找具有父子关系的记录,并构建一个层次结构。在每次迭代中,通过更新 @ids 变量,它查找与当前 @ids 中的 pid 值相关的子记录。这允许你在结果中获得每个记录的层级信息。
用于分析的数据
chat_summary表数据
id pid branch ask 1 0 0 苏州有几个区0 2 1 1 苏州有几个湖1 3 2 0 苏州有几个县跟着几个湖1来的 4 1 2 苏州有几个湖2 5 1 3 苏州有几个湖3 6 5 0 苏州有哪些美食,跟着有几个湖3来的 7 6 0 有哪些景区,跟着美食来的
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表结果如下:
LEVEL id pid ask 0 1 0 苏州有几个区0 1 2 1 苏州有几个湖1 1 4 1 苏州有几个湖2 1 5 1 苏州有几个湖3 2 3 2 苏州有几个县跟着几个湖1来的 2 6 5 苏州有哪些美食,跟着有几个湖3来的 3 7 6 有哪些景区,跟着美食来的
sql查询
select max ( id) , ask, level
from ttt
group by level
查询结果如下:
max(id) ask level 1 苏州有几个区 0 5 苏州有几个湖 1 6 苏州有几个县跟着几个湖1来的 2 7 有哪些景区,跟着美食来的 3
此时已经符合预期!!!
sql查询
select find_in_set( "abc" , "123,abc,456" )
结果如下:
探寻测试之路
tes表内容如下
id name interests 1 Alice Sports,Travel 2 Bob Music,Reading 3 Charlie Sports,Cooking,Music 4 David Travel,Cooking
sql查询
SELECT *
FROM tes
WHERE FIND_IN_SET( 'Music' , interests) > 0 ;
查询结果如下:
id name interests 2 Bob Music, Reading 3 Charlie Sports, 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
查询结果如下:
_ids cids LEVEL 1 2,4,5 1 2,4,5 3,6 2 3,6 7 3 7 NULL 4
sql查询
SELECT @ids := '0' , @l := 0
查询结果如下:
最终优化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;
最终结果 :
id pid branch ask 1 0 0 苏州有几个区0 5 1 3 苏州有几个湖3 6 5 0 苏州有哪些美食,跟着有几个湖3来的 7 6 0 有哪些景区,跟着美食来的
补充
准备下列数据tmp表
id pid branch ask 1 0 0 苏州有几个区0 2 1 1 苏州有几个湖1 3 2 0 苏州有几个县跟着几个湖1来的 4 1 2 苏州有几个湖2 5 1 3 苏州有几个湖3 6 5 0 苏州有哪些美食,跟着有几个湖3来的 7 6 0 有哪些景区,跟着美食来的
有下列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
执行过程
先执行子查询,SELECT @ids := ‘1’, @l := 0,也就是确认了表b的所有内容,此时b表中只有一条记录,即
此时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始终只有一行)进行自然连接的结果是
id pid branch ask @ids @l 1 0 0 苏州有几个区0 2,4,5 1 2 1 1 苏州有几个湖1 待定值 待定值 3 2 0 苏州有几个县跟着几个湖1来的 待定值 待定值 4 1 2 苏州有几个湖2 待定值 待定值 5 1 3 苏州有几个湖3 待定值 待定值 6 5 0 苏州有哪些美食,跟着有几个湖3来的 待定值 待定值 7 6 0 有哪些景区,跟着美食来的 待定值 待定值
重复以上步骤,a表的第二行与b的第一行进行自然连接,结果是
id pid branch ask @ids @l 1 0 0 苏州有几个区0 2,4,5 1 2 1 1 苏州有几个湖1 3,6 2
让a的每一行与b的每一行进行自然连接,结果是
id pid branch ask _ids cids LEVEL 1 0 0 苏州有几个区0 1 2,4,5 1 2 1 1 苏州有几个湖1 2,4,5 3,6 2 3 2 0 苏州有几个县跟着几个湖1来的 3,6 7 3 4 1 2 苏州有几个湖2 7 NULL 4 5 1 3 苏州有几个湖3 NULL NULL 5 6 5 0 苏州有哪些美食,跟着有几个湖3来的 NULL NULL 6 7 6 0 有哪些景区,跟着美食来的 NULL NULL 7
加上where条件后,筛选出_ids不为null的值
id pid branch ask _ids cids LEVEL 1 0 0 苏州有几个区0 1 2,4,5 1 2 1 1 苏州有几个湖1 2,4,5 3,6 2 3 2 0 苏州有几个县跟着几个湖1来的 3,6 7 3 4 1 2 苏州有几个湖2 7 NULL 4