通过Synapse无服务器池OPENROWSET无法加载Parquet格式0001-01-01时间戳
问题根因
这个问题和DATETIME2类型本身的支持范围无关,本质是Azure Synapse无服务器SQL池的Parquet文件解析逻辑存在兼容性限制:
- Spark默认写Parquet文件时,会用
INT96类型存储时间戳,该类型的时间计算逻辑在Synapse无服务器池底层是基于1970-01-01的纪元偏移实现的,解析早于1970-01-01的时间时会出现整数溢出,无法正常映射到DATETIME2类型。 - 即便把Parquet的时间存储格式改成
TIMESTAMP_MILLIS/TIMESTAMP_MICROS,Synapse无服务器池目前对1900年之前的时间戳解析也存在未修复的缺陷,才会出现明明值在DATETIME2支持范围内却无法加载的报错。
可行解决方法
方案1:写入Parquet时预处理(推荐)
在Spark生成Parquet的阶段直接处理时间字段,从源头规避问题:
- 方法A:将所有早于1970-01-01的时间戳统一转成字符串类型存储,后续读取后再做类型转换即可。
- 方法B:对业务无影响的前提下,将
0001-01-01这类极早的默认时间替换为1900-01-01等Synapse可正常解析的时间值,业务侧统一做逻辑兼容。
示例Spark修改代码:
from pyspark.sql.functions import to_timestamp, when, date_format, lit dfx = ( spark.createDataFrame( data = [ ("1","0001-01-01 00:00:00.000")], schema=["id","my_ts"]) .withColumn("my_ts",to_timestamp("my_ts")) # 方案A:转字符串 .withColumn("my_ts_str", date_format("my_ts", "yyyy-MM-dd HH:mm:ss.SSS")) # 方案B:替换极早时间 .withColumn("my_ts_fixed", when(dfx.my_ts < lit("1900-01-01"), lit("1900-01-01 00:00:00.000").cast("timestamp")).otherwise(dfx.my_ts)) )
方案2:读取时绕开自动类型转换
如果无法修改源Parquet文件,可以在Synapse查询时指定schema,先把时间字段以字符串形式读取再手动转换:
SELECT id, TRY_CAST(my_ts_str AS DATETIME2(3)) AS my_ts FROM OPENROWSET( BULK 'https://<MY-WORKSPACE>.dfs.core.windows.net/<MY-CONTAINER>/dfx/**', FORMAT = 'PARQUET', SCHEMA = ( id VARCHAR(10), my_ts_str VARCHAR(30) ) ) AS [result]
内容的提问来源于stack exchange,提问作者Anders Swanson
相关产品推荐
相关产品推荐

