本来这种中等题目是不值得特地写在文章中的,但是看到其中有一个题解使用窗口函数来解决连续出现N次数字的方法,并且在过程中遇到了比较有趣的问题,就在此记录一下
题目:
表:Logs
+-------------+---------+ | Column Name | Type | +-------------+---------+ | id | int | | num | varchar | +-------------+---------+ 在 SQL 中,id 是该表的主键。 id 是一个自增列。
找出所有至少连续出现三次的数字。
返回的结果表中的数据可以按 任意顺序 排列。
示例 1:
输入: Logs 表: +----+-----+ | id | num | +----+-----+ | 1 | 1 | | 2 | 1 | | 3 | 1 | | 4 | 2 | | 5 | 1 | | 6 | 2 | | 7 | 2 | +----+-----+ 输出: Result 表: +-----------------+ | ConsecutiveNums | +-----------------+ | 1 | +-----------------+ 解释:1 是唯一连续出现至少三次的数字。
题解:
with t1 as (
select id, num,
id - cast(row_number() over (partition by num order by id) as signed) as s_id
from Logs
order by num asc
)
select distinct num as ConsecutiveNums
from t1
group by num, s_id
having count(*) >= 3;
这里使用有趣的窗口函数按num进行分组row_number()
自增并使用id减去这个分组的row_number()
组,假如是连续的num
,那么id-自增id
得到的差应该是一样的,那么在这个基础上,对num
和diff
进行group by having count(*)
就能求出(num,diff)
一样的和的数量,这样只需要设置 having count(*)
的数目,即可确定连续出现N次的数字/内容。
有趣的内容:当我在直接使用id-row_number() over (partition by num order by id)
出现了一个错误提示: BIGINT UNSIGNED value is out of range in `(t.id - t.rnk)`
在MySQL中,如果你对两个整数进行减法运算,其中一个整数是 UNSIGNED
类型,默认情况下结果也会被视为 UNSIGNED
。这意味着如果结果本来是负数,由于 UNSIGNED
类型不能表示负数,你会得到一个错误的结果。例如:SELECT CAST(1 AS UNSIGNED) - 2;
在这个例子中,1
被转换为 UNSIGNED
类型,然后减去 2
,结果应该是 -1
。但是由于结果被视为 UNSIGNED
,MySQL会返回一个非常大的正数,而不是负数。
所以在上述的题解中,使用了cast 将row_number()转换为了signed有符号类型即可解决这个错误。