查询复杂类型的Spark SQL数据帧

查询复杂类型的Spark SQL数据帧,sql,scala,apache-spark,dataframe,apache-spark-sql,Sql,Scala,Apache Spark,Dataframe,Apache Spark Sql,如何查询具有复杂类型(如映射/数组)的RDD? 例如,当我编写此测试代码时: case class Test(name: String, map: Map[String, String]) val map = Map("hello" -> "world", "hey" -> "there") val map2 = Map("hello" -> "people", "hey" -> "you") val rdd = sc.parallelize(Array(Test("fi

如何查询具有复杂类型(如映射/数组)的RDD? 例如,当我编写此测试代码时:

case class Test(name: String, map: Map[String, String])
val map = Map("hello" -> "world", "hey" -> "there")
val map2 = Map("hello" -> "people", "hey" -> "you")
val rdd = sc.parallelize(Array(Test("first", map), Test("second", map2)))
我认为语法应该是这样的:

sqlContext.sql("SELECT * FROM rdd WHERE map.hello = world")

但我明白了

无法访问类型MapType(StringType、StringType、true)中的嵌套字段

org.apache.spark.sql.catalyst.errors.package$TreeNodeException:未解析的属性


分别。

一旦您将其转换为DF,您就可以简单地按如下方式获取数据

  val rddRow= rdd.map(kv=>{
    val k = kv._1
    val v = kv._2
    Row(k, v)
  })

val myFld1 =  StructField("name", org.apache.spark.sql.types.StringType, true)
val myFld2 =  StructField("map", org.apache.spark.sql.types.MapType(StringType, StringType), true)
val arr = Array( myFld1, myFld2)
val schema = StructType( arr )
val rowrddDF = sqc.createDataFrame(rddRow, schema)
rowrddDF.registerTempTable("rowtbl")  
val rowrddDFFinal = rowrddDF.select(rowrddDF("map.one"))
or
val rowrddDFFinal = rowrddDF.select("map.one")

一旦您将其转换为DF,您就可以简单地按如下方式获取数据

  val rddRow= rdd.map(kv=>{
    val k = kv._1
    val v = kv._2
    Row(k, v)
  })

val myFld1 =  StructField("name", org.apache.spark.sql.types.StringType, true)
val myFld2 =  StructField("map", org.apache.spark.sql.types.MapType(StringType, StringType), true)
val arr = Array( myFld1, myFld2)
val schema = StructType( arr )
val rowrddDF = sqc.createDataFrame(rddRow, schema)
rowrddDF.registerTempTable("rowtbl")  
val rowrddDFFinal = rowrddDF.select(rowrddDF("map.one"))
or
val rowrddDFFinal = rowrddDF.select("map.one")

它取决于列的类型。让我们从一些虚拟数据开始:

import org.apache.spark.sql.functions.{udf, lit}
import scala.util.Try

case class SubRecord(x: Int)
case class ArrayElement(foo: String, bar: Int, vals: Array[Double])
case class Record(
  an_array: Array[Int], a_map: Map[String, String], 
  a_struct: SubRecord, an_array_of_structs: Array[ArrayElement])


val df = sc.parallelize(Seq(
  Record(Array(1, 2, 3), Map("foo" -> "bar"), SubRecord(1),
         Array(
           ArrayElement("foo", 1, Array(1.0, 2.0, 2.0)),
           ArrayElement("bar", 2, Array(3.0, 4.0, 5.0)))),
  Record(Array(4, 5, 6), Map("foz" -> "baz"), SubRecord(2),
         Array(ArrayElement("foz", 3, Array(5.0, 6.0)), 
               ArrayElement("baz", 4, Array(7.0, 8.0))))
)).toDF
  • 数组(
    ArrayType
    )列:

    • Column.getItem
      方法

      df.select($"an_array".getItem(1)).show
      
      // +-----------+
      // |an_array[1]|
      // +-----------+
      // |          2|
      // |          5|
      // +-----------+
      
    • 配置单元括号语法:

      sqlContext.sql("SELECT an_array[1] FROM df").show
      
      // +---+
      // |_c0|
      // +---+
      // |  2|
      // |  5|
      // +---+
      
      sqlContext.sql("SELECT a_map['foz'] FROM df").show
      
      // +----+
      // | _c0|
      // +----+
      // |null|
      // | baz|
      // +----+
      
    • 自由民主党

      val get_ith = udf((xs: Seq[Int], i: Int) => Try(xs(i)).toOption)
      
      df.select(get_ith($"an_array", lit(1))).show
      
      // +---------------+
      // |UDF(an_array,1)|
      // +---------------+
      // |              2|
      // |              5|
      // +---------------+
      
    • 除了上面列出的方法之外,Spark还支持越来越多的在复杂类型上运行的内置函数。值得注意的例子包括高阶函数,如
      transform
      (SQL 2.4+、Scala 3.0+、PySpark/SparkR 3.1+):

    • 过滤器
      (SQL 2.4+、Scala 3.0+、Python/SparkR 3.1+)

    • 聚合
      (SQL 2.4+、Scala 3.0+、PySpark/SparkR 3.1+:

    • 数组处理功能(
      array.*
      )如
      array.\u distinct
      (2.4+):

    • array\u max
      array\u min
      ,2.4+):

    • 展平
      (2.4+)

    • 阵列\u-zip
      (2.4+):

    • array\u union
      (2.4+):

    • 切片
      (2.4+):

  • 映射(
    MapType
    )列

    • 使用
      Column.getField
      方法:

      df.select($"a_map".getField("foo")).show
      
      // +----------+
      // |a_map[foo]|
      // +----------+
      // |       bar|
      // |      null|
      // +----------+
      
    • 使用配置单元括号语法:

      sqlContext.sql("SELECT an_array[1] FROM df").show
      
      // +---+
      // |_c0|
      // +---+
      // |  2|
      // |  5|
      // +---+
      
      sqlContext.sql("SELECT a_map['foz'] FROM df").show
      
      // +----+
      // | _c0|
      // +----+
      // |null|
      // | baz|
      // +----+
      
    • 使用带点语法的完整路径:

      df.select($"a_map.foo").show
      
      // +----+
      // | foo|
      // +----+
      // | bar|
      // |null|
      // +----+
      
    • 使用自定义项

      val get_field = udf((kvs: Map[String, String], k: String) => kvs.get(k))
      
      df.select(get_field($"a_map", lit("foo"))).show
      
      // +--------------+
      // |UDF(a_map,foo)|
      // +--------------+
      // |           bar|
      // |          null|
      // +--------------+
      
    • 越来越多的
      map.*
      功能,如
      map.\u键
      (2.3+)

    • 映射值
      (2.3+)

    请查看详细清单

  • 使用带点语法的完整路径的struct(
    StructType
    )列:

    df.select($"a_map.foo").show
    
    // +----+
    // | foo|
    // +----+
    // | bar|
    // |null|
    // +----+
    
    • 使用DataFrameAPI

      df.select($"a_struct.x").show
      
      // +---+
      // |  x|
      // +---+
      // |  1|
      // |  2|
      // +---+
      
    • 使用原始SQL

      sqlContext.sql("SELECT a_struct.x FROM df").show
      
      // +---+
      // |  x|
      // +---+
      // |  1|
      // |  2|
      // +---+
      
  • 可以使用点语法、名称和标准
    方法访问
    结构数组中的字段:

    df.select($"an_array_of_structs.foo").show
    
    // +----------+
    // |       foo|
    // +----------+
    // |[foo, bar]|
    // |[foz, baz]|
    // +----------+
    
    sqlContext.sql("SELECT an_array_of_structs[0].foo FROM df").show
    
    // +---+
    // |_c0|
    // +---+
    // |foo|
    // |foz|
    // +---+
    
    df.select($"an_array_of_structs.vals".getItem(1).getItem(1)).show
    
    // +------------------------------+
    // |an_array_of_structs.vals[1][1]|
    // +------------------------------+
    // |                           4.0|
    // |                           8.0|
    // +------------------------------+
    
  • 可以使用UDF访问用户定义类型(UDT)字段。有关详细信息,请参阅

注释

  • 根据Spark版本的不同,这些方法中的某些方法只能在
    HiveContext
    中使用。UDF应该独立于标准
    SQLContext
    HiveContext
    的版本工作
  • 一般来说,嵌套值是第二类公民。嵌套字段上不支持所有典型操作。根据上下文的不同,可以更好地展平模式和/或分解集合

    df.select(explode($"an_array_of_structs")).show
    
    // +--------------------+
    // |                 col|
    // +--------------------+
    // |[foo,1,WrappedArr...|
    // |[bar,2,WrappedArr...|
    // |[foz,3,WrappedArr...|
    // |[baz,4,WrappedArr...|
    // +--------------------+
    
  • 点语法可以与通配符(
    *
    )结合使用,以选择(可能是多个)字段,而无需明确指定名称:

    df.select($"a_struct.*").show
    // +---+
    // |  x|
    // +---+
    // |  1|
    // |  2|
    // +---+
    
  • 可以使用
    get_JSON_object
    from_JSON
    函数查询JSON列。有关详细信息,请参阅


这取决于列的类型。让我们从一些虚拟数据开始:

import org.apache.spark.sql.functions.{udf, lit}
import scala.util.Try

case class SubRecord(x: Int)
case class ArrayElement(foo: String, bar: Int, vals: Array[Double])
case class Record(
  an_array: Array[Int], a_map: Map[String, String], 
  a_struct: SubRecord, an_array_of_structs: Array[ArrayElement])


val df = sc.parallelize(Seq(
  Record(Array(1, 2, 3), Map("foo" -> "bar"), SubRecord(1),
         Array(
           ArrayElement("foo", 1, Array(1.0, 2.0, 2.0)),
           ArrayElement("bar", 2, Array(3.0, 4.0, 5.0)))),
  Record(Array(4, 5, 6), Map("foz" -> "baz"), SubRecord(2),
         Array(ArrayElement("foz", 3, Array(5.0, 6.0)), 
               ArrayElement("baz", 4, Array(7.0, 8.0))))
)).toDF
  • 数组(
    ArrayType
    )列:

    • Column.getItem
      方法

      df.select($"an_array".getItem(1)).show
      
      // +-----------+
      // |an_array[1]|
      // +-----------+
      // |          2|
      // |          5|
      // +-----------+
      
    • 配置单元括号语法:

      sqlContext.sql("SELECT an_array[1] FROM df").show
      
      // +---+
      // |_c0|
      // +---+
      // |  2|
      // |  5|
      // +---+
      
      sqlContext.sql("SELECT a_map['foz'] FROM df").show
      
      // +----+
      // | _c0|
      // +----+
      // |null|
      // | baz|
      // +----+
      
    • 自由民主党

      val get_ith = udf((xs: Seq[Int], i: Int) => Try(xs(i)).toOption)
      
      df.select(get_ith($"an_array", lit(1))).show
      
      // +---------------+
      // |UDF(an_array,1)|
      // +---------------+
      // |              2|
      // |              5|
      // +---------------+
      
    • 除了上面列出的方法之外,Spark还支持越来越多的在复杂类型上运行的内置函数。值得注意的例子包括高阶函数,如
      transform
      (SQL 2.4+、Scala 3.0+、PySpark/SparkR 3.1+):

    • 过滤器
      (SQL 2.4+、Scala 3.0+、Python/SparkR 3.1+)

    • 聚合
      (SQL 2.4+、Scala 3.0+、PySpark/SparkR 3.1+:

    • 数组处理功能(
      array.*
      )如
      array.\u distinct
      (2.4+):

    • array\u max
      array\u min
      ,2.4+):

    • 展平
      (2.4+)

    • 阵列\u-zip
      (2.4+):

    • array\u union
      (2.4+):

    • 切片
      (2.4+):

  • 映射(
    MapType
    )列

    • 使用
      Column.getField
      方法:

      df.select($"a_map".getField("foo")).show
      
      // +----------+
      // |a_map[foo]|
      // +----------+
      // |       bar|
      // |      null|
      // +----------+
      
    • 使用配置单元括号语法:

      sqlContext.sql("SELECT an_array[1] FROM df").show
      
      // +---+
      // |_c0|
      // +---+
      // |  2|
      // |  5|
      // +---+
      
      sqlContext.sql("SELECT a_map['foz'] FROM df").show
      
      // +----+
      // | _c0|
      // +----+
      // |null|
      // | baz|
      // +----+
      
    • 使用带点语法的完整路径:

      df.select($"a_map.foo").show
      
      // +----+
      // | foo|
      // +----+
      // | bar|
      // |null|
      // +----+
      
    • 使用自定义项

      val get_field = udf((kvs: Map[String, String], k: String) => kvs.get(k))
      
      df.select(get_field($"a_map", lit("foo"))).show
      
      // +--------------+
      // |UDF(a_map,foo)|
      // +--------------+
      // |           bar|
      // |          null|
      // +--------------+
      
    • 越来越多的
      map.*
      功能,如
      map.\u键
      (2.3+)

    • 映射值
      (2.3+)

    请查看详细清单

  • 使用带点语法的完整路径的struct(
    StructType
    )列:

    df.select($"a_map.foo").show
    
    // +----+
    // | foo|
    // +----+
    // | bar|
    // |null|
    // +----+
    
    • 使用DataFrameAPI

      df.select($"a_struct.x").show
      
      // +---+
      // |  x|
      // +---+
      // |  1|
      // |  2|
      // +---+
      
    • 使用原始SQL

      sqlContext.sql("SELECT a_struct.x FROM df").show
      
      // +---+
      // |  x|
      // +---+
      // |  1|
      // |  2|
      // +---+
      
  • 可以使用点语法、名称和标准
    方法访问
    结构数组中的字段:

    df.select($"an_array_of_structs.foo").show
    
    // +----------+
    // |       foo|
    // +----------+
    // |[foo, bar]|
    // |[foz, baz]|
    // +----------+
    
    sqlContext.sql("SELECT an_array_of_structs[0].foo FROM df").show
    
    // +---+
    // |_c0|
    // +---+
    // |foo|
    // |foz|
    // +---+
    
    df.select($"an_array_of_structs.vals".getItem(1).getItem(1)).show
    
    // +------------------------------+
    // |an_array_of_structs.vals[1][1]|
    // +------------------------------+
    // |                           4.0|
    // |                           8.0|
    // +------------------------------+
    
  • 可以使用UDF访问用户定义类型(UDT)字段。有关详细信息,请参阅

注释

  • 根据Spark版本的不同,这些方法中的某些方法只能在
    HiveContext
    中使用。UDF应该独立于标准
    SQLContext
    HiveContext
    的版本工作
  • 一般来说,嵌套值是第二类公民。嵌套字段上不支持所有典型操作。根据上下文的不同,可以更好地展平模式和/或分解集合

    df.select(explode($"an_array_of_structs")).show
    
    // +--------------------+
    // |                 col|
    // +--------------------+
    // |[foo,1,WrappedArr...|
    // |[bar,2,WrappedArr...|
    // |[foz,3,WrappedArr...|
    // |[baz,4,WrappedArr...|
    // +--------------------+
    
  • 点语法可以与通配符(
    *
    )结合使用,以选择(可能是多个)字段,而无需明确指定名称:

    df.select($"a_struct.*").show
    // +---+
    // |  x|
    // +---+
    // |  1|
    // |  2|
    // +---+
    
  • 可以使用
    get_JSON_object
    from_JSON
    函数查询JSON列。有关详细信息,请参阅

    • 这是什么