在不使用 ROW_NUMBER() OVER 函数的情况下获取分区内行(排名)的序号
Posted
技术标签:
【中文标题】在不使用 ROW_NUMBER() OVER 函数的情况下获取分区内行(排名)的序号【英文标题】:Get sequential number of a row (rank) within a partition without using ROW_NUMBER() OVER function 【发布时间】:2014-05-02 10:03:12 【问题描述】:我需要按分区(或组)对行进行排名,即如果我的源表是:
NAME PRICE
---- -----
AAA 1.59
AAA 2.00
AAA 0.75
BBB 3.48
BBB 2.19
BBB 0.99
BBB 2.50
我想获取目标表:
RANK NAME PRICE
---- ---- -----
1 AAA 0.75
2 AAA 1.59
3 AAA 2.00
1 BBB 0.99
2 BBB 2.19
3 BBB 2.50
4 BBB 3.48
通常我会使用 ROW_NUMBER() OVER
函数,所以在 Apache Hive 中它会是:
select
row_number() over (partition by NAME order by PRICE) as RANK,
NAME,
PRICE
from
MY_TABLE
;
不幸的是,Cloudera Impala 不支持(目前)ROW_NUMBER() OVER
功能,所以我正在寻找解决方法。最好不要使用 UDAF,因为在政治上很难说服将其部署到服务器。
【问题讨论】:
【参考方案1】:如果您不能使用相关子查询来做到这一点,您仍然可以使用连接来做到这一点:
select t1.name, t1.price,
coalesce(count(t2.name) + 1, 1)
from my_table t1 join
my_table t2
on t2.name = t1.name and
t2.price < t1.price
order by t1.name, t1.price;
请注意,这并不完全符合row_number()
除非对于给定的name
,所有价格都是不同的。这个公式其实等价于rank()
。
对于row_number()
,您需要一个唯一的行标识符。
顺便说一下,下面相当于dense_rank()
:
select t1.name, t1.price,
coalesce(count(distinct t2.name) + 1, 1)
from my_table t1 join
my_table t2
on t2.name = t1.name and
t2.price < t1.price
order by t1.name, t1.price;
【讨论】:
除了使用内连接之外,还可以使用左连接,其中“coalesce(..., 1) 部分可能会被删除。【参考方案2】:对于不支持窗口功能的系统,通常的解决方法是这样的:
select name,
price,
(select count(*)
from my_table t2
where t2.name = t1.name -- this is the "partition by" replacement
and t2.price < t1.price) as row_number
from my_table t1
order by name, price;
SQLFiddle 示例:http://sqlfiddle.com/#!2/3b027/2
【讨论】:
我已经尝试过了,但不幸的是,Impala 目前不支持子查询。【参考方案3】:对于如何使用 Impala 并没有真正的答案,但 Hadoop 上的其他 SQL 解决方案已经提供了分析和子查询选项。如果没有这些功能,您可能将不得不依赖多步骤流程或一些 UDAF。
我是 InfiniDB 的架构师 InfiniDB 支持解析函数和子查询。http://infinidb.co
在 Radiant Advisors 的基准测试中查看查询 8,它是您所追求的类似风格的查询,利用排名分析功能。 Presto 也能够以较慢 (80x) 的速度运行这种样式查询 http://radiantadvisors.com/wp-content/uploads/2014/04/RadiantAdvisors_Benchmark_SQL-on-Hadoop_2014Q1.pdf
来自基准的查询(查询 8)
SELECT
sub.visit_entry_idaction_url,
sub.name,
lv.referer_url,
sum(visit_ total_time) total_time,
count(sub.idvisit),
RANK () OVER (PARTITION BY sub. visit_entry_idaction_url
ORDER BY
count(sub.idvisit)) rank_by_visits,
DENSE_RANK() OVER (PARTITION BY sub.visit_entry_idaction_url
ORDER BY
count(visit_total_time)) rank_by_ time_spent
FROM
log_visit lv,
(
SELECT
visit_entry_idaction_url,
name,
idvisit
FROM
log_visit JOIN log_ action
ON
(visit_entry_idaction_url = log_action.idaction)
WHERE
visit_ entry_idaction_url between 2301400 AND
2302400) sub
WHERE
lv.idvisit = sub.idvisit
GROUP BY
1, 2, 3
ORDER BY
1, 6, 7;
结果
Hive 0.12 Not Executable
Presto 0.57 506.84s
InfiniDB 4.0 6.37s
Impala 1.2 Not Executable
【讨论】:
以上是关于在不使用 ROW_NUMBER() OVER 函数的情况下获取分区内行(排名)的序号的主要内容,如果未能解决你的问题,请参考以下文章
ROW_NUMBER() OVER()函数用法;(分组,排序),partition by
ROW_NUMBER() OVER()函数用法;(分组,排序),partition by
ROW_NUMBER() OVER()函数用法;(分组,排序),partition by