Hive Sql完成过滤每组累积去重数低于某值计算

本文介绍了如何通过SQL查询实现公司学习功能中,计算每天每个学习内容的去重截止学习用户数,过滤掉数量小于等于1的情况。作者详细阐述了需求背景、期望结果,并展示了基于日期、内容和用户数据的基本思路和逻辑实现步骤。

实现限制组内截止数量

需求概述

近期公司开发某项学习功能,改功能有很多学习内容(如java,C,python等方向),每天都会有众多学习用户学习某一项或者多项学习内容。产生数据如下表:

日期内容学习用户
2022-01-01javau1
2022-01-02javau1
2022-01-02javau2
2022-01-01Cu1
2022-01-01Cu3
2022-01-01Pythonu4
2022-01-02Pythonu4
2022-01-02Pythonu5
2022-01-02Pythonu6

期望数据

现在想要计算截止每天每个学习内容的截止去重学习用户数,但是截止去重用户数小于等于1的要被过滤,期望数据如下:

日期内容去重截止学习用户数
2022-01-02java2
2022-01-01C2
2022-01-02Python3

截止到2022-01-01,学习内容java的为去重用户数为1,学习内容C的为去重用户数为2,学习内容Python的为去重用户数为1。所以2022-01-01学习内容为java和python的都要内过滤。

基本思路

将所有天数和学习种类去重后作为临时表b,将明细表作为表a,a表内关联b表,关联条件为a的pdate要小于等于b的pdate并且学习内容一致,这样每个学习种类的截止到每天的学习用户数就取出了。再分组求和,having过滤组内数量即可。

逻辑实现

with tmp as ( -- 基础数据
select '2022-01-01' as pdate,'java' as icate, 'u1' as user
union all 
select '2022-01-02' as pdate,'java' as icate, 'u1' as user
union all 
select '2022-01-02' as pdate,'java' as icate, 'u2' as user
union all 
select '2022-01-01' as pdate,'C' as icate, 'u1' as user
union all 
select '2022-01-01' as pdate,'C' as icate, 'u3' as user
union all 
select '2022-01-01' as pdate,'Python' as icate, 'u4' as user
union all 
select '2022-01-02' as pdate,'Python' as icate, 'u4' as user
union all 
select '2022-01-02' as pdate,'Python' as icate, 'u5' as user
union all 
select '2022-01-02' as pdate,'Python' as icate, 'u6' as user
),
tmp1 as (
select distinct pdate,icate from tmp
)
select
    a.pdate,a.icate,count(distinct a.user) as icount
from 
    tmp a 
join 
    tmp1 b
on 
    a.pdate <= b.pdate 
and 
    a.icate = b.icate
group by 
    a.pdate,a.icate
having 
    icount > 1;
