SQL 拆解
首先这条 SQL 里面有这么几个地方让阿粉迷惑,第一个是@
符号,然后是:=
然后还有个 case when then
语法,平日里在 CRUD 的时候没遇到过这种写法,不过不知道没关系,Google 一下就好了。网上查了下,@prev
表示的是声明变量,:=
操作是 MySQL 的赋值操作,case when then
when
后面接的是判断条件,条件成立则会返回then
后面的结果,需要注意的是 case
只会返回第一个符合条件的结果,剩下将会被忽略。
简单的了解了上面几个知识点过后,我们就可以对下面这条 SQL 进行拆解了。
select distinct Num as ConsecutiveNums from ( select Num, case when @currnet = Num then @count := @count + 1 when (@currnet := Num) is not null then @count := 1 end as CNT from Logs, (select @currnet := null,@count := 0) as t ) as temp where temp.CNT >= 3
- 最外层的
select distinct Num as ConsecutiveNums from () as temp where temp.CNT >= 3
; 我们可以看到中间的小括号里面被派生成了一个临时表,表名叫做 temp,并且 temp 表中有两个字段分别是Num,CNT
。其实Num
则是表Logs
里面的数字,CNT
则是连续出现的累积次数,最后的where temp.CNT >= 3
则是在根据要求连续出现的次数进行查询。 - 派生语句
SELECT Num,CASE WHEN @currnet=Num THEN @count:=@count+1 WHEN (@currnet:=Num) IS NOT NULL THEN @count:=1 END AS CNT FROM LOGS,(SELECT @currnet:=NULL,@count:=NULL) AS t
包含两个部分,一个是Select
中的case when then
另一个是from
中的(select @currnet:= null,@count := null) as t
其中select @currnet:= null,@count := null
也是一个派生表,这里通过声明两个变量@currnet, @count
并赋值为null
。 - 中间派生的表 temp 的内容如下,通过生成记录每个数字出现的次数的临时表来查询数据。
下面我们通过explain
命令看下整个 SQL 的执行过程,:
- 从
select_type
中我们可以看到总共派生了两个表,跟我们上面分析的一致; - ID 为 3 的派生表的内容是
select @current := null,@count := 0
定义两个变量并赋值,并且 id 越大越先执行; case
语句中第一个when
中判断当前扫描到的num
值与定义的变量是否一致,如果一致则count
加一,不一致则进行下一个when
条件判断,并将count
赋值为 1 返回;- 经过全表扫描过后,就得到了上面的中间表
temp
的内容;
不得不说,上面的方案是很完美的,不存在 ID 是否连续的问题,也不会多层自连接,而且也可以根据要求找出连续出现的次数,相对灵活。刚开始看到这个 SQL 的时候,阿粉并不清楚整个执行的过程,然后通过 explain 才渐渐明白整个执行过程, 而且对于在 SQL 中使用变量也有了一定的了解。
关于 explain 的详解,感兴趣的朋友可以去看公号之前发的文章MySQL 之 Explain 输出分析
最后说两句(求关注)
最近大家应该发现微信公众号信息流改版了吧,再也不是按照时间顺序展示了。这就对阿粉这样的坚持的原创小号主,可以说非常打击,阅读量直线下降,正反馈持续减弱。
所以看完文章,哥哥姐姐们给阿粉来个在看吧,让阿粉拥有更加大的动力,写出更好的文章,拒绝白嫖,来点正反馈呗~。
如果想在第一时间收到阿粉的文章,不被公号的信息流影响,那么可以给Java极客技术设为一个星标。
最后感谢各位的阅读,才疏学浅,难免存在纰漏,如果你发现错误的地方,留言告诉阿粉,阿粉这么宠你们,肯定会改的~
最后谢谢大家支持~
最最后,重要的事再说一篇~