SQLAlchemy/oracledb/cx_Oracle插入Timestamp被截断至秒的解决求助
需要将包含pandas._libs.tslibs.timestamps.Timestamp类型数据的DataFrame合并到Oracle数据库,DataFrame示例如下:
import pandas as pd my_test_date = pd.to_datetime(1674009901958454, unit='us') df = pd.DataFrame(data = {'payment_id_pay': [1, 2], 'DT_START': [my_test_date, my_test_date], 'DT_END': [my_test_date, my_test_date]})
通过SQLAlchemy执行合并后,数据库中Timestamp字段的毫秒/微秒部分被截断,仅保留到秒(毫秒部分显示为000000)。已尝试纯cx_Oracle、纯oracledb、SQLAlchemy调用setinputsizes,以及将Timestamp转换为datetime.datetime,均未解决问题。
使用版本:SQLAlchemy 2.0.25、oracledb 1.4.2、cx-Oracle 8.3.0;数据库:Oracle Database 19c Enterprise Edition。
请问如何实现不截断时间精度的插入?
以下几种方法可解决时间精度被截断的问题:
1. 确认Oracle表字段的精度支持
Oracle的DATE类型仅保留到秒,必须确保目标表的DT_START、DT_END字段类型为TIMESTAMP(6)(默认支持微秒)或TIMESTAMP(9)(Oracle 19c最高精度,支持纳秒),而非DATE类型。
2. SQLAlchemy映射类显式指定TIMESTAMP精度
在定义数据模型时,明确指定日期字段的类型和精度,让SQLAlchemy按高精度传递数据:
from sqlalchemy import Column, Integer, TIMESTAMP from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class Payment(Base): __tablename__ = 'your_target_table' payment_id_pay = Column(Integer, primary_key=True) DT_START = Column(TIMESTAMP(precision=6)) DT_END = Column(TIMESTAMP(precision=6))
使用该模型执行插入/合并操作时,会自动保留微秒精度。
3. oracledb显式指定绑定变量类型
直接使用oracledb操作时,通过setinputsizes强制指定字段为TIMESTAMP类型:
import oracledb # 建立数据库连接 conn = oracledb.connect(user="your_user", password="your_pwd", dsn="your_dsn") cursor = conn.cursor() # 准备待插入数据 data = [(1, my_test_date, my_test_date), (2, my_test_date, my_test_date)] # 显式指定时间字段的类型 cursor.setinputsizes(None, oracledb.DB_TYPE_TIMESTAMP, oracledb.DB_TYPE_TIMESTAMP) cursor.executemany("INSERT INTO your_target_table (payment_id_pay, DT_START, DT_END) VALUES (:1, :2, :3)", data) conn.commit() cursor.close() conn.close()
4. 转换为带微秒的字符串插入
将DataFrame中的Timestamp转换为包含微秒的标准时间字符串,直接以字符串形式插入,Oracle会自动解析为高精度TIMESTAMP:
# 转换时间字段为带微秒的字符串格式 df['DT_START'] = df['DT_START'].dt.strftime('%Y-%m-%d %H:%M:%S.%f') df['DT_END'] = df['DT_END'].dt.strftime('%Y-%m-%d %H:%M:%S.%f') # 使用to_sql插入数据 df.to_sql('your_target_table', con=engine, if_exists='append', index=False)
此方法需确保目标表字段为TIMESTAMP类型,否则字符串无法正确解析。
内容的提问来源于stack exchange,提问作者Faenno

