Spark SQL部分簡單使用詳解

Spark SQL簡介

Spark SQL是Spark處理數據的一個模塊,跟基本的Spark RDD的API不同,Spark SQL中提供的接口將會提供給Spark更多關於結構化數據和計算的信息。

Spark SQL is not about SQL
Spark SQL is about more than SQL
從嚴格意義上來說sparkSQL不僅僅是SQL,更加準確的來說,他是超乎SQL的作用。
Spark SQL is Apache Spark’s module for working with structured data.
Spice SQL是Apache Spark的用於處理結構化數據的模塊

RDD、DataFrame和Dataset區別

RDD和Dataset、DataFrame的主要區別是Dataset和DataFrame帶有數據的schema。
Dataset和DataFrame的區別是:

DataFrame = Dataset[Row]

從SparkSession開始

SparkSession是Spark中所有功能的入口類。
import org.apache.spark.sql.SparkSession

val spark = SparkSession
  .builder()
  .appName("Spark SQL basic example")
  .config("spark.some.config.option", "some-value")
  .getOrCreate()

創建DataFrame

要想創建DataFrame,需要先導入隱式轉換,然後可以從現有的RDD,Hive表或Spark數據源創建DataFrame 。

現有的RDD轉換成DataFrame

用RDD轉成DataFrame,有兩種轉換方式:

使用反射推斷機制
import spark.implicits._
case class Person(name: String, age: Long)
val peopleDF = spark.sparkContext
  .textFile("examples/src/main/resources/people.txt")
  .map(_.split(","))
  .map(attributes => Person(attributes(0), attributes(1).trim.toInt))
  .toDF()
  
//查看schema
personDF.printSchema()
以編程方式指定架構
import org.apache.spark.sql.types._

val peopleRDD = spark.sparkContext.textFile("examples/src/main/resources/people.txt")
val schemaString = "name age"
val fields = schemaString.split(" ")
  .map(fieldName => StructField(fieldName, StringType, nullable = true))
val schema = StructType(fields)

val rowRDD = peopleRDD
  .map(_.split(","))
  .map(attributes => Row(attributes(0), attributes(1).trim))
  
val peopleDF = spark.createDataFrame(rowRDD, schema)

數據源方式

Spark支持多種數據源,並可以從json或parquet等可以推斷出schema的文件中直接得到schema。

val df = spark.read.format("json").load("file://./examples/src/main/resources/people.json")

df.show()

在代碼中使用SQL

只需要把DataFrame創建一個別名:

df.createOrReplaceTempView("people")
spark.sql("SELECT * FROM global_temp.people").show()

就可以使用SQL了。

在代碼中使用算子

df.printSchema()

df.select("name").show()
//還可以使用帶$的方式,這種方式可以對結果進行修改
df.select($"name", $"age" + 1).show()

df.filter($"age" > 21).show()
df.groupBy("age").count().show()

//想在結果中加東西還可以這樣(按字段名)
df.map(x => "name" + x.getAs[String]("name")).show()
//按下標取單個值(對於單列來說)
df.map(x => "name" + x(0)).show()

//把DataFrame寫入本地
df.write.format("json").save("file://./examples/src/main/resources/writePeople.json")

//對於已經處在的路徑,save需要設定模式(默認ErrorIfExists),使用時是SaveMode.Append或者"append"(加一個新文件)
//另外還有overwrite(覆蓋(所有))和ignore(存在就跳過)
df.write.format("json").mode("ignore").save("file://./examples/src/main/resources/writePeople.json")

df.show(false)//不會把長的數據變成帶...的隱藏顯示
df.head(10).foreach(println)
df.filter("age > 10")
df.filter("name = '' or name = 'null'")
df.filter("substring(name, 0, 1) = 'M'")
    
//DataFrame裏面orderBy和sort是同一個,orderBy是sort的別名
df.sort("age").show()//得到的是字典順序(如:1,10,11,2,20,21...)
df.sort(students.col("age").desc).show()
df.sort(students.col("name"), students.col("age")).show()
df.sort(students.col("name"), students.col("age") as "my_age").show()

使用Dataset

case class Sales(transactionId:String)
//讀取csv格式的文件
val df = spark.read.format("csv").option("header", "true").option("inferSchema", "true").load("")
val ds = spark.read.format("csv").option("header", "true").option("inferSchema", "true").load("").as[Sales]

//其中,前面一部分是定義DataFrame的語句所以可以直接是
val ds = df.as[Sales]
//也可以這樣:val ds = spark.sparkContext.parallelize(List("1","2")).toDS()

//取值方式
df.select("transactionId").show
//Dataset可以使用DataFrame的方式,也可以這樣
ds.map(_.transactionId).show

使用catalog查看數據庫

val catalog = spark.catalog

catalog.listDatabases().show(false)//顯示數據庫信息
catalog.listDatabases().map(x=>x.locationUri).show(false)//取某列字段(這裏的是locationUri)
catalog.listTables().show(false)
//取出表明帶有page_views的表
catalog.listTables().filter(x=>x.name.contains("page_views")).show(false)

catalog.listColumns("page_views").show(false)
catalog.listFunctions().show(false)

catalog.isCached("page_views")
catalog.cacheTable("page_views")
catalog.uncacheTable("page_views")

分區操作

自動推導分區:

比如:定義一個hdfs文件路徑:hadoop fs -mkdir -p /parquet_table/gender=male/country=US
放進去一個文件:hadoop fs -put users.parquet /parquet_table/gender=male/country=US/
然後讀取(默認是hdfs):val df = spark.read.format("parquet").load("/parquet_table")
df.prientSchema
就可以看到最後兩個是gender和country,他們是分區。
如果指定的目錄是/parquet_table/gender=male,那麼只有country是分區字段

自定義UDF函數

val people = Array("zhangsan", "lisi", "wangwu")

val rdd = spark.sparkContext.parallelize(people)
//rdd.collect().foreach(println)
import spark.implicits._
rdd.toDF("name").createOrReplaceTempView("test")

//註冊udf函數(可以是匿名函數)
spark.udf.register("strLen", (str:String) => str.length)

spark.sql("select name, strLen(name) from test").show

注意

spark.stop()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章