PySpark通过JDBC读取MSSQL datetime时避免时区转换问题
PySpark读取MSSQL datetime时区偏移问题解决
问题背景
- MSSQL表结构:
CREATE TABLE [config].[some_temp_table]( [id] [int] NULL, [dt] [datetime] NULL ) -- 表中数据 -- id dt -- 1 2022-07-18 23:11:26.613 - 存储说明:
dt字段实际为UTC时间 - 环境时区:Spark服务器时区为
CEST +0200,MSSQL数据库时区为(UTC+01:00) - 问题现象:Spark通过JDBC读取后,
dt字段被转换为2022-07-18 21:11:26.613,出现2小时偏移 - 核心需求:读取时保留数据库原时间值,直接保存到Parquet,读取Parquet时可见原时间
已尝试无效方案
- 设置
spark.conf.set("spark.sql.session.timeZone", "UTC"),无效果 - 在JDBC URI中添加
serverTimezone=UTC,无效果(该参数为MySQL JDBC驱动专属,MSSQL不支持)
有效解决方案
方案1:以字符串形式读取datetime字段(推荐)
通过JDBC查询时,将datetime字段转换为字符串,完全规避Spark和JDBC驱动的时区转换逻辑:
df = spark.read.format("jdbc").options( url="jdbc:sqlserver://<你的数据库地址>:<端口>;databaseName=<数据库名>;user=<用户名>;password=<密码>", query="SELECT id, CONVERT(varchar(23), dt, 121) AS dt FROM config.some_temp_table" ).load()
CONVERT(varchar(23), dt, 121)会将datetime转成yyyy-MM-dd HH:mm:ss.SSS格式的字符串,完整保留原时间值- 读取后直接保存Parquet即可,后续读取时字符串类型不会有偏移问题
方案2:字符串转Timestamp并固定时区
如果需要将字段保留为Timestamp类型,读取字符串后指定UTC时区转换:
from pyspark.sql.functions import to_timestamp # 先以字符串读取 df = spark.read.format("jdbc").options( url="jdbc:sqlserver://<你的数据库地址>:<端口>;databaseName=<数据库名>;user=<用户名>;password=<密码>", query="SELECT id, CONVERT(varchar(23), dt, 121) AS dt FROM config.some_temp_table" ).load() # 设置会话时区为UTC,转换字符串为Timestamp spark.conf.set("spark.sql.session.timeZone", "UTC") df = df.withColumn("dt", to_timestamp("dt", "yyyy-MM-dd HH:mm:ss.SSS")) # 保存到Parquet df.write.parquet("/path/to/your/parquet/file")
- 读取Parquet时,同样需要设置
spark.sql.session.timeZone=UTC,才能看到正确的原时间值
无效方案原因说明
spark.sql.session.timeZone仅控制Spark内部Timestamp的显示和计算时区,无法干预JDBC驱动读取MSSQL datetime时的转换逻辑(JDBC驱动会按JVM时区转换datetime为Java Timestamp)serverTimezone=UTC是MySQL JDBC驱动的参数,MSSQL JDBC驱动不支持该配置,因此无效果
内容的提问来源于stack exchange,提问作者mhan
相关产品推荐
相关产品推荐

