Azure Databricks写入Azure SQL时Double值转为指数格式问题
问题描述
- 通过JDBC将Azure Databricks的Double类型数据写入Azure SQL数据库时,目标字段设为
nvarchar/varchar/float类型,写入值会自动转换为指数格式 - 目标字段设为
decimal/numeric类型时不会出现指数格式,但数值末尾会生成多余的0,且由于Databricks表数据未做规范定义,无法提前确认精度值是否能覆盖全量数据存储需求 - 提前将Databricks中对应列转为String类型再写入字符类型字段,数据仍然会被转换为指数格式
原写入代码如下:
%scala spark.sql("select ID from customers") .write .mode(SaveMode.Append) // 追加写入已有表 .jdbc(jdbcUrl,stg.customers, connectionProperties)
Databricks中ID列示例数据:
| ID |
|---|
| 1900845009567889.12 |
| 2134012183812321 |
Azure SQL目标字段设为numeric(38,15)时的写入结果:
| ID |
|---|
| 1900845009567889.1200000000000000 |
| 2134012183812321.0000000000000000 |
解决方案
问题根源是Spark对Double类型的默认字符串序列化、JDBC类型映射逻辑会自动触发科学计数法转换,用以下两步处理即可,不需要提前固定decimal精度,也不会出现多余尾0:
- Databricks端写入前做格式化处理:不要直接用原始Double类型、也不要直接用
cast(col as string)转字符串(Spark默认Double转String大数值就会出科学计数法,这是之前转String写入仍然失效的核心原因),用格式化函数把数值转成不带科学计数法、自动去除无意义尾0的字符串即可。
修改后的写入代码:
这段转换可以自动适配整数、带小数的场景:整数会去掉末尾的%scala import org.apache.spark.sql.functions.expr spark.sql("select ID from customers") // 核心转换:先按最高30位小数转成非科学计数法字符串,再用正则去除末尾多余的0和无意义的小数点 .withColumn("ID", expr("regexp_replace(format_number(ID, 30), '\\.?0+$', '')")) .write .mode(SaveMode.Append) .jdbc(jdbcUrl, "stg.customers", connectionProperties).0,带小数的值会只保留有效小数位,和原始展示值完全一致。 - Azure SQL端目标字段设置为
nvarchar(50)类型:此时JDBC拿到的是已经格式化好的纯数字字符串,不会触发数值类型自动转科学计数法的逻辑,写入结果完全符合预期。
如果后续需要在SQL中对该字段做数值计算,查询时临时用CAST(ID AS DECIMAL(38,15))转换即可,存储阶段用字符串类型可以完全规避精度丢失、格式异常问题。
内容的提问来源于stack exchange,提问作者Kshitij
相关产品推荐
相关产品推荐

