Spark案例类 - 十进制类型编码器错误"无法从十进制上升"

mis*_*spp 7 scala apache-spark apache-spark-sql

我正在从MySQL/MariaDB中提取数据,并且在创建数据集期间,数据类型会出错

线程"main"中的异常org.apache.spark.sql.AnalysisException:无法AMOUNT从十进制(30,6)向十进制(38,18)进行强制转换,因为它可能会截断目标对象的类型路径为: - field(class) :"org.apache.spark.sql.types.Decimal",名称:"AMOUNT") - 根类:"com.misp.spark.Deal"您可以向输入数据添加显式强制转换或选择更高的精度目标对象中字段的类型;

案例类定义如下

case class
(
AMOUNT: Decimal
)
Run Code Online (Sandbox Code Playgroud)

任何人都知道如何解决它而不是触摸数据库?

小智 10

那个错误说apache spark不能自动将BigDecimal(30,6)从数据库转换为BigDecimal(38,18),这在数据集中需要(我不知道为什么它需要固定的参数38,18.它甚至更多奇怪的是,火花不能自动将低精度的类型转换为高精度的类型).

据报道有一个错误:https://issues.apache.org/jira/browse/SPARK-20162(也许就是你).无论如何,我找到了通过在数据帧中将列转换为BigDecimal(38,18)然后将数据框转换为数据集来读取数据的良好解决方法.

//first read data to dataframe with any way suitable for you
var df: DataFrame = ???
val dfSchema = df.schema

import org.apache.spark.sql.functions._
import org.apache.spark.sql.types.DecimalType
dfSchema.foreach { field =>
  field.dataType match {
    case t: DecimalType if t != DecimalType(38, 18) =>
      df = df.withColumn(field.name, col(field.name).cast(DecimalType(38,18)))
  }
}
df.as[YourCaseClassWithBigDecimal]
Run Code Online (Sandbox Code Playgroud)

它应该解决阅读问题(但不是写作我猜)


Cho*_*ack 7

如前所述,由于您的数据库使用DecimalType(30,6)意味着总共有 30 个槽位,小数点后有 6 个槽位,剩下30-6=24小数点前面的区域。我喜欢称其为(24 left, 6 right)大十进制。这当然不适合 a (20 left, 18 right)(ie DecimalType(38,18)),因为后者左侧没有足够的插槽(需要 20 与 24)。我们只有 20 个左侧插槽DecimalType(38,18),但我们需要 24 个左侧插槽来容纳您DecimalType(30,6).

我们在这里可以做的是将 向下转换(24 left, 6 right)为 a (20 left, 6 right)(ie DecimalType(26,6)),以便当它自动转换为 a (20 left, 18 right)(ie DecimalType(38,18)) 时,两侧都会适合。您DecimalType(26,6)将有 20 个左插槽,可以容纳在 1 个插槽中,DecimalType(38,18)当然 6 个右插槽可以容纳在 18 个插槽中。

这样做的方法是在将任何内容转换为数据集之前,在数据帧上运行以下操作:

val downCastableData = 
  originalData.withColumn("amount", $"amount".cast(DecimalType(26,6)))
Run Code Online (Sandbox Code Playgroud)

然后转换为Dataset应该可以工作。

(实际上,您可以投射到任何(20 left, 6 right)或更少的东西,例如(19 left, 5 right)等......)。