Spark SQL是Apache Spark用于处理结构化数据的模块。Spark SQL允许使用SQL或熟悉的DataFrame API查询Spark程序内的结构化数据。Spark SQL支持多语言编程包括Java、Scala、Python和R,可以根据自身喜好进行选择。本文中所涉及的Spark SQL代码示例均使用python语言。
DataFrame是一个以命名列(类似于关系表中的字段)组织的分布式数据集。可以把它看成关系数据库中的一张表或R/python中的DataFrame数据结构(在spark 1.3以前,称为schema-RDD,后改为DataFrame)。
DataFrame可以通过多种来源创建:结构化数据文件,hive的表,外部数据库或者RDDs
Spark SQL所有的功能入口都是SQLContext 类,及其子类。不过要创建一个SQLContext对象,首先需要有一个SparkContext对象,然后再创建SQLContext
from pyspark import SparkContext
from pyspark import SQLContext
sc = SparkContext("local","my_pyspark")
spark = SQLContext(sc)
除了SQLContext之外,你也可以创建HiveContext,HiveContext是SQLContext 的超集。
数据来源可以是已有的RDD、或Hive表、或其他数据源
以下是一个从JSON文件创建DataFrame的小例子:
df = spark.read.json("/usr/local/spark/examples/src/main/resources/people.json") #自带的示例文件,可在安装路径下查找
df.show() #展示DataFrame的内容
以下是一个从RDD创建DataFrame的小例子:
from pyspark import Row
rdd = sc.parallelize([
Row(name='Michael',age=29),
Row(name='Andy', age=30),
Row(name='Justin', age=19)
])
df1 = spark.createDataFrame(rdd)
df1.show()
从文本文件创建DataFrame的小例子:
rdd2 = sc.textFile("/usr/local/spark/examples/src/main/resources/people.txt")
df2 = spark.createDataFrame(rdd)
df2.show()
df.printSchema() #打印数据的树形结构
df.select("name").show()#查询name字段
df.select(df['name'], df['age'] + 1).show()#查询每个人的name和age字段,但age加1
df.filter(df['age'] > 21).show()#筛选年龄大于21的
df.groupBy("age").count().show()#按年龄统计人数
SQLContext.sql可以执行SQL查询,并返回DataFrame结果。
df.createOrReplaceTempView("people")#将DataFrame注册为SQL临时视图
sqlDF = spark.sql("SELECT * FROM people")
sqlDF.show()
Spark SQL中的临时视图是会话范围的,如果创建它的会话终止,将会消失。 如果您希望在所有会话之间共享一个临时视图并保持活动状态,直到Spark应用程序终止,则可以创建一个全局临时视图。 全局临时视图与系统保存的数据库global_temp绑定,我们必须使用限定名称来引用它,例如, SELECT * FROM global_temp.view1。
df.createGlobalTempView("people1")
spark.sql("SELECT * FROM global_temp.people1").show()
spark.newSession().sql("SELECT * FROM global_temp.people1").show()
Spark SQL将现有RDD转换为Datasets的方法有两种。
1. 使用反射机制,推导包含指定类型对象RDD的schema。这种基于反射机制的方法使代码更简洁,而且如果你事先知道数据schema,推荐使用这种方式;
2. 编程方式构建一个schema,然后应用到指定RDD上。这种方式更啰嗦,但如果你事先不知道数据有哪些字段,或者数据schema是运行时读取进来的,那么你很可能需要用这种方式。
第一种方法使用反射来推断包含特定类型对象的RDD的模式。这种基于反射的方法导致更简洁的代码,并且在编写Spark应用程序时已经知道模式的情况下运行良好。
创建数据集的第二种方法是通过编程接口,允许您构建模式,然后将其应用于现有的RDD。虽然这个方法比较冗长,但是它允许你在构造数据集的时候直到运行时才知道列和它们的类型
Spark SQL可以将Row对象的RDD转换为DataFrame,从而推断出数据类型。 行是通过将一个键/值对列表作为kwargs传递给Row类来构造的。 此列表的键定义了表的列名,类型是通过对整个数据集进行采样来推断的,类似于在JSON文件上执行的推断。
from pyspark import SparkContext
from pyspark.sql import Row
from pyspark import SQLContext
sc = SparkContext()
spark = SQLContext(sc)
#加载一个文本文件,并将每行转换为一个Row对象。
lines = sc.textFile("/usr/local/spark/examples/src/main/resources/people.txt")#换成自己的地址
parts = lines.map(lambda l: l.split(","))
people = parts.map(lambda p: Row(name=p[0], age=int(p[1])))
#
schemaPeople = spark.createDataFrame(people)
schemaPeople.createOrReplaceTempView("people")
teenagers = spark.sql("SELECT name FROM people WHERE age >= 13 AND age <= 19")
teenNames = teenagers.rdd.map(lambda p: "Name: " + p.name).collect()
for name in teenNames:
print(name)
from pyspark import SparkContext
from pyspark.sql.types import *
from pyspark import SQLContext
sc = SparkContext()
spark = SQLContext(sc)
# 加载文本文件
lines = sc.textFile("/usr/local/spark/examples/src/main/resources/people.txt")
#分割每一行
parts = lines.map(lambda l: l.split(","))
# 将每一行都转换为一个元组
people = parts.map(lambda p: (p[0], p[1].strip()))
# 可以理解为定义列表占位符
# schemaString = "name age"
fields = [StructField(field_name, StringType(), True) for field_name in schemaString.split()]
schema = StructType(fields)
# 将schema应用到指定RDD上
schemaPeople = spark.createDataFrame(people, schema)
# 使用DataFrame创建一个临时视图
schemaPeople.createOrReplaceTempView("people")
# SQL可以通过已注册为表的DataFrame运行。
results = spark.sql("SELECT name FROM people")
results.show()
结果:
参考文档:http://spark.apache.org/docs/2.2.1/sql-programming-guide.html