Spark 3.1.3中Decimal类型round结果不符及优化关闭咨询
关于Spark 3.1.3 Decimal除法后Round精度不符的问题
问题重现代码
Dataset<Row> dataset = loadSource(); // 可忽略 dataset = dataset.withColumn("a", lit(1)); // 可忽略 dataset = dataset.withColumn("round", functions.round(lit(1).divide(lit(3)), 8)); // ① dataset = dataset.withColumn("round2", functions.round(lit(1).cast(DataTypes.createDecimalType(35, 10)).divide(lit(3).cast(DataTypes.createDecimalType(35, 10))), 8)); // ② dataset.explain(true); dataset.show();
执行结果
| round | round2 |
|---|---|
| 0.33333333 | 0.333333 |
预期round2结果同样为0.33333333,但实际仅保留6位小数。
执行计划对比
-- round的执行计划 round((cast(1 as double) / cast(3 as double)), 8) AS round -- round2的执行计划 round(CheckOverflow((promote_precision(cast(cast(1 as decimal(35,10)) as decimal(35,10))) / promote_precision(cast(cast(3 as decimal(35,10)) as decimal(35,10)))), DecimalType(38,6), true), 8) AS round2
从执行计划可见,round2的Decimal除法结果被CheckOverflow限制为DecimalType(38,6),导致后续Round到8位小数时已无足够精度支撑。
解决方案:关闭Decimal溢出检查
可以通过设置Spark配置参数关闭Decimal运算的溢出检查,从而保留足够精度:
代码中配置
spark.conf().set("spark.sql.decimalOperations.allowPrecisionLoss", "false"); spark.conf().set("spark.sql.ansi.enabled", "false");
提交命令中配置
--conf spark.sql.decimalOperations.allowPrecisionLoss=false --conf spark.sql.ansi.enabled=false
参数说明
spark.sql.ansi.enabled:关闭ANSI模式后,Decimal运算不会强制触发溢出检查与截断;spark.sql.decimalOperations.allowPrecisionLoss:设为false时,Decimal运算会尽量保留精度,避免自动截断到低精度类型。
修改配置后,round2的执行计划将移除CheckOverflow限制,除法结果保留足够小数位,最终Round到8位时即可得到预期的0.33333333。
内容的提问来源于stack exchange,提问作者Grand
相关产品推荐
相关产品推荐

