查询复杂类型的Spark SQL数据帧
如何查询具有复杂类型(如映射/数组)的RDD? 例如,当我编写此测试代码时:查询复杂类型的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
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还支持越来越多的在复杂类型上运行的内置函数。值得注意的例子包括高阶函数,如
(SQL 2.4+、Scala 3.0+、PySpark/SparkR 3.1+):transform
(SQL 2.4+、Scala 3.0+、Python/SparkR 3.1+)过滤器
(SQL 2.4+、Scala 3.0+、PySpark/SparkR 3.1+:聚合
- 数组处理功能(
)如array.*
(2.4+):array.\u distinct
(array\u max
,2.4+):array\u min
(2.4+)展平
(2.4+):阵列\u-zip
(2.4+):array\u union
(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.*
(2.3+)map.\u键
- 或
映射值
(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| // +---+
- 使用DataFrameAPI
- 可以使用点语法、名称和标准
方法访问列
结构数组中的字段:
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版本的不同,这些方法中的某些方法只能在
中使用。UDF应该独立于标准HiveContext
和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
函数查询JSON列。有关详细信息,请参阅from_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还支持越来越多的在复杂类型上运行的内置函数。值得注意的例子包括高阶函数,如
(SQL 2.4+、Scala 3.0+、PySpark/SparkR 3.1+):transform
(SQL 2.4+、Scala 3.0+、Python/SparkR 3.1+)过滤器
(SQL 2.4+、Scala 3.0+、PySpark/SparkR 3.1+:聚合
- 数组处理功能(
)如array.*
(2.4+):array.\u distinct
(array\u max
,2.4+):array\u min
(2.4+)展平
(2.4+):阵列\u-zip
(2.4+):array\u union
(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.*
(2.3+)map.\u键
- 或
映射值
(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| // +---+
- 使用DataFrameAPI
- 可以使用点语法、名称和标准
方法访问列
结构数组中的字段:
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版本的不同,这些方法中的某些方法只能在
中使用。UDF应该独立于标准HiveContext
和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
函数查询JSON列。有关详细信息,请参阅from_JSON
- 这是什么