【问题标题】:sqlserver datetime to oracle timestamp problemssqlserver datetime 到 oracle 时间戳问题
【发布时间】:2016-10-21 21:04:42
【问题描述】:

所以我正在做一个将数据从 sql server db 移动到 oracle db 的项目,日期让我很难过。为了进行数据移动,我有一个 python 脚本,它从 sql server db 中提取数据,然后运行插入查询。

问题是日期看起来像这样2016-06-01 05:45:06.003,但如果我去掉小数秒,它将违反主键,因为2016-06-01 05:45:06 有很多记录,但2016-06-01 05:45:06.003 只有一个记录,所以它必须有毫秒在里面。

我应该提到oracle中列的数据类型是TIMESTAMP

如果我只是提取记录并运行我的插入,这只是一个基本插入,其值类似于 VALUES(:1, :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, :12, :13, :14, :15)

这样做会让我的约会看起来像这样01-JUN-16 07.05.41.000000000 AM。这很好,但对于每条记录,小数秒都是 000000,所以我失去了准确性,这让我违反了主键。

我认为解决方法是明确说明格式,因此我将插入语句值子句修改为 VALUES(TO_TIMESTAMP(:1, 'YYYY-MM-DD HH24:MI:SS,FF9'), :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, :12, :13, :14, :15)。这更糟糕,因为它将每行中的所有日期更改为16-JUN-20 12.00.00.000000000 AM 我也尝试了YYYY-MM-DD HH24:MI:SS,FF9,但这会产生相同的结果。

我确信这是我想念的愚蠢的东西,但对于我的生活,我不知道它是什么。

既然被问到,sql server 表上列的数据类型是DateTime,而在 oracle 服务器上,我目前将其设置为TimeStamp(3),但我可以在 oracle 端更改数据类型。

我正在使用 cx_oracle 连接并运行插入。我可以确认在插入运行之前数据确实有毫秒。

蟒蛇:

def RunQuery():
    srccrsr.execute(query)
    return srccrsr.fetchall()

def RunQuery():
    srccrsr.execute(query)
    return srccrsr.fetchall()

def BuildBindList(recordsToWrite):
    closingRecords = []
    for rec in recordsToWrite:
        closingRecords.append((rec[0], rec[1], rec[2], rec[3], rec[4], rec[5], rec[6], rec[7], rec[8], rec[9], rec[10], rec[11], rec[12], rec[13], rec[14]))
    return closingRecords   

def write_to_table(recordsToWrite):  
    SQL = """INSERT INTO TRACE (DATETIME, ID, TZ, DOMAINID, EVENTNAME, REASONCODE, TARGETID, STATE, KEY, PERIPHERALKEY, RECOVERYKEY, DIRECTION, ROUTERDAY, ROUTERCKEY, ROUTERNUMBER)
            VALUES(TO_TIMESTAMP(TO_CHAR(:1), 'yyyy-mm-dd hh24:mi:ss.ff3'), :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, :12, :13, :14, :15)"""
    try:
        trgtcrsr.prepare(SQL)
    except cx_Oracle.DatabaseError, exception:
        print ('Failed to prepare cursor')
        print Exception(exception)
        exit (1)
    try:
        trgtcrsr.executemany(None, recordsToWrite)        
    except cx_Oracle.DatabaseError, exception:
        print ('Failed to insert rows')
        print Exception(exception)
        exit (1)

    trgtcnn.commit()
    trgtcnn.close()
    source_connection.close()

def main():
    recordstowrite = BuildBindList(RunQuery())
    write_to_table(recordstowrite) 

if __name__ == "__main__":
    main()

