一、 数据准备

本文主要介绍 Spark SQL 的多表连接,需要预先准备测试数据。分别创建员工和部门的 Datafame,并注册为临时视图,代码如下:

val spark = SparkSession.builder().appName("aggregations").master("local[2]").getOrCreate()

val empDF = spark.read.json("/usr/file/json/emp.json")
empDF.createOrReplaceTempView("emp")

val deptDF = spark.read.json("/usr/file/json/dept.json")
deptDF.createOrReplaceTempView("dept")

两表的主要字段如下:

emp 员工表
 |-- ENAME: 员工姓名
 |-- DEPTNO: 部门编号
 |-- EMPNO: 员工编号
 |-- HIREDATE: 入职时间
 |-- JOB: 职务
 |-- MGR: 上级编号
 |-- SAL: 薪资
 |-- COMM: 奖金  
dept 部门表
 |-- DEPTNO: 部门编号
 |-- DNAME:  部门名称
 |-- LOC:    部门所在城市

注:emp.json,dept.json 可以在本仓库的resources 目录进行下载。

二、连接类型

Spark 中支持多种连接类型:

  • Inner Join : 内连接;
  • Full Outer Join : 全外连接;
  • Left Outer Join : 左外连接;
  • Right Outer Join : 右外连接;
  • Left Semi Join : 左半连接;
  • Left Anti Join : 左反连接;
  • Natural Join : 自然连接;
  • Cross (or Cartesian) Join : 交叉 (或笛卡尔) 连接。

其中内,外连接,笛卡尔积均与普通关系型数据库中的相同,如下图所示:

这里解释一下左半连接和左反连接,这两个连接等价于关系型数据库中的 INNOT IN 字句:

-- LEFT SEMI JOIN
SELECT * FROM emp LEFT SEMI JOIN dept ON emp.deptno = dept.deptno
-- 等价于如下的 IN 语句
SELECT * FROM emp WHERE deptno IN (SELECT deptno FROM dept)

-- LEFT ANTI JOIN
SELECT * FROM emp LEFT ANTI JOIN dept ON emp.deptno = dept.deptno
-- 等价于如下的 IN 语句
SELECT * FROM emp WHERE deptno NOT IN (SELECT deptno FROM dept)

所有连接类型的示例代码如下:

2.1 INNER JOIN

// 1.定义连接表达式
val joinExpression = empDF.col("deptno") === deptDF.col("deptno")
// 2.连接查询
empDF.join(deptDF,joinExpression).select("ename","dname").show()

// 等价 SQL 如下:
spark.sql("SELECT ename,dname FROM emp JOIN dept ON emp.deptno = dept.deptno").show()

2.2 FULL OUTER JOIN

empDF.join(deptDF, joinExpression, "outer").show()
spark.sql("SELECT * FROM emp FULL OUTER JOIN dept ON emp.deptno = dept.deptno").show()

2.3 LEFT OUTER JOIN

empDF.join(deptDF, joinExpression, "left_outer").show()
spark.sql("SELECT * FROM emp LEFT OUTER JOIN dept ON emp.deptno = dept.deptno").show()

2.4 RIGHT OUTER JOIN

empDF.join(deptDF, joinExpression, "right_outer").show()
spark.sql("SELECT * FROM emp RIGHT OUTER JOIN dept ON emp.deptno = dept.deptno").show()

2.5 LEFT SEMI JOIN

empDF.join(deptDF, joinExpression, "left_semi").show()
spark.sql("SELECT * FROM emp LEFT SEMI JOIN dept ON emp.deptno = dept.deptno").show()

2.6 LEFT ANTI JOIN

empDF.join(deptDF, joinExpression, "left_anti").show()
spark.sql("SELECT * FROM emp LEFT ANTI JOIN dept ON emp.deptno = dept.deptno").show()

2.7 CROSS JOIN

empDF.join(deptDF, joinExpression, "cross").show()
spark.sql("SELECT * FROM emp CROSS JOIN dept ON emp.deptno = dept.deptno").show()

2.8 NATURAL JOIN

自然连接是在两张表中寻找那些数据类型和列名都相同的字段,然后自动地将他们连接起来,并返回所有符合条件的结果。

spark.sql("SELECT * FROM emp NATURAL JOIN dept").show()

以下是一个自然连接的查询结果,程序自动推断出使用两张表都存在的 dept 列进行连接,其实际等价于:

spark.sql("SELECT * FROM emp JOIN dept ON emp.deptno = dept.deptno").show()

由于自然连接常常会产生不可预期的结果,所以并不推荐使用。

三、连接的执行

在对大表与大表之间进行连接操作时,通常都会触发 Shuffle Join,两表的所有分区节点会进行 All-to-All 的通讯,这种查询通常比较昂贵,会对网络 IO 会造成比较大的负担。

而对于大表和小表的连接操作,Spark 会在一定程度上进行优化,如果小表的数据量小于 Worker Node 的内存空间,Spark 会考虑将小表的数据广播到每一个 Worker Node,在每个工作节点内部执行连接计算,这可以降低网络的 IO,但会加大每个 Worker Node 的 CPU 负担。

是否采用广播方式进行 Join 取决于程序内部对小表的判断,如果想明确使用广播方式进行 Join,则可以在 DataFrame API 中使用 broadcast 方法指定需要广播的小表:

