之前对 SQL 还是不是非常熟悉的,但是现在或多或少还是会写一些计算任务。比如最近在推送将所有天级的耗时任务都从传统关系型数据库迁移至 Spark 集群当中进行计算,中间遇到一些有趣的小问题在这里记录一下。

Q: 我想按照某个字段分组并且把一组查询字段连起来得到一个 json 然后把结果作为一个字段应该怎么弄?

A: 这里我的思路是将我们需要 dumps 的字段给拼接起来,然后使用列表将同一个分组里面的是数据组合起来。然后过一个 udf 把列表中的记录处理成数组最后 json.dumps 一下即可。来看个栗子

# 先查询出要操作的目标信息 这一步可以和下面的操作合并,我这里为了方便看拆开了
df = ss.sql("""
SELECT
t1.pay_id,
t1.pay_money,
t1.user_id
FROM
analytics_db.hd_day_order_record t1
""") # 拼接目标字符串并且组合
df = df.select(
df.pay_id,
df.pay_money,
df.pay_user_id,
f.concat_ws('\001', df.pay_id, df.pay_user_id, df.pay_money).alias('sku_buys'))
) # 拼接一个重复 user_id 的 list
df = df.groupBy(df.pay_user_id).agg(f.collect_list('sku_buys').alias('sku_buys')) # 将 sku_buys 丢给一个 jsondump 的 udf 就可以得到结果了
df = df.select(df.pay_user_id, sb_json(df.sku_buys).alias('sku_buys'))

Q: 如果我想对目标进行分组,并且让他在组内有序应该怎么做?

A: 这通常被称为进行组内排序。其实我之前一直尝试用类似的语法来达到这种效果

df = ss.sql("""
SELECT
first(t1.sku_mode) AS sku_mode,
first(t1.exchange_type_t01) AS exchange_type_t01,
first(t1.user_id) AS user_id,
first(t1.pay_id) AS pay_id,
first(t1.charge_time) AS charge_time,
first(t2.has_yxs_payment) AS has_yxs_payment,
first(t2.has_sxy_payment) AS has_sxy_payment,
first(t2.has_cxy_payment) AS has_cxy_payment,
first(t2.has_sxy19_payment) AS has_sxy19_payment,
first(t2.sxy19_join_time) AS sxy19_join_time,
first(t2.yxs_join_time) AS yxs_join_time
FROM
d_exchange_info t1
JOIN
analytics_db.md_day_dump_users t2
ON
t2.the_day = '{}'
AND t1.user_id = t2.user_id
GROUP BY
t1.user_id
ORDER BY
charge_time
""".format(st))

其实这是错的,这里的 order by 并不能达到一个组内排序的效果,而是一个外部排序。所以这里取 first 是一个不稳定的结果。有时候你拿到的是一个结果,也许有时候你拿到的是另外一个结果。要进行组内排序,我们可以先用这样的思路,先对需要 order by 字段的表进行组内排序,然后再让他与其他表 join 获得更多的信息,这样就能保证是有序的。

这里我引用一个窗口函数来达到这样的效果。

        _df = ss.sql("""
SELECT
t1.user_id,
t1.pay_id,
t1.sku_mode,
t1.charge_time,
t1.exchange_type_t01,
ROW_NUMBER() OVER(PARTITION BY t1.user_id ORDER BY t1.charge_time) as rid
FROM
{} t1
WHERE
t1.refund_state =
""".format(exchange_info_table))
    _df = _df.filter(_df.rid==1)

我先使用窗口函数 ROW_NUMBER 以 user_id 分组并且根据 charge_time 对表一进行组内排序。得到结果之后,使用 filter 过滤一下 rid =1 的结果。再与另外一张表 join 得到补充信息就能达到想要的效果。

Q: 我想对结果进行转列应该怎么做?

A: 行转列 列转行可能是 SQL 计算里面会经常使用到的方法,但是对于 SQL 并不熟悉的同学(比如我)就不知道该怎么整来看个例子

df = ss.sql("""
SELECT
user_id,
sku_mode,
credit_score
FROM
analytics_db.hd_day_user_credit
WHERE
gain_time >= '{}'
AND gain_time < '{}'
AND the_day = '{}'
""".format(start_time, end_time, st))
# df.show()

展示的数据类似于

+--------------------+--------+------------+
| user_id|sku_mode|credit_score|
+--------------------+--------+------------+
|d394899919216bc10...| yxs| |
|625002ad625bc9a69...| yxs| |
|8dd11e29bf50cb8c8...| cxy| |
|0f0b88ff589cb46cd...| yxs| |
|eeb8e839139876971...| yxs| |
|f63b2b9c8340d3c80...| cxy| |
|806c9f0349e7e8389...| cxy| |
|bf312181eaaa0ec9e...| yxs| |
|ee4a7984dc40cabbf...| yxs| |
|7a6b15f16c1f782de...| sxy19| |
+--------------------+--------+------------+
only showing top rows

