Hive中Select语句使用Partition By的原因及省略影响
搞懂Hive里的两种“分区”:表分区 vs 窗口函数Partition By
嘿,我太懂你为啥会懵了——这俩玩意儿都叫“分区”,但根本是完全不同的东西!咱们一步步拆解你的问题:
先明确两个“分区”的本质区别
1. 建表时的PARTITIONED BY(表分区)
这是物理存储层面的优化:Hive会把你的数据按照指定字段(比如dt日期、region地区)拆成不同的目录/文件存在HDFS上。目的是查询时只扫描你需要的分区数据,不用扫全表,大幅提升速度。比如你查dt='2024-05-20'的数据,Hive直接去对应目录读,其他日期的分区碰都不碰。这个分区是和数据存储绑定的,一旦建表就固定了(当然也可以后期加分区)。
2. 窗口函数里的PARTITION BY
这是查询逻辑层面的分组:它完全和数据怎么存没关系,只是在查询过程中,临时把结果集按照指定字段分成一个个独立的“窗口”(或者说小组),然后在每个窗口内部单独做计算(比如row_number()、sum()、avg())。它的目的是实现分组内的精细化计算,而不是全表计算。
回到你的SQL例子:为啥必须加partition by session_id?
你的需求是找出每个session里最近的一次点击记录(也就是每个session中ts最大的那条)。咱们拆解这段SQL:
SELECT user_id, page_name, recent_click FROM ( SELECT user_id, page_name, row_number() over (partition by session_id order by ts desc) as recent_click from clicks_data ) T WHERE recent_click = 1
这里的partition by session_id是在告诉row_number():
把所有点击数据按
session_id分成一个个小组,每个小组(每个session)内部,按照ts从晚到早排序,然后给每条记录编一个序号——序号1就是这个session里最新的点击。
如果省略这个partition by session_id,会发生什么?
row_number()会把整个clicks_data表的所有记录当成一个大窗口,然后全表按ts倒序编序号。- 最后你
WHERE recent_click = 1只能拿到全表时间最晚的那一条记录,而不是每个session各一条最新记录——这完全不符合你的业务需求!
举个直观的例子:
假设你有3个session,每个session有3条点击记录:
| session_id | ts | page_name |
|---|---|---|
| s1 | 2024-05-20 10:05:00 | pageA |
| s1 | 2024-05-20 10:03:00 | pageB |
| s1 | 2024-05-20 10:01:00 | pageC |
| s2 | 2024-05-20 10:06:00 | pageX |
| s2 | 2024-05-20 10:04:00 | pageY |
| s3 | 2024-05-20 10:07:00 | pageZ |
- 加
partition by session_id后,每个session内部编序号:s1的第一条是1,s2的第一条是1,s3的第一条是1,最后能拿到3条结果(每个session的最新点击)。 - 不加的话,全表按ts倒序编序号:s3的pageZ是1,s2的pageX是2,s1的pageA是3...最后只能拿到pageZ这一条,完全不是你要的结果。
总结一下
- 表分区:管数据怎么存,帮你查得更快;
- 窗口函数的Partition By:管查询时怎么分组计算,帮你实现分组内的统计需求;
- 你的SQL里的
partition by session_id是为了实现“每个session内取最新点击”的逻辑,和建表时的表分区没有半毛钱关系!
内容的提问来源于stack exchange,提问作者MiamiBeach
相关产品推荐
相关产品推荐

