Apache spark spark sql和dataframes中控制字段的可空性

Apache spark spark sql和dataframes中控制字段的可空性,apache-spark,dataframe,apache-spark-sql,Apache Spark,Dataframe,Apache Spark Sql,我正在使用spark sql的DataFrame实现一个通用的数据集成组件。 基本思想是,用户通过命名字段并使用简单的sql片段(可以出现在select子句中的片段)映射字段来配置字段,组件添加这些列并将它们分组到结构字段中(使用DSL列) 稍后的处理将这些结构字段中的一些字段分组到一个数组中,此时我遇到了一个问题,即其中一个字段在一个元组中可为null,而在另一个元组中不可为null 由于字段分组在一个结构中,所以我能够提取结构类型,修改它并使用Column.cast方法将其应用回整个元组,我

我正在使用spark sql的DataFrame实现一个通用的数据集成组件。 基本思想是,用户通过命名字段并使用简单的sql片段(可以出现在select子句中的片段)映射字段来配置字段,组件添加这些列并将它们分组到结构字段中(使用DSL列)

稍后的处理将这些结构字段中的一些字段分组到一个数组中,此时我遇到了一个问题,即其中一个字段在一个元组中可为null,而在另一个元组中不可为null

由于字段分组在一个结构中,所以我能够提取结构类型,修改它并使用Column.cast方法将其应用回整个元组,我不确定这种方法是否适用于顶级字段(顺便说一句,SQL cast语法不允许指定字段的可空性)

我的问题是,有没有更好的方法来实现这一点?类似于nullable()函数,可以应用于表达式以将其标记为可为null,类似于cast的工作方式

示例代码:

val df = (1 to 8).map(x => (x,x+1)).toDF("x","y")
val df6 = df.select(
      functions.struct( $"x" + 1 as "x1", $"y" + 1 as "y1" ) as "struct1",
      functions.struct( $"x" + 1 as "x1", functions.lit(null).cast( DataTypes.IntegerType ) as "y1" ) as "struct2"
    )
val df7 = df6.select( functions.array($"struct1", $"struct2") as "arr" )
此操作失败,但出现以下异常:

由于数据类型不匹配,无法解析“数组(结构1,结构2)”: 函数数组的输入应该是相同的类型,但是 [struct,struct]; org.apache.spark.sql.AnalysisException:无法解析 数据类型不匹配导致的“数组(结构1,结构2)”:函数的输入 数组应该是相同的类型,但它是[struct, 结构]

修复程序如下所示:

//val df7 = df6.select( functions.array($"struct1", $"struct2") as "arr" )
val df7 = df6.select( functions.array($"struct1"  cast df6.schema("struct2").dataType, $"struct2" ) as "arr" )

您可以使用创建
选项[Int]
udf
使其更干净一些:

val optionInt = udf[Option[Int],Int](i => Option(i))
然后,在为
struct1
创建
y1
时,需要使用
optionInt($“y”+1)
。其他一切都保持不变(尽管为简洁起见进行了编辑)


然后
df6。选择(数组($“struct1”,“$“struct2”)作为“arr”)
工作正常。

这可能会工作,但它有一些不足之处首先它需要快速定义udf,然后必须为所用的所有类型定义它,另一件事是Catalyst不知道udf内部发生了什么,这可能意味着一些优化机会的损失。
val df6 = df.select(
  struct($"x" + 1 as "x1", optionInt($"y" + 1) as "y1" ) as "struct1",
  struct($"x" + 1 as "x1", lit(null).cast(IntegerType) as "y1" ) as "struct2"
)