hiveSql 跨N天最大连续统计

hiveSql 跨N天最大连续统计

说明

看到标题可能没太能理解,什么叫跨N天连续?这里解释下:
跨N天连续定义为:登录相隔天数小于N
用跨2天举例(即隔一天也算连续登录)
例如 :
2022-01-01 与 2022-01-02算连续
2022-01-01 与 2022-01-03也算连续(隔了一个2022-01-02 但是也算连续登录)
2022-01-01 与 2022-01-04则不算连续了(隔了2天不算连续了)

需求

有数据如下:
表:tmp_login

id	dt
1001	2021-12-12
1001	2021-12-13
1001	2021-12-14
1001	2021-12-16
1001	2021-12-19
1001	2021-12-20
1001	2021-12-26
1001	2021-12-27
1001	2021-12-29
1002	2021-12-12
1002	2021-12-16
1002	2021-12-17

字段:
id:用户ID
dt:用户登录日期

希望计算每个用户的最大连续登录天数,这里的连续定义为小于2天。即隔一天也算连续登录
基于上述明细数据,期望获得以下数据:

idmax_days
10015
10022

1001:是2021-12-12到2021-12-16号 5天
1002:是2021-12-16到2021-12-17号 2天


到这里可以先思考下用hiveSql怎么实现

分析

对于严格每天登录,这个逻辑来计算最大连续登录天数,很容易想到,将用户登录日期排序,用登录日期减去排序序号,则按照减完之后的日期分组统计天数即可。但是现在隔一天也算连续,这种方式貌似实现不了,减完之后的日期会被分到多组中。所以最关键的点在于怎么把满足上述定义的连续的日期分到同一个组里,然后聚合组内天数即可。容易想到,在给每个用户的登录日期排序后,将相邻的两个日期做差,差值大于2的有一个断点,后续利用这个断点做日期分组。这种思路也叫做重分组思想。

有了上述思路,下面逐步完成sql编写

实现

  • 1.将用户登录日期排序,排序后相邻日期做差值
select
	id,dt,
	lag(dt,1,dt) over(partition by id order by dt) as previous_dt,
	datediff(dt,lag(dt,1,dt) over(partition by id order by dt)) as adjacent_dt_diff
from tmp_login

previous_dt:当前日期的升序排序后前一个日期
adjacent_dt_diff:当前日期 与 升序排序后前一个日期相差的天数
有结果如下:
在这里插入图片描述

  • 2.有相差天数后,可以按照需求定义小于什么差值是连续,这里是小于两天为连续。可以根据adjacent_dt_diff值来定义上述分析中的断点
select 
	id,dt,previous_dt
	,if(adjacent_dt_diff>2,1,0) as flag
from
	(select
		id,dt,
		lag(dt,1,dt) over(partition by id order by dt) as previous_dt,
		datediff(dt,lag(dt,1,dt) over(partition by id order by dt)) as adjacent_dt_diff
	from tmp_login
	) t;

flag:这里即是断点,因为用户可能会有多段满足条件的连续,每段的第一天flag即为1。断点与断点之间的日期即是满足条件的连续日期了。
结果如下:
在这里插入图片描述
上述截图可以看到,用户1001,在2021-12-16 与 2021-12-19之间是超过2天的,即不连续。2021-12-19与2021-12-20有是下一段的连续,flag在2021-12-19这条上是1。

  • 3.上述断点有了之后,怎么讲每个断点间的日期分在同一组呢?这里可以利用开窗sum() over() 累加的方式分组:
select 
	id,dt,previous_dt,adjacent_dt_diff
	,if(adjacent_dt_diff>2,1,0) as flag
    ,sum(if(adjacent_dt_diff>2,1,0)) over (partition by id order by dt) as group_flag
from
	(select
		id,dt,
		lag(dt,1,dt) over(partition by id order by dt) as previous_dt,
		datediff(dt,lag(dt,1,dt) over(partition by id order by dt)) as adjacent_dt_diff
	from tmp_login
	) t;

group_flag:满足条件的分组字段
在这里插入图片描述
上述截图可以看到,每个用户id下满足条件的连续登录日期都按照group_flag分组了,接下来只需要做分组,取组内最大日期减去组内最小日期即可

  • 4.分组聚合,取组内最大最小日期天数差
select 
    id,max(days) as max_days
from
    (select 
        id,group_flag,datediff(max(dt),min(dt)) as days
    from
        (select 
            id,dt,previous_dt,adjacent_dt_diff
            ,if(adjacent_dt_diff>2,1,0) as flag
            ,sum(if(adjacent_dt_diff>2,1,0)) over (partition by id order by dt) as group_flag
        from
            (select
                id,dt,
                lag(dt,1,dt) over(partition by id order by dt) as previous_dt,
                datediff(dt,lag(dt,1,dt) over(partition by id order by dt)) as adjacent_dt_diff
            from tmp_login
            ) t
        ) t1
        group by id,group_flag
    ) t2
