Scala SparkSQL创建UDF以处理列可以是有时结构和有时字符串时的异常
我正在努力创建一个udf来提取一些列数据。列很复杂,因为有时它是字符串,但在许多情况下可以是struct。我只想考虑列为struct时的时间,并提取它所需的信息 假设这个例子:Scala SparkSQL创建UDF以处理列可以是有时结构和有时字符串时的异常,scala,apache-spark,apache-spark-sql,Scala,Apache Spark,Apache Spark Sql,我正在努力创建一个udf来提取一些列数据。列很复杂,因为有时它是字符串,但在许多情况下可以是struct。我只想考虑列为struct时的时间,并提取它所需的信息 假设这个例子: SELECT annoyingCol.data From SomeDf 烦恼col.data等于string或struct,以避免出现如下错误:需要struct type,但得到string。我想知道是否可以创建一个检查列类型的自定义项,例如: SELECT case when isStruct(annoyingC
SELECT annoyingCol.data From SomeDf
烦恼col.data等于string或struct,以避免出现如下错误:需要struct type,但得到string代码>。我想知道是否可以创建一个检查列类型的自定义项,例如:
SELECT
case when isStruct(annoyingCol.data) then annoyingCol.data.my_data else null end
FROM SomeDf
我试过这个
val isStruct = udf((r: Row) => {
import org.apache.spark.sql.Row
import org.apache.spark.sql.types.BooleanType
import scala.util.Try
Try(r.getAs[String]("estimation_data_inc_waypoints")).isSuccess
}
)
spark.udf.register("isStruct", isStruct)
但是失败了,我知道我错过了一些东西。任何帮助都将不胜感激。从技术上讲,您可以创建如下udf
:
val isStruct = udf((r: Any) => r match {
case _: Row => true
case _ => false
})
val df = Seq(("foo", (1, "bar"))).toDF
df.select(isStruct($"_1")).show
// +-------+
// |UDF(_1)|
// +-------+
// | false|
// +-------+
df.select(isStruct($"_2")).show
// +-------+
// |UDF(_2)|
// +-------+
// | true|
// +-------+
但是
列很复杂,因为有时它是字符串,但在许多情况下可以是struct
听起来不对,因为DataFrame不能包含异构列,并且
需要结构类型,但得到字符串
是规划器错误,即使udf
的类型与数据不匹配,也不会引发此错误。相反,您将得到类似以下内容的运行时ClassCastException
:
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to org.apache.spark.sql.Row
所以你的问题在别的地方。可能在这里:
annoyingCol.data
如果输入的结构不好,并且hatteringcol
有时被推断为StringType
,则点语法将不起作用,您将得到问题中的查询计划器异常
这应该在查询之外处理。您可以检查以下类型:
df.schema("annoyingCol").dataType match {
case _: StructType => ??? // Take some path
case _ => ??? // Take another path
}
或者可以
如果可以的话,我强烈建议您在源代码处或在Spark中将数据解析为DataFrame
之前,在上游解决此问题。您的意思是不同的DataFrame(具有相同的列名)的列类型不同吗?不,Dataframe是相同的,但模式可能会更改json数组并不严格,在某些情况下可能会更改,因此每一行可能略有不同。Spark在了解json模式是什么时做得很好,但由于它只是对行进行采样,Spark可能会错过json结构不同的一些情况。
if (hasColumn(df, "annoyingCol.data") {
??? // Take some path
} else {
??? // Take another path
}