empDF.join(broadcast(deptDF), joinExpression).show()

参考资料

  1. Matei Zaharia, Bill Chambers . Spark: The Definitive Guide[M] . 2018-02

更多大数据系列文章可以参见 GitHub 开源项目大数据入门指南

Spark 系列(十二)—— Spark SQL JOIN 操作的更多相关文章

  1. SQL Server 2008空间数据应用系列十二:Bing Maps中呈现GeoRSS订阅的空间数据

    原文:SQL Server 2008空间数据应用系列十二:Bing Maps中呈现GeoRSS订阅的空间数据 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft SQL Se ...

  2. MySQL 系列(二) 你不知道的数据库操作

    第一篇:MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决 第二篇:MySQL 系列(二) 你不知道的数据库操作 本章内容: 查看\创建\使用\删除 数据库 用户管理及授权实战 局域网 ...

  3. Alamofire源码解读系列(十二)之请求(Request)

    本篇是Alamofire中的请求抽象层的讲解 前言 在Alamofire中,围绕着Request,设计了很多额外的特性,这也恰恰表明,Request是所有请求的基础部分和发起点.这无疑给我们一个Req ...

  4. 爬虫系列(十二) selenium的基本使用

    一.selenium 简介 随着网络技术的发展,目前大部分网站都采用动态加载技术,常见的有 JavaScript 动态渲染和 Ajax 动态加载 对于爬取这些网站,一般有两种思路: 分析 Ajax 请 ...

  5. 第十二章 Python文件操作【转】

    12.1 open() open()函数作用是打开文件,返回一个文件对象. 用法格式:open(name[, mode[, buffering[,encoding]]]) -> file obj ...

  6. Web 前端开发精华文章推荐(jQuery、HTML5、CSS3)【系列十二】

    2012年12月12日,[<Web 前端开发人员和设计师必读文章>系列十二]和大家见面了.梦想天空博客关注 前端开发 技术,分享各种增强网站用户体验的 jQuery 插件,展示前沿的 HT ...

  7. struts2官方 中文教程 系列十二:控制标签

    介绍 struts2有一些控制语句的标签,本教程中我们将讨论如何使用 if 和iterator 标签.更多的控制标签可以参见 tags reference. 到此我们新建一个struts2 web 项 ...

  8. Spark系列之二——一个高效的分布式计算系统

    1.什么是Spark? Spark是UC Berkeley AMP lab所开源的类Hadoop MapReduce的通用的并行计算框架,Spark基于map reduce算法实现的分布式计算,拥有H ...

  9. Spark(十二)--性能调优篇

    一段程序只能完成功能是没有用的,只能能够稳定.高效率地运行才是生成环境所需要的. 本篇记录了Spark各个角度的调优技巧,以备不时之需. 一.配置参数的方式和观察性能的方式 额...从最基本的开始讲, ...

  10. Spark(十二)SparkSQL简单使用

    一.SparkSQL的进化之路 1.0以前:   Shark 1.1.x开始:SparkSQL(只是测试性的)  SQL 1.3.x:          SparkSQL(正式版本)+Datafram ...

随机推荐

  1. ICEM-缺口圆柱

    原视频下载地址:https://pan.baidu.com/s/1bpahxd9 密码: bpp7

  2. #C++初学记录(typedef和define)

    typedef的用法 typedef关键字可以用于给数据类型定义一个别名,即可以给long long 定义成ll,也可以给结构体定义,当你定义了一个结构体时,每次创建一个结构体都要使用struct+结 ...

  3. NCCloud

    NC后台核心服务开发模式不变 NC原Client代码废弃 增加NC Web端Java代码 增加前端JS代码 原有的NC公共能力基本保留:模板.精度.自定义项等

  4. 安装mininet 一直显示 ‘Cloning into openflow'

    问题描述. 安装mininet卡在了下载openflow. git clone --branch 2.2.2 git@github.com:mininet/mininet.git ,然后输入命令./i ...

  5. 第07组 Alpha事后诸葛亮

    1.请在博客开头给出组长博客链接(3.1 2分) 团队:摇光 队长:杨明哲 组长博客:这里 2.参考邹欣老师的问题模板进行总结思考(3.2 27分) 设想和目标(2分) 1.我们的软件要解决什么问题? ...

  6. win10回收站右键有2个“CCleaner”怎么删除

      win10回收站右键有2个"CCleaner"怎么删除? win10系统安装最新的CCleaner后遇到了这样子的问题:右击回收站有两个关于CCleaner的乱码,卸载CCle ...

  7. MYSQL事务的开启与提交

    MYSQL 事务处理主要有两种方法: 1.用 BEGIN, ROLLBACK, COMMIT来实现 BEGIN 开始一个事务 ROLLBACK 事务回滚 COMMIT 事务确认 2.直接用 SET 来 ...

  8. F12修改html进行本地js操作页面元素

    F12修改html进行本地js操作页面元素

  9. bat脚本的基本命令语法

    bat脚本的基本命令语法 一.批处理的常见命令 1.REM 和 ::     2.ECHO 和 @     3.PAUSE     4.ERRORLEVEL     5.TITLE     6.COL ...

  10. _stdcallthunk

    // c:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.14.26428\atlmfc\in ...