Working with Spark SQL DataFrames and Datasets

Working with Spark SQL DataFrames and Datasets

Table of Contents- Spark SQL - - - DataFrame - Dataset - Project Setup with IDEA - Custom Functions - User Defined Aggregate Functions (Weakly Typed) - User Defined Aggregate Functions (Strongly Typed)

DataFrame Operations

Initializing Spark Session

scala> spark
res0: org.apache.spark.sql.SparkSession = org.apache.spark.sql.SparkSession@4e8598d9


Exploring available data sources for DataFrame creation

scala> spark.read.
csv   format   jdbc   json   load   option   options   orc   parquet   schema   table   text   textFile


Reading JSON file into DataFrame

scala> val df = spark.read.json("hdfs://192.168.83.101:9000/datas/2.json")
res2: org.apache.spark.sql.DataFrame = [age: bigint, name: string]


Displaying DataFrame contents

scala> df.show()
+---+--------+
|age|    name|
+---+--------+
| 20|zhangsan|
| 20|    lisi|
| 20|  wangwu|
+---+--------+


Creating temporary view (read-only)

scala> df.createTempView("emp")


Querying the temporary view

scala> spark.sql("select * from emp").show
+---+--------+
|age|    name|
+---+--------+
| 20|zhangsan|
| 20|    lisi|
| 20|  wangwu|
+---+--------+


Temporary tables exist within the session scope; upon session termination, they disappear. For application-wide visibility, use global tables with full path access like: global_temp.people

DSL syntax usage

scala> df.printSchema
root
 |-- age: long (nullable = true)
 |-- name: string (nullable = true)


Selecting name column data

scala> df.select("name").show()
+--------+
|    name|
+--------+
|zhangsan|
|    lisi|
|  wangwu|
+--------+


Selecting name and age+1 values

scala> df.select($"name",$"age" + 1).show()
+--------+---------+
|    name|(age + 1)|
+--------+---------+
|zhangsan|       21|
|    lisi|       21|
|  wangwu|       21|
+--------+---------+


Filtering records where age is less than 21

scala> df.filter($"age" < 21).show()
+---+--------+
|age|    name|
+---+--------+
| 20|zhangsan|
| 20|    lisi|
| 20|  wangwu|
+---+--------+


Grouping by age and counting entries

scala> df.groupBy("age").count().show()
+---+-----+
|age|count|
+---+-----+
| 21|    2|
| 20|    2|
+---+-----+


Converting RDD to DataFrame

scala> val rdd = sc.makeRDD(List(1,2,3,4))
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[40] at makeRDD at <console>:24

scala> val df = rdd.toDF("id")
df: org.apache.spark.sql.DataFrame = [id: int]

scala> df.show()
+---+
| id|
+---+
|  1|
|  2|
|  3|
|  4|
+---+


Transforming a single RDD element to DataFrame

scala> val rdd = sc.makeRDD(List((1,"zhangsan",20),(2,"lisi",30),(3,"wangwu",40)))
rdd: org.apache.spark.rdd.RDD[(Int, String, Int)] = ParallelCollectionRDD[45] at makeRDD at <console>:24

scala> val df = rdd.toDF("id","name","age")
df: org.apache.spark.sql.DataFrame = [id: int, name: string ... 1 more field]

scala> df.show()
+---+--------+---+
| id|    name|age|
+---+--------+---+
|  1|zhangsan| 20|
|  2|    lisi| 30|
|  3|  wangwu| 40|
+---+--------+---+


Creating DataFrame via case class

scala> case class People(name:String,age:Int)
defined class People

scala> val rdd = sc.makeRDD(List(("zhangsan",20),("lisi",30),("wangwu",40)))
rdd: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[55] at makeRDD at <console>:24

scala> val peopleRDD = rdd.map(t=>{People(t._1,t._2)})
peopleRDD: org.apache.spark.rdd.RDD[People] = MapPartitionsRDD[56] at map at <console>:28

scala> peopleRDD.toDF
res25: org.apache.spark.sql.DataFrame = [name: string, age: int]


Converting DataFrame back to RDD

Direct method call suffices

scala> df.rdd
res27: org.apache.spark.rdd.RDD[org.apache.spark.sql.Row] = MapPartitionsRDD[64] at rdd at <console>:29


Dataset Operations

Defining a case class

scala> case class Person(name:String,age:Long)
defined class Person


Creating Dataset

scala> val caseClassDS = Seq(Person("Andy",32)).toDS()
caseClassDS: org.apache.spark.sql.Dataset[Person] = [name: string, age: bigint]


Accessing Dataset (similar to DataFrame)

scala> caseClassDS.show()
+----+---+
|name|age|
+----+---+
|Andy| 32|
+----+---+


Converting RDD to Dataset

scala> case class Person(name:String,age:Long)
defined class Person

scala> val mapRDD = rdd.map(t=>{Person(t._1,t._2)})
mapRDD: org.apache.spark.rdd.RDD[Person] = MapPartitionsRDD[65] at map at <console>:28

