大家好,今天分享一下pyspark中各种sql join。
本文以学生和班级为单位进行介绍。
学生表有sid(学生id)、sname(学生姓名)、sclass(学生班级id)。
班级表有cid(班级id)、cname(班级名称)。
通过学生表的sclass和班级表的cid将两张表关联在一起。
下面是数据文件
数据的重点在于:
学生表的sclass是1,2,3,4,5
班级表的cid是1,2,4,6
即学生表比班级表多了3,5,班级表比学生表多了6
students.json
{"sid": 1, "sname": "xiaoming", "sclass":1}
{"sid": 2, "sname": "xiaogang", "sclass":2}
{"sid": 3, "sname": "xiaozhi", "sclass":3}
{"sid": 4, "sname": "xiaofang", "sclass":4}
{"sid": 5, "sname": "xiaohong", "sclass":5}
classes.json
{"cid": 1, "cname":"class1"}
{"cid": 2, "cname":"class2"}
{"cid": 4, "cname":"class4"}
{"cid": 6, "cname":"class6"}
from pyspark.sql import SparkSession
spark = SparkSession.builder.enableHiveSupport().getOrCreate()
stu_df = spark.read.json('./data/students.json')
class_df = spark.read.json('./data/classes.json')
注意两个json文件的路径,我是放在了当前工作目录的data子目录下。
然后看下读取的学生df和班级df。
stu_df.show()
+------+---+--------+
|sclass|sid| sname|
+------+---+--------+
| 1| 1|xiaoming|
| 2| 2|xiaogang|
| 3| 3| xiaozhi|
| 4| 4|xiaofang|
| 5| 5|xiaohong|
+------+---+--------+
class_df.show()
+---+------+
|cid| cname|
+---+------+
| 1|class1|
| 2|class2|
| 4|class4|
| 6|class6|
+---+------+
下面的代码帮助我们创建了stu和class两个视图。
stu_df.createOrReplaceTempView("stu")
class_df.createOrReplaceTempView("class")
然后看一下视图中的数据
首先是视图stu
temp_df = spark.sql("select * from stu")
temp_df.show()
+------+---+--------+
|sclass|sid| sname|
+------+---+--------+
| 1| 1|xiaoming|
| 2| 2|xiaogang|
| 3| 3| xiaozhi|
| 4| 4|xiaofang|
| 5| 5|xiaohong|
+------+---+--------+
然后是视图class
temp_df = spark.sql("select * from class")
temp_df.show()
+---+------+
|cid| cname|
+---+------+
| 1|class1|
| 2|class2|
| 4|class4|
| 6|class6|
+---+------+
我们是通过学生表的sclass和班级表的cid将两张表关联在一起,所以定义join表达式如下
expr = stu_df["sclass"]==class_df["cid"]
inner join比较容易理解,就是把两张表中互相匹配的行选择出来。
sql写法:
spark.sql("select sid, sclass, cname from stu join class on stu.sclass = class.cid").show()
df写法:
stu_df.join(class_df, expr).select("sid", "sclass", "cname").show()
上面提到:
学生表的sclass是1,2,3,4,5
班级表的cid是1,2,4,6
公共部分就是1,2,4
所以上述两行代码的输出都是
+---+------+------+
|sid|sclass| cname|
+---+------+------+
| 1| 1|class1|
| 2| 2|class2|
| 4| 4|class4|
+---+------+------+
这种join就是把两张表的所有记录选择出来,如果一张表里有对应数据,另一张表里没有对应数据,就用NULL代替。
sql写法:
spark.sql("select sid, sclass, cname from stu full outer join class on stu.sclass = class.cid").show()
df写法:
stu_df.join(class_df, expr, "outer").select("sid", "sclass", "cname").show()
上面提到:
学生表的sclass是1,2,3,4,5
班级表的cid是1,2,4,6
并集就是1,2,3,4,5,6
所以上述两行代码的输出都是
+----+------+------+
| sid|sclass| cname|
+----+------+------+
| 1| 1|class1|
| 2| 2|class2|
| 3| 3| NULL|
| 4| 4|class4|
| 5| 5| NULL|
|NULL| NULL|class6|
+----+------+------+
这种join就是把左边的表的所有行都取出来,如果右边表有匹配的行,就用匹配的行,如果右边表没有匹配的行,就用NULL代替。
sql写法:
spark.sql("select sid, sclass, cname from stu left outer join class on stu.sclass = class.cid").show()
df写法:
stu_df.join(class_df, expr, "left_outer").select("sid", "sclass", "cname").show()
上面提到:
学生表的sclass是1,2,3,4,5
班级表的cid是1,2,4,6
学生表是左表,班级表是右表,所以结果中会有1,2,3,4,5
3和5没有出现在右表中,所以对应字段用NULL代替。
所以上述两行代码的输出都是
+---+------+------+
|sid|sclass| cname|
+---+------+------+
| 1| 1|class1|
| 2| 2|class2|
| 3| 3| NULL|
| 4| 4|class4|
| 5| 5| NULL|
+---+------+------+
这种join就是把右边的表的所有行都取出来,如果左边表有匹配的行,就用匹配的行,如果左边表没有匹配的行,就用NULL代替。
sql写法:
spark.sql("select sid, sclass, cname from stu right outer join class on stu.sclass = class.cid").show()
df写法:
stu_df.join(class_df, expr, "right_outer").select("sid", "sclass", "cname").show()
上面提到:
学生表的sclass是1,2,3,4,5
班级表的cid是1,2,4,6
学生表是左表,班级表是右表,所以结果中会有1,2,4,6
6没有出现在左表中,所以对应字段用NULL代替。
所以上述两行代码的输出都是
+----+------+------+
| sid|sclass| cname|
+----+------+------+
| 1| 1|class1|
| 2| 2|class2|
| 4| 4|class4|
|NULL| NULL|class6|
+----+------+------+
这种join就是把左边表中能和右表中的行匹配的行取出来,只取左边表的记录。
sql写法:
spark.sql("select * from stu left semi join class on stu.sclass = class.cid").show()
df写法:
stu_df.join(class_df, expr, "left_semi").show()
上面提到:
学生表的sclass是1,2,3,4,5
班级表的cid是1,2,4,6
学生表是左表,班级表是右表
两张表匹配的行是1,2,4
所以上述两行代码的输出都是
+------+---+--------+
|sclass|sid| sname|
+------+---+--------+
| 1| 1|xiaoming|
| 2| 2|xiaogang|
| 4| 4|xiaofang|
+------+---+--------+
这种join是把左边表中不能和右边表中的行匹配的行取出来,也是只取左边表中的行。
sql写法:
spark.sql("select * from stu left anti join class on stu.sclass = class.cid").show()
df写法:
stu_df.join(class_df, expr, "left_anti").show()
上面提到:
学生表的sclass是1,2,3,4,5
班级表的cid是1,2,4,6
学生表是左表,班级表是右表
左边表的3和5不能和右边表中的行匹配
所以上述两行代码的输出都是
+------+---+--------+
|sclass|sid| sname|
+------+---+--------+
| 3| 3| xiaozhi|
| 5| 5|xiaohong|
+------+---+--------+
这种join就是把左边表中的所有行和右边表的所有行做乘积,相当于左边表中的每一行都和右边表中的所有行组合一次,即左边表×右边表。
sql写法:
spark.sql("select * from stu cross join class").show()
df写法:
stu_df.crossJoin(class_df).show()
所以上述两行代码的输出都是
+------+---+--------+---+------+
|sclass|sid| sname|cid| cname|
+------+---+--------+---+------+
| 1| 1|xiaoming| 1|class1|
| 1| 1|xiaoming| 2|class2|
| 1| 1|xiaoming| 4|class4|
| 1| 1|xiaoming| 6|class6|
| 2| 2|xiaogang| 1|class1|
| 2| 2|xiaogang| 2|class2|
| 2| 2|xiaogang| 4|class4|
| 2| 2|xiaogang| 6|class6|
| 3| 3| xiaozhi| 1|class1|
| 3| 3| xiaozhi| 2|class2|
| 3| 3| xiaozhi| 4|class4|
| 3| 3| xiaozhi| 6|class6|
| 4| 4|xiaofang| 1|class1|
| 4| 4|xiaofang| 2|class2|
| 4| 4|xiaofang| 4|class4|
| 4| 4|xiaofang| 6|class6|
| 5| 5|xiaohong| 1|class1|
| 5| 5|xiaohong| 2|class2|
| 5| 5|xiaohong| 4|class4|
| 5| 5|xiaohong| 6|class6|
+------+---+--------+---+------+
一文让你记住Pyspark下DataFrame的7种的Join 效果_pyspark dataframe join-CSDN博客
深入浅出leftsemijoin_笔记大全_设计学院
Spark权威指南之 - pyspark各种join - 知乎
Spark 系列(十二)—— Spark SQL JOIN 操作 - 掘金
JOIN - Spark 3.5.0 Documentation