Apache spark Spark:在不更改列的可空属性的情况下强制转换十进制

Apache spark Spark:在不更改列的可空属性的情况下强制转换十进制,apache-spark,apache-spark-sql,Apache Spark,Apache Spark Sql,将列强制转换为DataFrame中的DecimalType,似乎会更改nullable属性。具体来说,我有一个类型为DecimalType(12,4)的不可空列,我使用df.withColumn(columnName,df.col(columnName).cast(dataType))将其转换为DecimalType(38,9)。这将生成一个具有预期数据类型的字段,但该字段现在可以为空。有没有一种方法可以在不更改列的nullable属性的情况下强制转换 我在Spark 2.2.1和Spark 2

将列强制转换为
DataFrame
中的
DecimalType
,似乎会更改nullable属性。具体来说,我有一个类型为
DecimalType(12,4)
的不可空列,我使用
df.withColumn(columnName,df.col(columnName).cast(dataType))将其转换为
DecimalType(38,9)
。这将生成一个具有预期数据类型的字段,但该字段现在可以为空。有没有一种方法可以在不更改列的nullable属性的情况下强制转换


我在Spark 2.2.1和Spark 2.3.0中都观察到了这种行为。

感谢您提出了一个有趣的观点。我深入研究了一些源代码来理解这种行为,在我看来,答案是Cast.scala表示Cast表达式。暴露为nullability的属性的计算如下:

override def nullable: Boolean = Cast.forceNullable(child.dataType, dataType) || child.nullable

  def forceNullable(from: DataType, to: DataType): Boolean = (from, to) match {
  case (NullType, _) => true
  case (_, _) if from == to => false

  case (StringType, BinaryType) => false
  case (StringType, _) => true
  case (_, StringType) => false

  case (FloatType | DoubleType, TimestampType) => true
  case (TimestampType, DateType) => false
  case (_, DateType) => true
  case (DateType, TimestampType) => false
  case (DateType, _) => true
  case (_, CalendarIntervalType) => true

  case (_, _: DecimalType) => true  // overflow
  case (_: FractionalType, _: IntegralType) => true  // NaN, infinity
  case _ => false
}
如您所见,从任何类型到
DecimalType
的转换总是返回一个可为空的类型。我想知道为什么,可能是因为这里表示的溢出风险:

/**
 * Change the precision / scale in a given decimal to those set in `decimalType` (i  f any),
 * returning null if it overflows or modifying `value` in-place and returning it if successful.
 *
 * NOTE: this modifies `value` in-place, so don't call it on external data.
 */
private[this] def changePrecision(value: Decimal, decimalType: DecimalType): Decimal = {
  if (value.changePrecision(decimalType.precision,   decimalType.scale)) value else null
}
changePrecision
方法依次检查是否可以修改精度,如果可以,返回true,否则返回false。它解释了为什么上面的方法可以返回null,以及为什么DecimalType在源类型上独立强制转换时默认设置为null

因此,IMO没有简单的方法来保持原始列的可空性。也许你可以看看用户定义的类型,然后建立自己的源属性,保留DecimalType?但在我看来,可撤销性并非没有理由,我们会尊重这一点,以避免在不久或稍后的过程中出现一些不好的意外