Cloudera Impala SQL查询指定列唯一值对应分组首行方法
问题说明
现有表adsb_table存储ADS-B飞行数据,结构如下:
callsign:飞行器呼号time:数据上报时间戳speed:飞行速度
表样例数据:
| callsign | time | speed |
|---|---|---|
| A | 23421 | 431 |
| A | 23422 | 426 |
| A | 23423 | 459 |
| B | 23424 | 521 |
| B | 23425 | 601 |
| B | 23426 | 401 |
| C | 23427 | 454 |
| C | 23428 | 499 |
| C | 23429 | 621 |
需求为返回每个唯一callsign对应的首条(时间最早)记录,预期输出:
| callsign | time | speed |
|---|---|---|
| A | 23421 | 431 |
| B | 23424 | 521 |
| C | 23427 | 454 |
原尝试执行的SQL未返回预期结果:
SELECT callsign, time, speed FROM adsb_table WHERE speed>400 ORDER BY callsign GROUP by callsign
错误原因
原SQL存在两处核心问题:
- 语法顺序不符合SQL规范(Impala同样遵循该规范):子句执行顺序必须是
WHERE -> GROUP BY -> ORDER BY,ORDER BY写在GROUP BY前本身就会触发语法报错。 GROUP BY逻辑错误:直接按callsign分组时,未做聚合处理的time、speed字段返回值是分组内随机取值,Impala不会自动按排序返回分组首行,根本无法保证结果符合预期。
正确实现方案(Impala 兼容)
需求本质是取每个callsign分组内time最小的记录,以下两种写法均可实现:
方案1:窗口函数写法(推荐,性能最优)
用ROW_NUMBER()窗口函数实现分组内排序取Top1,是这类需求的通用最优写法,Impala全版本支持:
SELECT callsign, time, speed FROM ( SELECT callsign, time, speed, ROW_NUMBER() OVER (PARTITION BY callsign ORDER BY time ASC) AS row_rank FROM adsb_table WHERE speed > 400 -- 保留原有的速度过滤条件 ) tmp WHERE row_rank = 1;
方案2:子查询关联写法(兼容极老版本场景)
如果遇到特殊环境不支持窗口函数,可以先分组取每个呼号对应的最早时间,再关联原表取完整记录:
SELECT a.callsign, a.time, a.speed FROM adsb_table a JOIN ( SELECT callsign, MIN(time) AS first_time FROM adsb_table WHERE speed > 400 GROUP BY callsign ) b ON a.callsign = b.callsign AND a.time = b.first_time WHERE a.speed > 400;
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

