背景
leetcode刷题,一些问题记录,mysql之连续出现的数字
描述
编写一个 SQL 查询,查找所有至少连续出现三次的数字。
预期结果
方案
自己的写法
缺陷:Id必须是连续的,如果连续出现的次数太多就没有办法写了
SELECT DISTINCT
l1.Num AS ConsecutiveNums
FROM
Logs l1,
Logs l2,
Logs l3
WHERE
l1.Id = l2.Id - 1
AND l2.Id = l3.Id - 1
AND l1.Num = l2.Num
AND l2.Num = l3.Num
来自大佬山水有相逢的写法(不受版本的影响)
不受Logs表中的Id是否连续的限制,而且可以任意设定某个值连续出现的次数
select distinct Num as ConsecutiveNums
from (
select Num,
case
when @prev = Num then @count := @count + 1
when (@prev := Num) is not null then @count := 1
end as CNT
from Logs, (select @prev := null,@count := null) as t
) as temp
where temp.CNT >= 3;
大佬给出的解释:
与自关联或自连接相比,这种方法的效率更高,不受Logs表中的Id是否连续的限制,而且可以任意设定某个值连续出现的次数。
-
逻辑:构建两个变量@prev 和@count ,前者用于与Num做比较判断,后者用于@prev和Num相等时的条件计数;
-
(select @prev := null,@count := null) as t 这句的作用是初始化两个变量,并将初始化后的变量放到一张临时表t中,:=符号在MySQL中是赋值的意思;
-
when @prev = Num then @count := @count + 1和when (@prev := Num) is not null then @count := 1 这两个语句不能交换顺序,赋值语句永远非NULL,所以一旦执行顺序来到了第二个when,@count 是一定会被赋值为1的,后者放到前面的话就达不到计数的目的;
-
(@prev := Num) is not null这部分去掉后面加的判断,SQL也能正常执行,上面SQL中case when的这种用法,when后是判断条件,赋值后又加判断,我原以为这样会好理解点;
-
case when本质是一个函数,有值时就返回内部处理得到的值,无值就返回NULL,针对每一个Num,上面SQL中的case when 都会有一个计数,并把这个计数返回给CNT。
其他的写法
MySQL8.0以后的版本支持窗口函数
- LEAD()函数是一个窗口函数,允许您向前看多行并从当前行访问行的数据。
与LAG()函数类似,LEAD()函数对于计算同一结果集中当前行和后续行之间的差异非常有用。
lead() over()
这里lead(Num,1),Num代表当前的列,1代表当前行的下一行
lead(Num,2),Num代表当前的列,2代表当前行的往下的第二行
SELECT distinct Num as ConsecutiveNums
FROM(
SELECT id,Num,lead(Num,1) over(order by id) as a,lead(Num,2) over(ORDER BY id) as b
FROM logs
) t
WHERE Num=a and a=b;
结果
- lag函数
lag函数与lead函数相似,不同的是lag函数是向上走,lead函数是向下走,记住这里的行都是相对而言,相对当前行而言
SELECT distinct Num as ConsecutiveNums
FROM(
SELECT id,Num,lag(Num,1) over(order by id) as a,lag(Num,2) over(ORDER BY id) as b
FROM logs
) t
WHERE Num=a and a=b;