group by id

结果如下:
在这里插入图片描述
此处有个小的注意事项,最大日期 - 最小日期后加1。

  • 5.精简sql,完整sql如下:
select 
    id,max(days) as max_days
from
    (select 
        id,flag_sum,datediff(max(dt),min(dt))+1 as days
    from
        (select 
            id,dt,flag
            ,sum(flag) over(partition by id order by dt) as flag_sum
        from
            (select 
                id,dt,
                if(datediff(dt,lag(dt,1,dt) over(partition by id order by dt) ) > 2,1,0) as flag
            from tmp_login
            ) t
        ) t1
    group by id,flag_sum
    ) t2
group by id;

最后

喜欢的点赞、关注、收藏吧~ 感谢支持~~

<think> 首先,用户查询是:"我想查找在HiveSQL算连续出现次数的方法 请问HiveSQL算连续出现次数的方法"。这明确是关于在HiveSQL中处理连续出现问题的技术。 参考提供的站内引用: - **引用[1]**:讨论使用Hive的窗口函数(如lag和lead)来算连续算问题,特别是针对数字是否连续出现多次。它提到了使用lag或lead函数来检查前一行的数据。 - **引用[2]**:描述如何算连续签到数,使用row_number()窗口函数结合date_sub来算连续数。关键是通过日期减去行号得到一个分组标识。 - **引用[3]**:详细说明了找出连续日期及连续数的另一种方法。它使用rank()窗口函数,然后日期减去行号来分组,最后聚合找出最小和最大日期以及数。 - **引用[4]**:涉及算连续逾期最长月份数,使用lateral view,但描述不完整。不过,它暗示了处理序列数据的方法。 这些引用都指向使用Hive的窗口函数来处理连续性问题。核心思想是利用窗口函数(如lag, lead, row_number, rank)结合算术操作(如日期减法)来标识连续组。 系统指令要求: - 使用中文回答。 - 结构清晰,帮助用户逐步解决问题。 - 生成相关问题。 - 在回答中引用的段落末尾添加引用标识。 - 对于数学表达式或代码,使用适当的LaTeX格式:行内用$...$,独立公式用$$...$$。 我的回答应该基于这些引用,提供在HiveSQL算连续出现次数的方法。我将逐步解释方法,包括示例代码。 关键步骤分析(从引用中提炼): 1. **排序序列**:首先,对数据进行排序(通常是按时间或顺序字段),partition by user_id或其他分组键,order by日期。 2. **生成行号**:使用窗口函数如row_number()或rank()添加行号。 3. **算分组标识**:通过日期减去行号(或类似操作),创建一个新列。如果序列是连续的,这个差值将是常量,从而标识连续组。 4. **分组和聚合**:基于分组标识,group by该标识,然后算最小日期、最大日期和数(或出现次数)。 5. **处理特定连续性问题**:如引用[1],可以使用lag或lead直接检查相邻行的值是否连续。 我将整合这些到一个清晰的回答中。 回答结构: 1. **介绍**:简要解释连续出现次数问题及其在HiveSQL中的重要性。 2. **核心方法**:逐步描述如何使用窗口函数算连续出现次数。 - 步骤1: 排序数据。 - 步骤2: 添加行号。 - 步骤3: 算分组标识(日期减行号)。 - 步骤4: 分组和聚合。 基于引用[2]和[3]。 3. **替代方法**:提到使用lag或lead函数(如引用[1]),适用于相邻行检查。 4. **示例代码**:提供一个简单的HiveSQL查询示例,展示如何算连续签到数或类似场景。 5. **生成相关问题**:在末尾添加相关问题。 确保LaTeX语法正确:在描述公式时使用$...$,比如日期减行号的部分。 引用标识:在描述引用内容时添加[^n]。 例如:使用row_number()窗口函数可以生成行号[^2]。 现在,大纲回答: - **标题**:HiveSQL算连续出现次数的方法 - **内容**: - 连续出现次数问题在数据分析中常见,例如连续签到、连续逾期等。HiveSQL的窗口函数是高效解决方案。 - 核心步骤: 1. **排序序列**:使用窗口函数对数据按时间排序。 2. **添加行号**:应用row_number()添加行号。 3. **算分组标识**:例如,$ \text{sign\_date} - \text{rk} $ 其中rk是行号,如果连续,此值相同。 4. **分组聚合**:group by user_id和分组标识,算min(date), max(date), count(*)作为连续数。 [基于引用[2]和[3]] - 另一种方法:使用lag或lead检查相邻行是否连续。例如,算连续出现的数字[^1]。 - **示例查询**:提供一个完整的HiveSQL查询代码块。 例如: ```sql SELECT user_id, sign_group, MIN(sign_date) AS start_date, MAX(sign_date) AS end_date, COUNT(1) AS days FROM ( SELECT user_id, sign_date, DATE_SUB(sign_date, rk) AS sign_group FROM ( SELECT user_id, sign_date, ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY sign_date) AS rk FROM login_log ) t1 ) t2 GROUP BY user_id, sign_group; ``` 基于引用[3]。 - 讨论优势和局限性。 - **相关问题**(生成3-4个): 1. HiveSQL中如何优化连续算查询的性能? 2. 窗口函数lag和lead的具体用法是什么? 3. 如何处理缺失数据时的连续性问题? 最后,确保回答真实可靠,基于提供的引用。 </think> ### HiveSQL算连续出现次数的方法 在数据分析中,连续出现次数问题常见于连续签到、连续逾期等场景(如用户行为跟踪或风险评估)。HiveSQL通过**窗口函数**高效处理此类问题,核心思想是利用排序、行号生成和分组标识来识别连续序列。以下基于站内引用整理标准方法,逐步说明实现流程(关键步骤参考引用[1][2][3])。 #### 方法一:使用窗口函数(row_number/rank)和日期减法(推荐) 此方法适合连续日期序列(如签到、登录记录)。核心是通过行号创建分组标识:如果序列连续,则 $ \text{日期} - \text{行号} $ 的值将恒定,从而标识连续组。 **步骤分解:** 1. **排序数据并添加行号**:使用 `ROW_NUMBER()` 或 `RANK()` 窗口函数,按时间字段升序排序,生成行号列 `rk`。 2. **算分组标识**:定义新列 `sign_group = 日期 - rk`。连续序列中,$ \text{sign\_group} $ 值相同(因日期差恒定)。 3. **分组聚合算连续数**:基于 `sign_group` 分组,统最小日期、最大日期和行数(即连续数)。 **完整示例查询(用户连续签到数)**: ```sql SELECT user_id, sign_group, MIN(sign_date) AS start_date, -- 连续序列开始日期 MAX(sign_date) AS end_date, -- 连续序列结束日期 COUNT(1) AS consecutive_days -- 连续出现次数(数) FROM ( SELECT user_id, sign_date, DATE_SUB(sign_date, rk) AS sign_group -- 关键:日期减行号生成分组标识 FROM ( SELECT user_id, sign_date, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY sign_date) AS rk -- 按用户分组排序生成行号 FROM login_log -- 原始表(示例数据:user_id, sign_date) ) t1 ) t2 GROUP BY user_id, sign_group; -- 分组聚合统 ``` **说明**: - **输入表**: `login_log` 含 `user_id`(用户ID)和 `sign_date`(签到日期)。 - **输出**: 每个用户的连续签到组起始日期、结束日期及连续数。 - **数学原理**: 若日期连续,则 $ \text{sign\_date} - \text{rk} $ 为常数,分组后 $ \text{COUNT}(1) $ 即为连续出现次数[^2][^3]。 - **优势**: 适用于任意长度连续序列,支持缺失数据处理(非连续日期不影响分组)。 #### 方法二:使用lag/lead函数检查相邻行 此方法适合简单场景(如检查数字是否连续出现两次),通过比较当前行与前一行的值直接判断连续性。 **步骤分解:** 1. **生成相邻行数据**:使用 `LAG(col, n, default)` 或 `LEAD(col, n, default)` 函数获取当前行向上/下 `n` 行的值。 2. **过滤连续序列**:比较当前值与相邻值,筛选连续出现的记录。 **示例查询(算连续出现至少两次的数字)**: ```sql SELECT num, COUNT(*) AS consecutive_count -- 连续出现次数 FROM ( SELECT num, LAG(num, 1) OVER (ORDER BY id) AS prev_num -- 获取上一行数字 FROM number_table -- 原始表(示例数据:id, num) ) t WHERE num = prev_num -- 当前数字与前一数字相同即连续 GROUP BY num; ``` **说明**: - **输入表**: `number_table` 含 `id`(顺序ID)和 `num`(数字值)。 - **输出**: 连续出现至少两次的数字及次数。 - **数学原理**: 若 $ \text{num} = \text{prev\_num} $,则标识连续出现[^1]。 - **局限性**: 仅适合相邻行检查,无法处理长序列或多中断场景(需结合方法一)。 #### 方法选择建议 - **长连续序列(日期/数值)**:优先用方法一(row_number + 日期减法),支持复杂分组[^3]。 - **简单相邻比较**:用方法二(lag/lead),代码简洁[^1]。 - **性能优化提示**: 确保分区字段(如 `user_id`)有索引,避免全表扫描;大数据集可启用Hive矢量化执行。 #### 注意事项 - **数据去重**: 原始数据需先处理重复值(用 `DISTINCT` 或子查询)。 - **日期格式**: 确保日期字段为DATE类型,否则需转换(如 `CAST(sign_date AS DATE)`)。 - **缺失值处理**: lag/lead函数返回NULL时,需用 `COALESCE` 设默认值[^1]。 如需扩展(如处理逾期序列引用[^4]),可结合 `LATERAL VIEW` 拆分字段,但核心仍依赖上述窗口函数逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@nanami

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值