背景:开发过程中遇到一个场景,就是要保证拼接起来的json是按照json里的某个字段排序的,我们这里是按照省市的数量来保证拼接起来的json是倒序的,我先展示下结果,以便于理解:
{
"name": "广东",
"number": 9999999,
"value": [
113.264434,
23.129162
]
},
{
"name": "安徽",
"number": 999998,
"value": [
117.283042,
31.861190
]
},
{
"name": "河南",
"number": 34565,
"value": [
113.624946,
34.747040
]
},
拼接起来的json是按照number来倒序的。
本着可以用sql解决的事情就不要用别的语言来解决,否则会增加解决问题的复杂度,这里就用到了collect_set ,但是有个问题就是collect_set 拼接起来的数据是无序的,无法保证顺序,所以这里就要结合distribute by 和 sort by 一起来解决问题。上代码:
select
concat_ws(',',collect_set(strs))
from
(select
cnt,
strs
from
(select
cnt ,
concat('{','\"name\":','\"',province,'\"',',','\"number\":',cnt,',','\"value\":','[',longitude,',',latitude,']','}') as strs
from tblname
order by cnt desc)t1 --先做了一次排序了已经
distribute by strs
sort by cnt desc )t2
;
以上仅供参考~
加上一段解释吧友好些:
其目的是从 tblname
表中提取一组按 cnt
字段降序排列的 JSON 格式的字符串集合。代码主要分为以下几个步骤:
- 子查询 t1: 首先,从
tblname
表中选择cnt
和province
列,并构造一个 JSON 格式的字符串strs
。每个strs
字符串包含name
(省份名)、number
(对应cnt
值)和value
(由经度longitude
和纬度latitude
构成的数组)。子查询t1
结果按cnt
字段降序排列。
SELECT cnt, concat('{','\"name\":','\"',province,'\"',',','\"number\":',cnt,',','\"value\":','[',longitude,',',latitude,']','}') AS strs FROM tblname ORDER BY cnt DESC
-
DISTRIBUTE BY strs: 对子查询
t1
的结果应用DISTRIBUTE BY
子句,将数据分发到不同的 reducer(或计算节点),使得具有相同strs
值的行被发送到同一个 reducer。请注意,DISTRIBUTE BY
不影响行的全局排序。 -
SORT BY cnt DESC: 在每个 reducer 上,对收到的行进行局部排序,依据
cnt
字段进行降序排列。由于DISTRIBUTE BY strs
已经确保了相同strs
值的行被聚集在一起,这里SORT BY cnt DESC
主要是对这些行内部按照cnt
进行重新排序。 -
主查询: 最后,对所有 reducer 返回的结果进行汇总。由于每个 reducer 内部的行已经是按
cnt
降序排列的,使用collect_set(strs)
函数收集所有 reducer 返回的strs
字符串,并用concat_ws(',', collect_set(strs))
将这些字符串以逗号分隔的方式合并成一个单一的字符串。
综上所述,这段代码通过子查询 t1
先对原始数据按 cnt
进行全局降序排序,然后在每个 reducer 上对具有相同 strs
值的行进行局部降序排序。最后,主查询收集并合并这些已排序的 strs
字符串。尽管 DISTRIBUTE BY
和 SORT BY
会影响数据在 reducers 之间的分布和局部排序,但原始数据在子查询 t1
中已经进行了全局的 cnt
降序排序,因此最终得到的字符串集合中的元素(JSON 字符串)仍然是按照 cnt
值的降序排列的。