我们可以基于此将 sku_mode 一样的类型合并进行行转列变换

df = df.groupby('user_id').pivot(
'sku_mode', ['yxs', 'cxy', 'sxy', 'sxy19']
).agg(
f.sum('credit_score')
).fillna()

这句话的意思是根据 user_id 进行分组,并且将 sku_mode 的行转列,需要转列的类型需要在后面的 list 中添加,并且列里记录 各sku_mode credit_score 汇总的量。

+--------------------+---+---+---+-----+
| user_id|yxs|cxy|sxy|sxy19|
+--------------------+---+---+---+-----+
|5ec336994e7b5d73f...| | | | |
|06b1120a4544b1b8a...| | | | |
|6fe19e193ad43bafc...| | | | |
|3e5f9fc4550ae7cba...| | | | |
|b1d1d856e28908f5a...| | | | |
|7a065e02ed1693cf4...| | | | |
|651f9f0b11de08003...| | | | |
|d02491502946aba2f...| | | | |
|e24b58cb87762b2da...| | | | |
|925f6a832b1a95c45...| | | | |
+--------------------+---+---+---+-----+
only showing top rows

Q: 我想对结果进行列转行应该怎么做?

A: 我们接着上面的例子来讲 unpivot 行转列的逆操作。还是接着刚才那个栗子。

df2 = df
df2 = df2.selectExpr("user_id",
"stack(4, 'yxs', yxs, 'cxy', cxy, 'sxy', sxy, 'sxy19', sxy19) AS (sku_mode, credit_score)") df.where(df.user_id=='e24b58cb87762b2da9fa118316e9c91a').show(, False)
df2.filter(df2.user_id=='e24b58cb87762b2da9fa118316e9c91a').show(, False) +--------------------------------+---+---+---+-----+
|user_id |yxs|cxy|sxy|sxy19|
+--------------------------------+---+---+---+-----+
|e24b58cb87762b2da9fa118316e9c91a| | | |
+--------------------------------+---+---+---+-----+ +--------------------------------+--------+------------+
|user_id |sku_mode|credit_score|
+--------------------------------+--------+------------+
|e24b58cb87762b2da9fa118316e9c91a|yxs | |
|e24b58cb87762b2da9fa118316e9c91a|cxy | |
|e24b58cb87762b2da9fa118316e9c91a|sxy | |
|e24b58cb87762b2da9fa118316e9c91a|sxy19 | |
+--------------------------------+--------+------------+

可以看到我们通过这种办法将列重新组合成行记录。这里需要多延伸一下,这里使用的 selectExpr 语句的语意是将里面的参数直接解析成 select 里面的内容。

stack 函数是 spark 中的 func.他接收无数个参数,第一个参数 n 的意义是转换的行数,对二个开始到后面的参数都是内容。

stack 的作用是将第二个开始的到后面的参数 塞进 n 行中。

举个栗子来说哦,就是上文使用的

stack(, 'yxs', yxs, 'cxy', cxy, 'sxy', sxy, 'sxy19', sxy19) AS (sku_mode, credit_score)

这里的语意就是切分成 4 行。从第二个字段开始 字符串部分表达的是匹配的 sku_mode 分辨是('yxs', 'cxy', 'sxy', 'sxy19')然后跟在他们后面的分别是credit_score 的值  然后展现成两列两个字段。有点绕需要多理解一下。最好是在 spark 终端中试一试比较有感觉。

之后还有有意思的姿势会继续补充在这里。

Reference:

https://sparkbyexamples.com/how-to-pivot-table-and-unpivot-a-spark-dataframe/   How to Pivot and Unpivot a Spark SQL DataFrame

https://stackoverflow.com/questions/56371391/in-group-sort-table-join-a-another-table-use-first-func/56371513#56371513

