Pandas+SQLAlchemy将Datetime转为Date加载至SQL Server的问题
解决Pandas加载数据到SQL Server时仅保留日期部分的问题
我明白你遇到的困扰了——用dt.floor('d')或dt.normalize()处理后,SQL Server里要么显示带00:00的完整datetime,要么列被转成字符串。核心原因是Python的datetime.datetime本身没有纯日期的概念,即使时间部分为0,它依然是datetime类型;而Pandas默认的datetime64[ns]类型也会保留时间戳结构。要让SQL Server真正存储纯日期,需要从两个关键环节调整:
1. 将Pandas列转换为纯date对象类型
把datetime64类型的列转换成Python原生的datetime.date对象,彻底剥离时间部分。修改LoadData里的处理逻辑:
df['uplod_tmstp'] = df['uplod_tmstp'].dt.date
这一步会把每一行的时间戳从datetime.datetime(2020,5,5,0,0)变成datetime.date(2020,5,5),完全去除时间信息。
2. 调整sqlcol函数,正确识别date类型
转换后的列在Pandas里的dtype会变成object(因为存储的是Python原生对象),原来的函数只检查datetime类型,会误把它当成字符串处理。我们需要增加判断逻辑,识别存储date对象的列:
首先记得导入datetime模块:
import datetime
然后修改sqlcol函数:
def sqlcol(df): dtypedict = {} for c in df: col_dtype = str(df[c].dtype) if 'object' in col_dtype: # 检查列是否存储的是date对象 if len(df[c]) > 0 and isinstance(df[c].iloc[0], datetime.date): dtypedict.update({c: sa.types.Date()}) else: # 处理普通字符串列,默认长度50避免空值报错 max_len = df[c].map(len).max() if len(df[c]) > 0 else 50 dtypedict.update({c: sa.types.NVARCHAR(length=max_len)}) elif "datetime" in col_dtype: dtypedict.update({c: sa.types.Date()}) return dtypedict
完整修改后的代码
import sqlalchemy as sa import pandas as pd import pyodbc import urllib import datetime # 新增导入 class myengine(object): def engine(): params = urllib.parse.quote_plus("DRIVER={SQL Server Native Client 11.0};" "SERVER=myserver;" "DATABASE=DB1;" "Trusted_Connection=yes;") engine = sa.create_engine("mssql+pyodbc:///?odbc_connect={}".format(params),echo=True) @sa.event.listens_for(engine, 'before_cursor_execute') def receive_before_cursor_execute(conn, cursor, statement, params, context, executemany): if executemany: cursor.fast_executemany = True cursor.commit() return engine def sqlcol(df): dtypedict = {} for c in df: col_dtype = str(df[c].dtype) if 'object' in col_dtype: # 识别date对象列 if len(df[c]) > 0 and isinstance(df[c].iloc[0], datetime.date): dtypedict.update({c: sa.types.Date()}) else: max_len = df[c].map(len).max() if len(df[c]) > 0 else 50 dtypedict.update({c: sa.types.NVARCHAR(length=max_len)}) elif "datetime" in col_dtype: dtypedict.update({c: sa.types.Date()}) return dtypedict def LoadData (f): df = pd.read_csv(f, dtype='str',parse_dates=['uplod_tmstp']) # 转换为纯date对象 df['uplod_tmstp'] = df['uplod_tmstp'].dt.date df.to_sql('Myable', myengine.engine() , if_exists='replace', schema='dbo', index=False, chunksize=1000, dtype=sqlcol(df)) if __name__ == "__main__": LoadData('test.csv')
为什么这样有效?
dt.date直接生成纯日期对象,彻底移除时间信息;- 修改后的
sqlcol函数能正确识别这类列,并指定SQL Server的Date类型,确保写入数据库时不会带上时间部分; - 避免了字符串转换的问题,保持了日期的原生类型,方便后续SQL查询时的日期操作。
内容的提问来源于stack exchange,提问作者Tyger Guzman
相关产品推荐
相关产品推荐