【问题讨论】:

  • 从基础开始——你检查过Python脚本检索到的数据有毫秒级的数据吗? Python 脚本是将值存储为字符串值还是时间戳?
  • 这与 SQL Server 或 Oracle 几乎没有关系,尽管像这样使用时间戳作为主键不是一个好主意。在任何情况下,日期都没有格式,它们是二进制值。如果您将它们转换为文本并使用参数化查询,则可以将值从一个数据库按原样传递到另一个数据库。你用的是什么库?
  • 我完全清楚这不是一个好主意。但是,我是一个低级的合作社,他们不想改变它。不过,该键确实有其他列。
  • @user5999614 发布代码!没有它,您就是在要求人们猜测!如果源数据有毫秒,而目标字段接受毫秒,则问题出在脚本中
  • 来自这个Oracle articleFractional seconds of a date/time value passed as a bind variable will be truncated unless the setinputsizes() method is used between prepare() and execute()

标签: python sql-server oracle cx-oracle


【解决方案1】:

This Oracle article 警告:

作为绑定变量传递的日期/时间值的小数秒将被截断,除非在 prepare() 和 execute() 之间使用 setinputsizes() 方法

文章的示例显示,要节省毫秒,您需要为tval参数指定输入大小:

ts = datetime.datetime.now()
cursor.prepare("INSERT INTO python_tstamps VALUES(:t_val)")

cursor.setinputsizes(t_val=cx_Oracle.TIMESTAMP)

cursor.execute(None, {'t_val':ts})
db.commit()

【讨论】:

  • 使用带有位置参数的 set inputsizes() 给了我语法错误,所以我切换到命名参数。调用时间戳d_val 我在准备和执行之间有trgtcrsr.setinputsizes(d_val=cx_Oracle.TIMESTAMP),现在我得到ORA-01830: date format picture ends before converting entire input string
  • 这实际上是问题所在。我不得不把它弄得一团糟才能让它工作,但使用setinputsizes() 修复了它。
【解决方案2】:

您遇到的问题可能与您的 nls 格式设置有关。但是,我认为使用 to_timestamp 没有问题:

with sample_data as (select '2016-06-01 05:45:06.003' col1 from dual union all
                     select '2016-06-01 12:55:06.638' col1 from dual union all
                     select '2016-06-02 11:53:24.827' col1 from dual)
select col1,
       to_timestamp(col1, 'yyyy-mm-dd hh24:mi:ss.ff3') col1_ts,
       to_char(to_timestamp(col1, 'yyyy-mm-dd hh24:mi:ss.ff3'), 'yyyy-mm-dd hh24:mi:ss.ff3') col1_ts_str
from   sample_data;

COL1                    COL1_TS                                            COL1_TS_STR                  
----------------------- -------------------------------------------------- -----------------------------
2016-06-01 05:45:06.003 01/06/2016 05:45:06.003000000                      2016-06-01 05:45:06.003      
2016-06-01 12:55:06.638 01/06/2016 12:55:06.638000000                      2016-06-01 12:55:06.638      
2016-06-02 11:53:24.827 02/06/2016 11:53:24.827000000                      2016-06-02 11:53:24.827  

(col1_ts 列是 oracle 存储的时间戳,以默认格式作为字符串返回(在不同的客户端上可能会有所不同,具体取决于它们是否弄乱了它们的 nls 格式),而 col1_ts_str 是时间戳以我请求的特定格式作为字符串返回)。

您应该通过在时间戳列上执行 to_char 来检查数据是否已正确输入,例如

select to_char(your_timestamp_column, '<format>') ts_col
from   your_table;

【讨论】:

  • 这很奇怪,因为如果在 SQL Developer 中使用to_timestamp(col1, 'yyyy-mm-dd hh24:mi:ss.ff3') 运行选择,它会给出正确的结果。当它在 python 脚本的插入中使用时,它不会。
  • 那么,如果你通过 SQL Developer 运行插入,它会得到与在 Python 中运行时相同的结果吗?
  • 是的,它也可以在那里工作。也许是蟒蛇。如果我调试 python 脚本并查看从 sql server 中提取的记录,则表示日期值的类型是 datetime.datetime。但是毫秒仍然存在。 datetime.datetime(2016, 6, 1, 5, 45, 6, 20000)
猜你喜欢
  • 2014-07-05
  • 1970-01-01
  • 2012-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-19
  • 2018-01-04
  • 1970-01-01
相关资源
最近更新 更多