大家好,在数仓开发、用户行为分析和面试中,“连续” 类问题出现频率极高:连续登录、连续活跃、连续消费、连续出现数字、连续打卡……这类题目看似五花八门,其实核心逻辑完全一致

今天这篇文章,用 Hive 实战案例带你彻底吃透:一套通用思路 + 多个真实场景,看完就能直接写进项目、搞定面试。

一、核心思想:连续行为的本质是什么?

不管是 “连续出现的数字” 还是 “连续活跃的用户”,本质都是:在有序序列中,找到一段值相同且位置连续的记录。

通用解法有两种:

  1. 自连接法:适合固定连续次数(连续 3 次、连续 7 天)
  2. 窗口函数法:通用万能,支持任意连续天数(面试首选)

下面直接上案例。

二、案例 1:LeetCode 经典 —— 连续出现三次的数字

需求

Logs 表,查找至少连续出现 3 次的数字。

create table logs(
    id int,
    num int
);

insert into logs values
(1,1),(2,1),(3,1),
(4,2),(5,1),(6,2),(7,2);

解法 1:自连接(直观易懂)

连续 3 次 = 当前行、下一行、下下行数字相同。进行联表查询

select l1.num from logs l1
left join logs l2
--连续的话id应该是要差值为1的并且内容要相等
on l1.id=l2.id-1 and l1.num = l2.num
left join logs l3
on l2.id=l3.id-1 and l3.num = l2.num
最后三列均不为空便是连续三次出现的
where l2.id is not null and l3.id is not null;

结果

解法2:窗口函数

with t1 as (
select id,num,row_number() over (partition by num order by id ) rk from logs)
,t2 as (select num, id-rk flag from t1),t3 as (
select num,count(1) lianxu from t2 group by num,flag)
select num from t3 where lianxu>=3;

下面为大家一一拆解这个sql函数

第一步,先按照num进行分组,把把相同的数字放在一起,再按照id进行组内排序,保证数字是连续的,然后给组内的每一个数字进行编号,如果他是连续的,那么,编号-id一定是相等的

select id,num,row_number() over (partition by num order by id ) rk from logs

运行结果为

第二步,进行id-rk获取标识,如果连续,id-rk一定是相等的

with t1 as (
select id,num,row_number() over (partition by num order by id ) rk from logs)
select num, id-rk flag from t1

运行结果为

后面的步骤过于简单,便不再详细讲述,只需根据flag进行分组,并且统计出现的次数,最后使用where进行条件查询即可。

三、案例 2: 连续活跃 3 天及以上用户

create table t_useractive(
    uid string,
    dt  string
);

insert into t_useractive values
('A','2023-10-01'),('A','2023-10-02'),('A','2023-10-03'),('A','2023-10-04'),
('B','2023-10-01'),('B','2023-10-03'),('B','2023-10-04'),('B','2023-10-05'),
('C','2023-10-01'),('C','2023-10-03'),('C','2023-10-05'),('C','2023-10-06'),
('D','2023-10-02'),('D','2023-10-03'),('D','2023-10-05'),('D','2023-10-06');

万能窗口解法(必须背)

核心公式:连续日期 = 日期 - 行号 得到相同分组标记

with  t1 as (
select uid,dt,row_number() over (partition by uid order by dt) n
from t_useractive),t2 as (
        select uid,dt,date_sub(dt,n) flag from t1)
,t3 as (  select uid,flag,count(*) active_day from t2 group by uid,flag)
select  distinct uid from t3 where active_day >=3;

步骤与第一个案例相似,先按照uid进行分组,然后根据分组,对组内的dt日期进行排序,相当于给每个日期搞了一个行编号,这个行编号是有序的,之后便是在此基础上进行时间的做差,如果时间是连续的,那么他的行编号与时间的差值一定是相等的,2018-10-01 编号 1, 2018-10-2 编号为2,date_sub(时间,天数)相当于date_sub(时间,编号)  如果连续一定是相等的。求出flag后,便可以对flag进行分组,求出分组内每个出现的次数,最后条件查询次数大于3即可。

四、案例 3:扩展实战 —— 用户最长连续活跃天数

with  t1 as (
select uid,dt,row_number() over (partition by uid order by dt) n
from t_useractive),t2 as (
        select uid,dt,date_sub(dt,n) flag from t1)
,t3 as (  select uid,flag,count(*) active_day from t2 group by uid,flag)
--只需要再次不加条件过滤换成分组求组内最大值
select   uid,max(active_day) from t3 group by uid;

五、案例 4:连续消费 / 连续打卡 / 连续观看

结构一模一样,只换表名和字段名:

  • 连续消费:按用户 + 日期分组
  • 连续打卡:按用户 + 打卡日期
  • 连续观看视频:按用户 + 观看日期

六、总结(干货重点)

  1. 固定连续次数 → 自连接
  2. 任意连续天数 → 窗口函数(date_sub + row_number)
  3. 连续行为万能公式:分组排序行号 → 日期减行号 → 分组统计次数
  4. 面试高频:连续数字、连续活跃、最长连续、TopN 连续

只要掌握这一套,Hive 里所有连续类问题直接秒杀。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