【问题标题】:Retrieve MSSQL datetime2(7) value as Python datetime with microseconds rounded instead of truncated将 MSSQL datetime2(7) 值检索为 Python 日期时间,微秒舍入而不是截断
【发布时间】:2017-11-13 13:40:52
【问题描述】:

如果我在 T-SQL 中执行 CAST0397 会通过适当的舍入舍入为 040,但 pyodbc 会将其截断为 039。我怎样才能像 SQL Server 那样轻松地进行这种舍入?

1> select logid, timestamputc from eventlog where logid=166944;
2> go
logid                timestamputc
-------------------- --------------------------------------
              166944            2017-05-30 08:59:37.6650397

1> select logid from eventlog
where cast(timestamputc as datetime2(6))='2017-05-30 08:59:37.665039';
2> go
logid
--------------------
(0 rows affected)

1> select logid from eventlog
where cast(timestamputc as datetime2(6))='2017-05-30 08:59:37.665040';
2> go
logid
--------------------
              166944

使用pyodbc:

[{'logid': 166944, 'timestamputc': '2017-05-30 08:59:37.665039'}]

【问题讨论】:

    标签: python sql-server python-3.x pyodbc


    【解决方案1】:

    正如您所发现的,当 pyodbc 检索 datetime2(7) 列作为 Python datetime 对象时,其默认行为是截断小数点后七位。如果您希望对 datetime 对象进行四舍五入,因为 SQL Server 会将 datetime2(7) 值 CAST 返回到 datetime2(6),那么您可以使用 output converter function

    例如,如果您将输出转换器函数定义为

    def handle_datetime2(dt2_value):
        tup = struct.unpack("<6hI", dt2_value)  # e.g., (2017, 5, 30, 8, 59, 37, 0, 665039700)
        return datetime(tup[0], tup[1], tup[2],
                        hour=tup[3], minute=tup[4], second=tup[5],
                        microsecond=math.floor(tup[6] / 1000.0 + 0.5))
    

    并像这样使用它

    cnxn = pyodbc.connect(conn_str, autocommit=True)
    crsr = cnxn.cursor()
    
    cnxn.add_output_converter(pyodbc.SQL_TYPE_TIMESTAMP, handle_datetime2)
    
    dt_string = '2017-05-30 08:59:37.6650397'
    dt_value = crsr.execute(f"SELECT CAST('{dt_string}' AS DATETIME2(7))").fetchval()
    print(f'{dt_string}\n -> {repr(dt_value)}')
    
    dt_string = '2017-05-30 08:59:37.6650395'
    dt_value = crsr.execute(f"SELECT CAST('{dt_string}' AS DATETIME2(7))").fetchval()
    print(f'{dt_string}\n -> {repr(dt_value)}')
    
    dt_string = '2017-05-30 08:59:37.6650394'
    dt_value = crsr.execute(f"SELECT CAST('{dt_string}' AS DATETIME2(7))").fetchval()
    print(f'{dt_string}\n -> {repr(dt_value)}')
    

    结果将如下所示

    2017-05-30 08:59:37.6650397
     -> datetime.datetime(2017, 5, 30, 8, 59, 37, 665040)
    2017-05-30 08:59:37.6650395
     -> datetime.datetime(2017, 5, 30, 8, 59, 37, 665040)
    2017-05-30 08:59:37.6650394
     -> datetime.datetime(2017, 5, 30, 8, 59, 37, 665039)
    

    【讨论】:

    • 感谢您提供的出色示例。我将对此进行测试。有没有一种简单的方法可以从 datetime2(7) 中删除最后 100nano 数字而不在 mssql 内四舍五入。在 oracle 中,我只设置了 NLS_TIMESTAMP_FORMAT,一切都会自动转换。
    • 在mssql中可以这样做:cast(left(timestamputc, len(timestamputc)-1) as datetime2(6))
    • 我已经有了一个使用connection.add_output_converter(pyodbc.SQL_TYPE_TIMESTAMP, datetime_as_string) 的日期时间类型转换器。如何区分 datetime 和 datetime2(6) 的 output_converters?
    • @DirkR - 你不需要。无论 datetime2 精度值如何,Microsoft 的 ODBC 驱动程序都将小数秒返回为纳秒。
    • @GordThompson “我不需要”。抱歉,这对我没有帮助。如果我针对日期时间列运行此处理程序,我会在 tup = struct.unpack("&lt;6hI", dt2_value) 上收到错误消息。我使用struct.unpack("&lt;2l", value) 处理日期时间,使用struct.unpack("&lt;6hI2h" 处理日期时间2。目前我能做到这一点的唯一方法是尝试一个,如果出错则尝试另一个。因此我的问题。
    猜你喜欢
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 2017-12-26
    • 2021-12-16
    • 2012-02-19
    • 2015-12-07
    相关资源
    最近更新 更多