Pyspark 最近使用的一些有趣姿势的梳理的更多相关文章

  1. 【洛谷】P2000 拯救世界

    题解 小迪的blog : https://www.cnblogs.com/RabbitHu/p/9178645.html 请大家点推荐并在sigongzi的评论下面点支持谢谢! 掌握了小迪生成函数的有 ...

  2. Writeup:第五届上海市大学生网络安全大赛-Web

    目录 Writeup:第五届上海市大学生网络安全大赛-Web 一.Decade 无参数函数RCE(./..) 二.Easysql 三.Babyt5 二次编码绕过strpos Description: ...

  3. CTF SQL注入

    目录 一.宽字节注入 二.基于约束的注入 三.报错注入 四.时间盲注 五.bool盲注 六.order by的注入 六.INSERT.UPDATE.DELETE相关的注入 七.堆叠注入 八.常用绕过 ...

  4. 【BZOJ-4127】Abs 树链剖分 + 线段树 (有趣的姿势)

    4127: Abs Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 381  Solved: 132[Submit][Status][Discuss] ...

  5. 一次有趣的XSS漏洞挖掘分析(1)

    最近认识了个新朋友,天天找我搞XSS.搞了三天,感觉这一套程序还是很有意思的.因为是过去式的文章,所以没有图.但是希望把经验分享出来,可以帮到和我一样爱好XSS的朋友.我个人偏爱富文本XSS,因为很有 ...

  6. 【转载】soapui基于持续集成工具自动化运行的调研姿势

    soapui中的testrunner.bat调研姿势,用于自动化测试副标题:soapui基于持续集成工具自动化运行的调研姿势 各位亲爱的同仁们,大家好吗?最近项目在搞持续集成工具,我们的测试用例都是基 ...

  7. FaceRank,最有趣的 TensorFlow 入门实战项目

    FaceRank,最有趣的 TensorFlow 入门实战项目 TensorFlow 从观望到入门! https://github.com/fendouai/FaceRank 最有趣? 机器学习是不是 ...

  8. Pyspark spark-submit 集群提交任务以及引入虚拟环境依赖包攻略

    网上提交 scala spark 任务的攻略非常多,官方文档其实也非常详细仔细的介绍了 spark-submit 的用法.但是对于 python 的提交提及得非常少,能查阅到的资料非常少导致是有非常多 ...

  9. Bypass X-WAF SQL注入防御(多姿势)

    0x00 前言 ​ X-WAF是一款适用中.小企业的云WAF系统,让中.小企业也可以非常方便地拥有自己的免费云WAF. ​ 本文从代码出发,一步步理解WAF的工作原理,多姿势进行WAF Bypass. ...

随机推荐

  1. Java隐式类型转换和强制类型转换

    一.强制类型转换 char 和 整型之间的类型转换 char a7 = 'a'; System.out.println(a7); System.out.println( (int)a7 ); Syst ...

  2. 一个 Vim 重度用户总结的 vim 超全指南

    我本人是 Vim 的重度使用者,就因为喜欢上这种双手不离键盘就可以操控一切的feel,Vim 可以让我对文本的操作更加精准.高效. 对于未使用过 Vim 的朋友来说,可能还无法体会到这种感觉.由于使用 ...

  3. 【转载】 C#中List集合使用InsertRange方法在指定的位置插入另一个list集合

    在C#的List集合操作过程中,如果在集合中的某个位置插入一个新的元素对象,可以使用Insert方法进行操作.其实List集合也提供了在特定的位置插入另一个集合,然后另一个集合的数据整个写入到当前集合 ...

  4. javascript设计模式之适配器模式

    ---恢复内容开始--- 定义: 是指讲一个接口转换成客户端希望 的另外一个接口,该模式使得原本不兼容的类可以一起工作.适配器模式的作用事解决两个软件实体间的接口不兼容的问题. 生活中的实例: USB ...

  5. 浅谈React编程思想

    React是Facebook推出的面向视图层开发的一个框架,用于解决大型应用,包括如何很好地管理DOM结构,是构建大型,快速Web app的首选方式. React使用JavaScript来构建用户界面 ...

  6. docker 安装及使用介绍

    docker 安装及使用用介绍 安装docker所依赖的基础环境 1 64 bits CPU 2 Linux Kernel 3.10+ //如果低于则需要手动给内核打补丁.因为分层构建联合挂载系统得在 ...

  7. (Linux基础学习)第五章:Linux中的screen应用

    第1节:安装screen1.加载系统镜像文件,因为screen的安装包在系统镜像文件中图001 2.列出系统上所有的磁盘[root@centos6 ~]# lsblk图002 3.安装screen应用 ...

  8. Jmeter 中正则表达式提取器Regular Expression Extractor

    正则表达式提取器点击后置处理器中Post Processors 中的正则表达式提取器 Regular Expression Extractor Appy to: 表示作用于哪一个请求Main samp ...

  9. linux网络编程之socket编程(三)

    今天继续对socket编程进行学习,在学习之前,需要回顾一下上一篇中编写的回射客户/服务器程序(http://www.cnblogs.com/webor2006/p/3923254.html),因为今 ...

  10. ted演讲小总结(持续更新_12月15日)

    目录 2019年12月1日 星期日 2019年12月2日 星期一 2019年12月3日 星期二 2019年12月8日 星期日 2019年12月15日 星期日(这个演讲相对来说不好理解,因为这类逻辑暂时 ...