分析sql过程
预热
- 分析下面的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 @ids AS _ids,
@n as maxid,
@n:=( SELECT @ids := max(id) FROM chat WHERE pid= @ids group by pid) AS cids,
@l := @l + 1 AS LEVEL
FROM
chat, ( SELECT @ids := '1', @l := 0, @n := '1' ) b
WHERE
@ids IS NOT NULL
and @n is not null) c
left join chat_summary d on c.maxid = d.id
SELECT max(id)
FROM chat
group by pid
最终结果:
id |
pid |
branch |
ask |
1 |
0 |
0 |
苏州有几个区0 |
5 |
1 |
3 |
苏州有几个湖3 |
6 |
5 |
0 |
苏州有哪些美食,跟着有几个湖3来的 |
7 |
6 |
0 |
有哪些景区,跟着美食来的 |