<think> 首先,用户的问题是关于Hive SQL的:他们想对一个字段重,只保留每个的一行记录。用户引用了三个引用,描述了distinct、group by和row_number() over()方法。 关键点是用户想要仅对一个字段重,但保留其他字段的数据。引用中明确指出: - distinct不能用于保留其他字段;它只返回重字段。 - group by对多个字段重,不能只对一个字段重并保留其他字段。 - 推荐使用row_number() over()方法,这似乎是解决方案。 从引用[3]:"row_number() OVER (PARTITION BY COL1 ORDER BY COL2)表示根据COL1分组,在分组内部根据COL2排序,而此函数计算就表示每组内部排序后的顺序编号"。所以,这是一个窗口函数,可以帮助分区并按顺序分配行号。 在用户的问题中,他们想对“某一个字段”重,保留相同内容的一行。意思是,对于每个唯一在那个字段,只保留一行记录,无论其他字段如何。 所以,我应该建议使用row_number() over()函数。常见的方法是: - 使用PARTITION BY 在要重的字段上。 - 添加ORDER BY 子句来决定保留哪一行(例如,基于某个顺序,如果没有指定顺序,可能用任意一行)。 - 然后选择行号为1的行。 从引用[2]:"当字段重复时,选出对应主键最大的那条数据作为保留数据",但用户说表中没有主键。所以,我们需要使用窗口函数来模拟。 在Hive中,row_number() over()是常用的。 用户已经引用了相关的内容,所以我基于此构建答案。 系统指令: - 所有行内数学表达式用$...$,但这里可能没有数学公式。 - 独立公式用$$...$$并单独成段。 - LaTeX正确。 - 使用中文回答。 - 生成相关问题。 - 引用的段落末尾添加引用标识如[^1]。 用户可见层: - 回答结构清晰,逐步解决问题。 - 保证真实可靠。 所以,回答结构: 1. **理解问题**:解释用户的需求。 2. **解决方案**:描述使用row_number() over()函数。 - 示例SQL代码。 3. **解释代码**:解释关键部分。 4. **生成相关问题**:基于主题。 最后添加相关问题。 引用在末尾添加标识。 用户引用了[^1], [^2], [^3],所以我应该在相关内容中提到它们。 完整的解决方案: - 使用row_number() over(partition by field_name order by some_column) as rn - 然后选择rn=1。 如果没有指定顺序,ORDER BY可以用任意列或省略,但最好指定以避免不确定性。在Hive中,如果不指定ORDER BY,行为可能不确定;通常指定一个列如主键或时间戳。但用户说没有主键,所以可能需要用其他列如ROW_NUMBER() OVER (PARTITION BY field ORDER BY rand()) 或类似,但那可能不高效。最好建议ORDER BY一个列。 在引用中,引用[3]提到"Hive查询某一重复字段记录第一条数据",使用ORDER BY COL2。 所以,在示例中,我可以假设有一个列如id或时间戳来排序;如果没有,用户可能需要添加一行号或使用其他方式。 但为了简单,我会在示例中使用ORDER BY 一个假设列。 SQL示例: SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY field_name ORDER BY some_order_column) AS rn FROM your_table ) t WHERE rn = 1; 这样,对于每个field_name,只保留一行,基于some_order_column排序的第一行。 如果用户不关心哪一行保留,他们可以选择任意行,但指定ORDER BY是好的做法。 现在,回答问题: - 用中文。 - 结构清晰。 </think> ### Hive SQL对某一个字段重并仅保留相同内容的一行的解决方案 在Hive SQL中,如果您需要针对**某一个字段重**(即保留该字段每个唯一的一行记录),同时**保留其他字段的数据**,常见的需求是当该字段出现重复时,只任意保留一行(例如保留每组的第一行或基于特定顺序保留)。根据您提供的引用内容,distinct方法无法保留其他字段,group by则会强制对所有字段重,而row_number() over()窗口函数是实现这一目标的推荐方法[^1][^2][^3]。下面我将逐步解释解决方案,并提供示例SQL代码。 #### 解决方案的核心思路 使用窗口函数ROW_NUMBER() OVER(): - 通过PARTITION BY子句对要重的字段进行分组。 - 通过ORDER BY子句指定组内排序规则(决定保留哪一行,如果不指定顺序,行为可能不确定)。 - 为每组分配行号(rn),然后选择行号为1的记录。 - 这种方法能高效实现单字段重并保留其他字段的数据,尤其适用于Hive环境[^3]。 #### 详细步骤和示例 假设您有一个Hive表`sales_data`,其中包含以下字段: - `product_id`(要重的字段) - `sale_date`(销售日期,用于排序) - `amount`(销售额) - `region`(区域) 您的目标是:基于`product_id`重,每个`product_id`仅保留一行记录(例如,保留每个产品的最早销售记录),同时保留其他字段如`amount`和`region`。 ##### 步骤1: 编写SQL查询 使用ROW_NUMBER() OVER()函数创建子查询,分配行号,然后过滤出行号为1的记录。 ```sql SELECT * FROM ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY product_id -- 替换为您的重字段名 ORDER BY sale_date ASC -- 指定排序字段和顺序(ASC保留最早一行,DESC保留最新一行) ) AS rn -- 行号列 FROM sales_data -- 替换为您的表名 ) t WHERE rn = 1; ``` ##### 步骤2: 关键参数解释 - **PARTITION BY product_id**:对`product_id`字段分组,每个唯一产品ID被视为一个组。 - **ORDER BY sale_date ASC**:在组内按`sale_date`升序排序(保留最早一行)。您可以根据需求替换排序字段(如使用随机列`rand()`保留任意行,但建议指定列以避免不确定性)[^2][^3]。 - **rn = 1**:只保留每组的第一行记录(行号1)。 - **输出结果**:结果集将包含每个`product_id`的唯一一行,其他字段如`amount`和`region`被完整保留。 ##### 注意事项 - **为什么使用ROW_NUMBER()?** 它比distinct或group by更灵活,能处理无主键的表(如您提到的场景),并允许自定义保留规则(通过ORDER BY)[^2][^3]。 - **性能优化**:在Hive中,确保排序字段(如`sale_date`)已索引或分区,以避免全表扫描。如果表很大,可以先过滤数据或使用LIMIT测试。 - **特殊情况处理**: - 如果重字段包含NULL,ROW_NUMBER()会将NULL视为一个独立组(保留一行NULL记录),这与distinct行为一致[^1]。 - 如果不需要指定顺序,可设置`ORDER BY rand()`保留任意一行,但这可能降低性能。 - **示例输出**:假设原始数据有三行`product_id = 'A001'`,查询后将只保留一行(基于`sale_date`最早的那条)。 #### 常见场景扩展 - **仅保留最新记录**:修改ORDER BY为`ORDER BY sale_date DESC`。 - **无排序字段时**:如果表没有主键或时间列,可添加虚拟列(如`ORDER BY rand()`),但结果可能不稳定。 - **多字段重**:如果需对多个字段重,扩展PARTITION BY(如`PARTITION BY product_id, region`)。 此方法已在Hive中使用,能可靠解决单字段重问题[^3]。如果您提供更多表结构细节,我可以优化SQL示例。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

@nanami

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

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

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

打赏作者

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

抵扣说明:

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

余额充值