题目描述
编写一个 SQL 查询,查找所有至少连续出现三次的数字。
Id | Num |
---|---|
1 | 1 |
2 | 1 |
3 | 1 |
4 | 2 |
5 | 1 |
6 | 2 |
7 | 2 |
例如,给定上面的 Logs
表, 1
是唯一连续出现至少三次的数字。
ConsecutiveNums |
---|
1 |
相关知识
此题不需要前置知识
解题过程
-
分析题意
- 声明两个变量
- @last_num:上一个数字
- @last_time:当前数字出现的次数
- 当当前元组的数字与前一个元组的数字相同,则出现次数等于上一个元组数字出现次数加一
- 当当前元组的数字与前一个元组的数字不同,则出现次数重置为1
- 当连续出现3次时选择这个数,并去重
- 声明两个变量
-
编写sql
SELECT DISTINCT Num AS ConsecutiveNums FROM ( SELECT Num, ( CASE WHEN @last_num=Num THEN @last_time:=@last_time+1 WHEN (@last_num:=Num) OR (@last_num =0) THEN @last_time:=1 END ) AS Continuettimes FROM `Logs`,(SELECT @last_num:=NULL,@last_time:=0) t ) c WHERE Continuettimes>2
-
提交答案,通过
-
查看题解,发现有个更简洁的写法
-
sql
SELECT DISTINCT (num) "ConsecutiveNums" FROM ( SELECT Num, (row_number() over(order by id ) - row_number() over(partition by num order by id)) rank_ FROM Logs ) tmp group by rank_,Num having count(rank_)>=3;
-
首先通过ROW_NUMBER()获取使用id排序的行号,解决id不连续的问题(id 1,2,3,100,101,102,…..)
-
然后通过ROW_NUMBER()获取根据Num分组后每个分组内通过id排序获得的行号
-
将两个行号对于相减,如果它们是相邻的,那么相邻的第一个行号会增加1,相邻的第二个行号也会增加1,相减后结果是相等的,即这个连续数字串的起始行号
-
由于可能会有重复,所以使用DISTINCT去重
-
最后,通过num和order共同分组,使用count进行统计,由于count函数执行在where之后所以此时进行筛选需要使用having筛选出需要的num
-
学习总结
-
变量运算符
- 变量在进行计算赋值时使用的运算符是
:=
- 变量在进行比较运算时使用的运算符是
=
- 变量在进行计算赋值时使用的运算符是
-
SQL的执行顺序
- 执行FROM
- WHERE条件过滤
- GROUP BY分组
- 执行SELECT投影列,聚集函数
- HAVING条件过滤
- 执行ORDER BY 排序
这也是为什么在WHERE语句中声明的变量可以在SELECT中使用