如果觉得内容不错,别忘记一键三连哦!!!
如果觉得内容不错,别忘记一键三连哦!!!
如果觉得内容不错,别忘记一键三连哦!!!
Getting Started
Starting Point: SparkSession
Spark中所有功能的入口点都是SparkSession类。要创建基本的SparkSession,只需使用SparkSession.builder()
import org.apache.spark.sql.SparkSession
val spark: SparkSession = SparkSession
.builder()
.appName("Spark SQL basic example")
.config("spark.some.config.option", "some-value")
.getOrCreate()
在Spark存储库中的“ examples / src / main / scala / org / apache / spark / examples / sql / SparkSQLExample.scala”中找到完整的示例代码。
Spark 2.0中的SparkSession为Hive功能提供了内置支持,包括使用HiveQL编写查询的能力,对Hive UDF的访问以及从Hive表读取数据的能力。 要使用这些功能,您不需要现有的Hive设置。
Creating DataFrames
使用SparkSession,应用程序可以从现有的RDD、Hive表或Spark数据源创建DataFrame 。以基于JSON文件内容创建一个DataFrame 为例
val df: DataFrame = spark.read.json("examples/src/main/resources/people.json")
// Displays the content of the DataFrame to stdout
df.show()
// +----+-------+
// | age| name|
// +----+-------+
// |null|Michael|
// | 30| Andy|
// | 19| Justin|
// +----+-------+
在Spark存储库中的“ examples / src / main / scala / org / apache / spark / examples / sql / SparkSQLExample.scala”中找到完整的示例代码。
Untyped Dataset Operations (aka DataFrame Operations
DataFrames为Scala、Java、Python和R中的结构化数据操作提供了一种特定于领域的语言。
如上所述,在Spark 2.0中,DataFrame只是Scala和Java API中的Dataset of Rows。 与强类型的Scala / Java数据集附带的“类型转换”相反,这些操作也称为“非类型转换”。
这里我们包括一些使用Datasets进行结构化数据处理的基本例子
// This import is needed to use the $-notation
import spark.implicits._
// Print the schema in a tree format
df.printSchema()
// root
// |-- age: long (nullable = true)
// |-- name: string (nullable = true)
// Select only the "name" column
df.select("name").show()
// +-------+
// | name|
// +-------+
// |Michael|
// | Andy|
// | Justin|
// +-------+
// Select everybody, but increment the age by 1
df.select($"name", $"age" + 1).show()
// +-------+---------+
// | name|(age + 1)|
// +-------+---------+
// |Michael| null|
// | Andy| 31|
// | Justin| 20|
// +-------+---------+
// Select people older than 21
df.filter($"age" > 21).show()
// +---+----+
// |age|name|
// +---+----+
// | 30|Andy|
// +---+----+
// Count people by age
df.groupBy("age").count().show()
// +----+-----+
// | age|count|
// +----+-----+
// | 19| 1|
// |null| 1|
// | 30| 1|
// +----+-----+
在Spark存储库中的“ examples / src / main / scala / org / apache / spark / examples / sql / SparkSQLExample.scala”中找到完整的示例代码。
有关可以对dataSet执行的操作类型的完整列表,请参阅API文档。
除了简单的列引用和表达式之外,dataSet还有一个丰富的函数库,包括字符串操作、数据运算、常见的数学运算等等。完整的列表可以在DataFrame函数参考中找到。
Running SQL Queries Programmatically(以编程方式运行SQL查询)
SparkSession上的sql函数允许应用程序以编程方式运行sql查询,并将结果作为DataFrame返回。
// Register the DataFrame as a SQL temporary view
df.createOrReplaceTempView("people")
val sqlDF = spark.sql("SELECT * FROM people")
sqlDF.show()
// +----+-------+
// | age| name|
// +----+-------+
// |null|Michael|
// | 30| Andy|
// | 19| Justin|
// +----+-------+
Global Temporary View(全局临时视图)
Spark SQL中的临时视图是会话范围的,如果创建临时视图的会话终止,临时视图就会消失。如果您希望拥有一个在所有会话之间共享的临时视图,并在Spark应用程序终止之前保持活动状态,那么可以创建一个全局临时视图。全局临时视图绑定到一个系统保留的数据库global_temp,我们必须使用限定名来引用它,例如SELECT * FROM global_temp.view1.
// Register the DataFrame as a global temporary view
df.createGlobalTempView("people")
// Global temporary view is tied to a system preserved database `global_temp`
spark.sql("SELECT * FROM global_temp.people").show()
// +----+-------+
// | age| name|
// +----+-------+
// |null|Michael|
// | 30| Andy|
// | 19| Justin|
// +----+-------+
// Global temporary view is cross-session
spark.newSession().sql("SELECT * FROM global_temp.people").show()
// +----+-------+
// | age| name|
// +----+-------+
// |null|Michael|
// | 30| Andy|
// | 19| Justin|
// +----+-------+
Creating Datasets
Datasets 与RDD相似,但是,它们不使用Java serialization or Kryo,而是使用专用的Encoder对对象进行序列化以进行网络处理或传输。 虽然编码器和标准序列化都负责将对象转换为字节,但是编码器是动态生成的代码,并使用一种格式,该格式允许Spark执行许多操作,例如过滤,排序和哈希处理,而无需将字节反序列化回对象。
case class Person(name: String, age: Long)
// Encoders are created for case classes
val caseClassDS: Dataset[Person] = Seq(Person("Andy", 32)).toDS()
caseClassDS.show()
// +----+---+
// |name|age|
// +----+---+
// |Andy| 32|
// +----+---+
// Encoders for most common types are automatically provided by importing spark.implicits._
val primitiveDS: Dataset[Int] = Seq(1, 2, 3).toDS()
primitiveDS.map(_ + 1).collect() // Returns: Array(2, 3, 4)
// DataFrames can be converted to a Dataset by providing a class. Mapping will be done by name
val path = "examples/src/main/resources/people.json"
val peopleDS = spark.read.json(path).as[Person]
peopleDS.show()
// +----+-------+
// | age| name|
// +----+-------+
// |null|Michael|
// | 30| Andy|
// | 19| Justin|
// +----+-------+
Interoperating with RDDs
Spark SQL支持两种不同的方法来将现有的RDDs转换为Datasets。第一种方法使用反射来推断包含特定类型对象的RDD的schema 。这种基于反射的方法可以使代码更简洁,并且在编写Spark应用程序时,如果您已经了解了schema ,这种方法就可以很好地工作。
创建Datasets 的第二种方法是通过一个编程接口,该接口允许您构造一个schema ,然后将其应用到现有的RDD。虽然此方法更详细,但它允许在运行时才知道列及其类型时构造Datasets 。
Inferring the Schema Using Reflection
Spark SQL的Scala接口支持自动将包含case类的RDD转换为DataFrame。case类定义表的schema 。使用反射读取case类的参数名称,并成为列的名称。Case类也可以是嵌套的,或者包含复杂类型,比如Seqs or Arrays。这个RDD可以隐式转换为一个DataFrame ,然后注册为一个表。表可以在后续的SQL语句中使用。
// For implicit conversions from RDDs to DataFrames
import spark.implicits._
// Create an RDD of Person objects from a text file, convert it to a Dataframe
val peopleDF:DataFrame = spark.sparkContext :SparkComtext
.textFile("examples/src/main/resources/people.txt") :RDD[String]
.map(_.split(",")) :RDD[Array[String]]
.map(attributes => Person(attributes(0), attributes(1).trim.toInt)) :RDD[Person]
.toDF() :DataFrame
// Register the DataFrame as a temporary view
peopleDF.createOrReplaceTempView("people")
// SQL statements can be run by using the sql methods provided by Spark
val teenagersDF = spark.sql("SELECT name, age FROM people WHERE age BETWEEN 13 AND 19")
// The columns of a row in the result can be accessed by field index
teenagersDF.map(teenager => "Name: " + teenager(0)).show()
// +------------+
// | value|
// +------------+
// |Name: Justin|
// +------------+
// or by field name
teenagersDF.map(teenager => "Name: " + teenager.getAs[String]("name")).show()
// +------------+
// | value|
// +------------+
// |Name: Justin|
// +------------+
// No pre-defined encoders for Dataset[Map[K,V]], define explicitly
implicit val mapEncoder = org.apache.spark.sql.Encoders.kryo[Map[String, Any]]
// Primitive types and case classes can be also defined as
// implicit val stringIntMapEncoder: Encoder[Map[String, Any]] = ExpressionEncoder()
// row.getValuesMap[T] retrieves multiple columns at once into a Map[String, T]
teenagersDF.map(teenager => teenager.getValuesMap[Any](List("name", "age"))).collect()
// Array(Map("name" -> "Justin", "age" -> 19))
Programmatically Specifying the Schema
当不能提前定义case类时(例如,记录的结构以字符串编码,或者文本数据集将被解析,字段将针对不同的用户进行不同的投影),可以通过三个步骤以编程方式创建数据框架。
1、从原始RDD创建一个Row组成的RDD
2、创建与步骤1中创建的RDD中的行结构相匹配的StructType表示的schema 。
3、通过SparkSession提供的createDataFrame方法将schema 应用到Row的RDD。
import org.apache.spark.sql.Row
import org.apache.spark.sql.types._
// Create an RDD
val peopleRDD: RDD[String] = spark.sparkContext.textFile("examples/src/main/resources/people.txt")
// The schema is encoded in a string
val schemaString: String = "name age"
// Generate the schema based on the string of schema
val fields: Array[StructField] = schemaString.split(" ")
.map(fieldName => StructField(fieldName, StringType, nullable = true))
val schema: StructType = StructType(fields)
// Convert records of the RDD (people) to Rows
val rowRDD: RDD[Row] = peopleRDD
.map(_.split(","))
.map(attributes => Row(attributes(0), attributes(1).trim))
// Apply the schema to the RDD
val peopleDF: DataFrame = spark.createDataFrame(rowRDD, schema)
// Creates a temporary view using the DataFrame
peopleDF.createOrReplaceTempView("people")
// SQL can be run over a temporary view created using DataFrames
val results: DataFrame = spark.sql("SELECT name FROM people")
// The results of SQL queries are DataFrames and support all the normal RDD operations
// The columns of a row in the result can be accessed by field index or by field name
results.map(attributes => "Name: " + attributes(0)).show()
// +-------------+
// | value|
// +-------------+
// |Name: Michael|
// | Name: Andy|
// | Name: Justin|
// +-------------+
Scalar Functions
scala函数是每行返回一个值的函数,而聚合函数则返回一组行的值。Spark SQL支持各种内置标量函数。它还支持用户定义的scala函数。
Aggregate Functions
聚合函数是在一组行上返回单个值的函数。内置的聚合函数提供了常见的聚合,如count()、countDistinct()、avg()、max()、min()等。用户不受预定义聚合函数的限制,可以创建自己的聚合函数。有关用户自定义聚合函数的详细信息,请参阅用户自定义聚合函数的文档。