scala> mapRDD.toDS
res30: org.apache.spark.sql.Dataset[Person] = [name: string, age: bigint]

scala> mapRDD.toDS.show()
+--------+---+
|    name|age|
+--------+---+
|zhangsan| 20|
|    lisi| 30|
|  wangwu| 40|
+--------+---+


Dataset to RDD conversion

scala> val ds = mapRDD.toDS
ds: org.apache.spark.sql.Dataset[Person] = [name: string, age: bigint]

scala> ds.rdd
res32: org.apache.spark.rdd.RDD[Person] = MapPartitionsRDD[73] at rdd at <console>:33


DataFrame to Dataset conversion

scala> rdd.collect
res33: Array[(String, Int)] = Array((zhangsan,20), (lisi,30), (wangwu,40))

scala> val df = rdd.toDF("name","age")
df: org.apache.spark.sql.DataFrame = [name: string, age: int]

scala> df.as[Person]
res34: org.apache.spark.sql.Dataset[Person] = [name: string, age: int]


Dataset to DataFrame conversion

scala> val ds = df.as[Person]
ds: org.apache.spark.sql.Dataset[Person] = [name: string, age: int]

scala> ds.toDF
res35: org.apache.spark.sql.DataFrame = [name: string, age: int]


Project Setup Using IDEA

package com.bigdata.sparksql

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.SparkSession

object SparkSQL01_Demo {
  def main(args: Array[String]): Unit = {
    //TODO Create configuration object
    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Demo")
    val sc = new SparkContext(sparkConf)
    //TODO Initialize SparkSQL environment
    val spark = SparkSession.builder().config(sparkConf).config("spark.sql.warehouse.dir", "C:/Users/RenZetong/Desktop/桌面备份/SparkSQLDev/spark-warehouse").getOrCreate()
    //TODO Construct DataFrame
    val frame = spark.read.json("input/2.json")
    frame.show()
    spark.stop()
  }
}


Building DataFrame with SQL Queries

package com.bigdata.sparksql

import org.apache.spark.sql.SparkSession
import org.apache.spark.{SparkConf, SparkContext}

object SparkSQL02_SQL {
  def main(args: Array[String]): Unit = {
    //TODO Create configuration object
    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Demo")
    val sc = new SparkContext(sparkConf)
    //TODO Initialize SparkSQL environment
    val spark = SparkSession.builder().config(sparkConf).config("spark.sql.warehouse.dir", "C:/Users/RenZetong/Desktop/桌面备份/SparkSQLDev/spark-warehouse").getOrCreate()
    //TODO Construct DataFrame
    val frame = spark.read.json("input/2.json")
    //Convert frame to table
    frame.createTempView("user")
    //Execute SQL query
    spark.sql("select * from user").show()
    spark.stop()
  }
}


Type Conversiosn

package com.bigdata.sparksql

import org.apache.spark.sql.SparkSession
import org.apache.spark.{SparkConf, SparkContext}

object SparkSQL03_Transform {
  def main(args: Array[String]): Unit = {
    //TODO Create configuration object
    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Demo")
    val sc = new SparkContext(sparkConf)
    //TODO Initialize SparkSQL environment
    val spark = SparkSession.builder().config(sparkConf).config("spark.sql.warehouse.dir", "C:/Users/RenZetong/Desktop/桌面备份/SparkSQLDev/spark-warehouse").getOrCreate()

    //Import implicit conversions
    import spark.implicits._

    //Create RDD
    val rdd = spark.sparkContext.makeRDD(List((1,"张三",22),(2,"李四",20),(3,"王五",30)))
    //Convert to DataFrame
    val df = rdd.toDF("id","name","age")
    //Convert to Dataset
    val ds = df.as[User]
    //Convert back to DataFrame
    val df1 = ds.toDF()
    //Convert to RDD
    val rdd1 = df1.rdd

    rdd1.foreach(
      row=>{
        println(row.getInt(0) + "," + row.getString(1) + "," + row.getInt(2))
      }
    )
  }
}
//TODO Define case class for table structure
case class User(id:Int,name:String,age:Int)


Custom Functions

In Shell, custom functions can be registered through spark.udf

scala> var df = spark.read.json("hdfs://192.168.83.101:9000/datas/2.json")
df: org.apache.spark.sql.DataFrame = [age: bigint, name: string]

scala> spark.udf.register("addName",(x:String)=>"Name:" + x)
res1: org.apache.spark.sql.expressions.UserDefinedFunction = UserDefinedFunction(<function1>,StringType,Some(List(StringType)))

scala> df.createTempView
   def createTempView(viewName: String): Unit

scala> df.createTempView("user")
+---+--------+
|age|    name|
+---+--------+
| 20|zhangsan|
| 20|    lisi|
| 21|  wangwu|
| 21|   liliu|
| 18|   liyue|
+---+--------+

scala> spark.sql("select * from user").show()

scala> spark.sql("select addName(name)  from user").show()
+-------------+
|    UDF(name)|
+-------------+
|Name:zhangsan|
|    Name:lisi|
|  Name:wangwu|
|   Name:liliu|
|   Name:liyue|
+-------------+


