Mar*_*der 6 apache-spark apache-spark-sql
将列强制转换为 aDecimalType中的 aDataFrame似乎会更改可为 null 的属性。具体来说,我有一个不可为空的类型列DecimalType(12, 4),我将其转换为DecimalType(38, 9)using df.withColumn(columnName, df.col(columnName).cast(dataType))。这会产生一个具有预期数据类型的字段,但该字段现在可为空。有没有一种方法可以在不更改列的可空属性的情况下进行转换?
我在 Spark 2.2.1 和 Spark 2.3.0 中都观察到了这种行为。
谢谢你提出一个有趣的观点。我深入研究了源代码来理解这种行为,IMO 答案是在代表强制转换表达式的 Cast.scala 中。暴露可空性的属性的计算方式如下:
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
}
Run Code Online (Sandbox Code Playgroud)
如您所见,从任何类型到的转换DecimalType始终返回可为 null 的类型。我想知道为什么,这可能是因为这里表达的溢出风险:
/**
* 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
}
Run Code Online (Sandbox Code Playgroud)
changePrecision方法依次检查精度是否可以修改,如果可以修改则返回 true,否则返回 false。它解释了为什么上述方法可以返回 null,以及为什么 DecimalType 在独立于源类型进行强制转换时默认设置为可为 null。
因此,IMO 没有简单的方法来保持原始列的可为空性。也许您可以尝试查看 UserDefinedTypes 并构建您自己的、源属性保留的 DecimalType ?但在我看来,可空性的存在并非没有原因,我们会尊重这一点,以避免迟早出现一些糟糕的意外情况。
| 归档时间: |
|
| 查看次数: |
2837 次 |
| 最近记录: |