HiveQL collect_list保持顺序小记

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HiveQL collect_list保持顺序小记相关的知识,希望对你有一定的参考价值。

参考技术A 有以下Hive表的定义:

这张表是我们业务里话题推荐分值表的简化版本。category_id代表分类ID,topic_id是话题ID,score是评分值。rank代表每个分类下话题分值的排名,用开窗函数计算出来的:
row_number() over(partition by t.category_id order by t.score desc)

在对外提供推荐结果时,我们会将每个小组下排名前1000的话题ID取出,拼成一个逗号分隔的字符串,处理之后送入HBase供调用方查询。拼合的SQL语句如下:

看起来没什么问题?但实际上是错误的。输出结果中总会有一些category_id对应的列表顺序异常,比如本来排名正数与排名倒数的两批ID调换了位置,即rank变成了 n-3, n-2, n-1, n, 5, 6, 7, ..., n-4, 1, 2, 3, 4 。

产生这个问题的根本原因自然在MapReduce,如果启动了多于一个mapper/reducer来处理数据,select出来的数据顺序就几乎肯定与原始顺序不同了。考虑把mapper数固定成1比较麻烦(见 我之前写的那篇Hive调优文章 ),也不现实,所以要迂回地解决问题:把rank加进来再进行一次排序,拼接完之后把rank去掉。如下:

这里将rank放在了topic_id之前,用冒号分隔,然后用sort_array函数对collect_list之后的结果进行排序(只支持升序)。特别注意,rank必须要在高位补足够的0对齐,因为排序的是字符串而不是数字,如果不补0的话,按字典序排序就会变成 1, 10, 11, 12, 13, 2, 3, 4... ,又不对了。
将排序的结果拼起来之后,用regexp_replace函数替换掉冒号及其前面的数字,大功告成。

顺便看一下Hive源码中collect_list和collect_set函数对应的逻辑吧。

HiveQL逻辑执行顺序

FROM->WHERE->GROUP BY->HAVING->SELECT->ORDER BY

Hive总是按照从左到右的顺序执行的,如a、b、c三个表关联

select a.id,b.colname,c.colname from a join b on a.id = b.id join c on a.id = c.id

大多数情况下,hive会对每个join连接对象启动一个MapReduce任务,上面的列子首先会启动一个MapReduce job对表a和表b进行连接操作,然后会再启动一个MapReduce job将第一个MapReduce job的输出和表c进行连接操作。当对3个或以上的表进行join连接时,如果每个on子句都使用相同的连接键,那么只会产生一个MapReduce job。

因此对于有join的操作要特别注意:
将数据量小的表放在join的左边,保证查询中表的大小从左到右是依次增加的。
普通的join操作,会在map端根据key的hash值,shuffle到某一个reduce上去,在reduce端做join连接操作,内存中缓存join左边的表,遍历右边的表

Oracle、SQL Server中SELECT的列别名只有ORDER BY可以用

HiveSQL基于MySQL存储的元数据信息,HAVING后可使用SELECT指定的别名

逻辑执行顺序:

1.FROM <left_table>
3.<join_type> JOIN <right_table> 2.ON <join_condition>
4.WHERE <where_condition>
5.GROUP BY <group_by_list>
6.WITH {cube|rollup}
7.HAVING <having_condition>
8.SELECT 9.DISTINCT 11.<top_specification> <select_list>
10.ORDER BY <order_by_list>

---------------------------------------------------------------------------------------------------------------------------

1.FROM:对FROM子句中前两个表执行笛卡尔积生成虚拟表vt1
2.ON:对vt1表应用ON筛选器只有满足<join_condition>为真的行才被插入vt2
3.OUTER(join):如果指定了 OUTER JOIN保留表(preserved table)中未找到的行将行作为外部行添加到vt2 生成t3如果from包含两个以上表则对上一个联结生成的结果表和下一个表重复执行步骤和步骤直接结束
4.WHERE:对vt3应用 WHERE 筛选器只有使<where_condition>为true的行才被插入vt4
5.GROUP BY:按GROUP BY子句中的列列表对vt4中的行分组生成vt5
6.CUBE|ROLLUP:把超组(supergroups)插入vt6 生成vt6
7.HAVING:对vt6应用HAVING筛选器只有使<having_condition>为true的组才插入vt7
8.SELECT:处理select列表产生vt8
9.DISTINCT:将重复的行从vt8中去除产生vt9
10.ORDER BY:将vt9的行按order by子句中的列列表排序生成一个游标vc10
11.TOP:从vc10的开始处选择指定数量或比例的行生成vt11 并返回调用者

--SQLServer中支持Values claues With table constructor,返回结果200  

SELECT ROUND((100*(100000/new.uv))-100,4) AS rate FROM (VALUES(32525))new(uv)
SELECT ROUND((100*(100000/32525))-100,4) AS rate

--Hive中不支持Values claues With table constructor  

FAILED: SemanticException [Error 10296]: Values clause with table constructor not yet supported

--用实际值测试返回结果207.4558,相比SQLServer不需要显式整型的转换了  

SELECT ROUND((100*(100000/32525))-100,4) AS rate

    

以上是关于HiveQL collect_list保持顺序小记的主要内容,如果未能解决你的问题,请参考以下文章

collect_list() 是不是保持行的相对顺序?

PySpark 使用 collect_list 收集不同长度的数组

基于spark中的列组合数据

collect_list 通过保留基于另一个变量的顺序

Groupby 和 collect_list 基于 PySpark 中的另一列维护顺序

HiveQL逻辑执行顺序