User Defined Aggregate Functions (Weakly Typed)

Spark provides built-in aggregate functions like count(), countDistinct(), avg(), max(), min(). Users may also define their own.

Weakly typed UDAF implementation involves extending UserDefinedAggregateFunction:

package com.bigdata.sparksql

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.{Row, SparkSession}
import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, DoubleType, LongType, StructType}

object SparkSQL01_Demo {
  def main(args: Array[String]): Unit = {
    //TODO Create configuration object
    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Demo")
    val sc = new SparkContext(sparkConf)
    //TODO Initialize SparkSQL environment
    val spark = SparkSession.builder().config(sparkConf).config("spark.sql.warehouse.dir", "C:/Users/RenZetong/Desktop/桌面备份/SparkSQLDev/spark-warehouse").getOrCreate()
    //TODO Register custom aggregate function
    val udaf = new MyAgeAvgFunction
    spark.udf.register("avgAge", udaf)

    //Use aggregate function
    val frame = spark.read.json("input/2.json")

    frame.createTempView("user")
    spark.sql("select avgAge(age) from user").show()

    spark.stop()
  }
}

//TODO Declare weakly typed UDAF
//1. Extend UserDefinedAggregateFunction
//2. Implement methods
class MyAgeAvgFunction extends UserDefinedAggregateFunction {

  //TODO Input schema definition
  override def inputSchema: StructType = {
    //Input has one field 'age' of type Long
    new StructType().add("age", LongType)
  }

  //TODO Buffer schema definition
  override def bufferSchema: StructType = {
    new StructType().add("sum", LongType).add("count", LongType)
  }

  //TODO Output data type
  override def dataType: DataType = DoubleType

  //TODO Deterministic property
  override def deterministic: Boolean = true

  //TODO Initialize buffer
  override def initialize(buffer: MutableAggregationBuffer): Unit = {
    buffer(0) = 0L // Represents sum
    buffer(1) = 0L // Represents count
  }

  //TODO Update buffer based on input
  override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
    buffer(0) = buffer.getLong(0) + input.getLong(0)
    buffer(1) = buffer.getLong(1) + 1
  }

  //TODO Merge buffers from different nodes
  override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
    buffer1(0) = buffer1.getLong(0) + buffer2.getLong(0)
    buffer1(1) = buffer1.getLong(1) + buffer2.getLong(1)
  }

  //TODO Final computation result
  override def evaluate(buffer: Row): Any = {
    buffer.getLong(0).toDouble / buffer.getLong(1)
  }
}



User Defined Aggregate Functions (Strongly Typed)

package com.bigdata.sparksql

import org.apache.spark.sql.expressions.Aggregator
import org.apache.spark.sql.{Dataset, Encoder, Encoders, Row, SparkSession}
import org.apache.spark.{SparkConf, SparkContext}

object SparkSQL04_UDAF_Class {
  def main(args: Array[String]): Unit = {
    //TODO Create configuration object
    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Demo")
    val sc = new SparkContext(sparkConf)
    //TODO Initialize SparkSQL environment
    val spark = SparkSession.builder().config(sparkConf).config("spark.sql.warehouse.dir", "C:/Users/RenZetong/Desktop/桌面备份/SparkSQLDev/spark-warehouse").getOrCreate()
    //Import implicit conversions
    import spark.implicits._

    //Create UDAF instance
    val udaf = new MyAgeAvgClassFunction
    //Convert UDAF to column expression
    val vagCol = udaf.toColumn.name("avgAge")

    val frame = spark.read.json("input/2.json")

    val userDS = frame.as[UserBean]

    //Apply function
    userDS.select(vagCol).show()

    spark.stop()
  }
}

case class UserBean(name: String, age: BigInt)

case class AvgBuffer(var sum: BigInt, var count: Int)

//TODO Declare strongly typed UDAF
//1. Extend Aggregator
//2. Implement methods

class MyAgeAvgClassFunction extends Aggregator[UserBean, AvgBuffer, Double] {
  //TODO Initialization
  override def zero: AvgBuffer = {
    AvgBuffer(0, 0)
  }

  /**
   * Aggregate data
   */
  override def reduce(b: AvgBuffer, a: UserBean): AvgBuffer = {
    b.sum = b.sum + a.age
    b.count = b.count + 1
    b
  }

  //TODO Buffer merging
  override def merge(b1: AvgBuffer, b2: AvgBuffer): AvgBuffer = {
    b1.sum = b1.sum + b2.sum
    b1.count = b1.count + b2.count
    b1
  }

  //TODO Final calculation
  override def finish(reduction: AvgBuffer): Double = {
    reduction.sum.toDouble / reduction.count
  }

  //TODO Buffer encoder
  override def bufferEncoder: Encoder[AvgBuffer] = Encoders.product

  //TODO Output encoder
  override def outputEncoder: Encoder[Double] = Encoders.scalaDouble
}



Tags: Spark SQL DataFrame Dataset Scala Spark

Posted on Wed, 21 Oct 2026 16:48:31 +0000 by FrOzeN