Spark SQL
Spark SQL 分为三类:
- SQL
- DataFrame (参考pandas,但略有不同)
- Datasets (由于python是动态的,所以不支持python)
初始环境:
import findspark
findspark.init()
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName('myspark').getOrCreate() # 初始化session
# spark.sparkContext.parallelize([1,2,3,4]).collect() # 里面包含之前说过的sparkContext
...
中间这部分留给下面写
...
spark.stop() # 关闭 session
从json导入为df:
df = spark.read.json("file:///home/lin/data/user.json",multiLine=True)
打印DF字段信息:
df.printSchema()
root
|-- age: long (nullable = true)
|-- gender: string (nullable = true)
|-- name: string (nullable = true)
CRUD
增
from pyspark.sql import functions as f
# schema就相当于 pandas 指定的 columns, 双层序列, [2x4] 的样本
df1 = spark.createDataFrame([[1,2,3,4],[5,6,7,8]],schema=['1_c','2_c', '3_c', '4_c'])
+-----+-----+-----+-----+
|1\_col|2\_col|3\_col|4\_col|
+-----+-----+-----+-----+
| 1| 2| 3| 4|
| 5| 6| 7| 8|
+-----+-----+-----+-----+
# lit可以在指定空列的时候,指定 null值, 或者 int型(里面有很多类型,可以发现)
# df2 = df1.withColumn('null_col', f.lit(None)).withColumn('digit_col', f.lit(2))
df2 = df1.withColumn('5_col', df1['4_col']+1) # 在原来列字段基础上。
df2.show()
删
df2 = df1.drop('age') # 删除 age列
df2 = df1.dropna() # 删除空行
df2 = df1.drop_duplicates() # 删除重复-行
改
和"增",差不多,只不过字段,指定为原有字段字符串即可。
查
下面的讲的(投影、过滤、排序、分组),几乎都是查。
投影
投影所有:
df.show(n=20) # 默认就是 n=20 只返回 前20条记录
+---+------+--------+
|age|gender| name|
+---+------+--------+
| 18| man|zhangsan|
+---+------+--------+
选中某列投影:
df.select('name','age').show() # 若直接写 '*', 和直接 df.show()是一个效果
+--------+---+
| name|age|
+--------+---+
|zhangsan| 18|
+--------+---+
或者用另两种方式投影(投影过程可计算):
df.select(df['name'],df['age']+20).show() # 同上,这是另一种写法,注意一下列名
df.select(df.name, df.age+20).show() # 同上,这是另二种写法,注意一下列名
+--------+----------+
| name|(age + 20)|
+--------+----------+
|zhangsan| 38|
+--------+----------+
取出前N条DF,并转化为 [ {} , {} ] 格式
df_user.take(1) # [Row(age=18, name='张三')]
df_user.head(1) # [Row(age=18, name='张三')]
df_user.first() # Row(age=18, name='张三') # 注意,无列表
排序
df_user.head(1)
df_user.sort(df_user.name.desc()).show()
# 另外说明一点, df的每个熟悉都有,一些操作符函数, desc()就是一种操作符函数
过滤:
df.filter( df['age'] > 15).show()
+---+------+----+
|age|gender|name|
+---+------+----+
+---+------+----+
分组:
df.groupBy('name').count().show()
+--------+-----+
| name|count|
+--------+-----+
|zhangsan| 1|
+--------+-----+
Join
df_user.join(df_user, on=df_user.name==df_user.name, how='inner').show()
+----+---+----+---+
|name|age|name|age|
+----+---+----+---+
|李四| 20|李四| 20 |
|张三| 18|张三| 18 |
+----+---+----+---+
# 特别提醒, 此 Join, 只要都进来是 DF格式的任何数据库,都可 Join
# 比如: MySQL 和 Hive , Json 也可。
储存为临时视图(表), 并调用sql语句:
df.createOrReplaceTempView('user') # 创建为 user临时视图
df_sql = spark.sql('select * from user').show() # spark.sql返回的还是df, 所以要show()
+---+------+--------+
|age|gender| name|
+---+------+--------+
| 18| man|zhangsan|
+---+------+--------+
RDD 与 DF互转
RDD -> DF
### RDD -> DF 需要把RDD做成两种格式(任选其一)
### 第一种 Row 格式
from pyspark import Row
rdd_user = spark.sparkContext.parallelize( [('张三',18), ('李四',20)] )
rdd_user_row = rdd_user.map(lambda x:Row(name=x[0], age=x[1]))
print(rdd_user_row.collect()) # [Row(age=18, name='张三'), Row(age=20, name='李四')]
df_user = spark.createDataFrame(rdd_user_row)
### 第二种 [('张三', 18),('李四', 20)]
rdd_user = spark.sparkContext.parallelize( [('张三',18), ('李四',20)] )
df_user = rdd_user.toDF(['name', 'age']) # 给定列名
df_user.show()
DF -> RDD
rdd_row = df_user.rdd.map(lambda x: x.asDict()) # 或者 x.name, x.age取值
rdd_row.collect() # [{'age': 18, 'name': '张三'}, {'age': 20, 'name': '李四'}]
CSV读写
从HDFS中读取(我们先新建一个CSV并扔到HDFS中),
vi mydata.csv:
name,age
zhangsan,18
lisi, 20
hadoop fs -mkdir /data # 在HDFS中新建一个目录 /data
hadoop fs -put mydata.csv /data # 并把本地 mydata.csv扔进去 (-get可拿出来)
在代码中读取 HDFS数据:
df = spark.read.csv("hdfs:///data/mydata.csv", header=True)
df.show()
# header=True 代表, csv文件的第一行作为csv的抬头(列名)
# df.write.csv("hdfs:///data/mydata.csv", header=True) # read改为write就变成了写
Hive读写
Hive的配置与依赖之前讲过了(最值得注意的是需要先启动一个 metadata的服务)
先验传送门:https://segmentfault.com/a/1190000020841646
import findspark
findspark.init()
from pyspark.sql import SparkSession
spark = SparkSession.builder\
.appName("Spark Hive Example")\
.master("local[*]")\
.config("hive.metastore.uris", "thrift://localhost:9083")\
.enableHiveSupport()\
.getOrCreate()
spark.sql("use mydatabase") # 执行Hive 的 SQL, 切换数据库(前提你得有)
读:
df = spark.table('person').show() # 直接对表操作 (注意,sql语句也可)
写:
df = spark.table('person')
df2 = df.withColumn('nickname', df.name) # 稍微变动一下,添一个字段
df2.write.saveAsTable("new_person") # 写入新表
MySQL读写
读:
# 注意0:有好几种方式,我只列举一个 成对的读写配置。
# 注意1: url中 "hive"是数据库名. 你也可以起为别的名
# 注意2:table的值--"TBLS", 它是 MySQL中"hive库"中的一个表。
# 注意3:特别注意! TBLS不是我们想要的表。他只是一个大表,管理了我们hive的信息
# TBLS中的 一个列属性 "TBL_NAME" 才是真正我们需要的表!!
df = spark.read.jdbc(
url='jdbc:mysql://localhost:3306/hive?user=root&password=123',
table="TBLS",
properties={"driver":"com.mysql.jdbc.Driver"},
)
df.show()
df.select("TBL_NAME").show()
写:
df.write.jdbc(
url='jdbc:mysql://localhost:3306/hive?user=root&password=123',
table="new_table",
mode='append',
properties={"driver": "com.mysql.jdbc.Driver"}
)
# 同是特别注意: 和读一样, 它写入的新表,也是一个整体的表结构。
# 此表的一个列"TBL_NAME",它才对应了我们真正要操作的表