Spark 3.3.1写入decimal(38,10)零值至Hive表变为NULL问题
问题:Spark写入Hive Parquet表时Decimal类型零值变为NULL
环境
- Apache Spark: 3.3.1
- Hive: 3.1.3(元数据存储+Hive服务)
- 表存储格式: PARQUET
- 插入方式:
dataframe.format("hive").insertInto("db.table") - 问题字段: 类型为
decimal(38,10)的col_REG
问题现象
在Spark中计算得到的decimal类型零值(DataFrame.show()显示为0E-10),通过insertInto写入Parquet格式的Hive表后,对应字段值变为NULL,无任何异常抛出。
预期结果
Spark DataFrame显示为0E-10,写入Hive表后应存储为0.0000000000(decimal(38,10)类型)而非NULL。
实际结果
- Spark DataFrame:字段值显示为
0E-10 - Hive表:对应行的该字段存储为NULL
代码示例
val res = df.withColumn("col_REG",when(col("col1_REG") === "C", col("col2").cast("decimal(38,10)")) .when(col("col3") === "D",col("col4").cast("decimal(38,10)")) .otherwise(lit("0.0000000000").cast("decimal(38,10)")) )
解决方案
1. 直接构造Decimal类型零值
避免通过字符串转换生成零值,直接创建符合精度要求的Decimal字面量,消除精度偏差问题:
import org.apache.spark.sql.types.Decimal val res = df.withColumn("col_REG", when(col("col1_REG") === "C", col("col2").cast("decimal(38,10)")) .when(col("col3") === "D", col("col4").cast("decimal(38,10)")) .otherwise(lit(Decimal(0, 38, 10))) )
2. 配置Spark与Hive兼容的序列化规则
添加以下配置,强制Spark采用与Hive兼容的Parquet序列化逻辑,保留Decimal零值的精度信息:
spark.conf.set("spark.sql.parquet.writeLegacyFormat", "true") spark.conf.set("spark.sql.decimalOperations.allowPrecisionLoss", "false")
3. 校验Hive表字段定义
确保Hive表中col_REG字段的类型严格定义为decimal(38,10),避免元数据不一致导致的解析错误:
ALTER TABLE db.table CHANGE COLUMN col_REG col_REG DECIMAL(38,10);
原因分析
Spark通过字符串转换生成的零值可能因精度处理逻辑被识别为极小值,在Parquet序列化时被标记为无效数据;Hive读取Parquet文件时,会将这类无效的Decimal值解析为NULL。直接构造Decimal类型零值可避免转换偏差,配置项则确保Spark与Hive的类型处理逻辑一致。
内容的提问来源于stack exchange,提问作者Chandra Prakash




