将MSSQL datetime2(7)值作为Python日期时间检索,并以微秒舍入而不是截断

时间:2017-11-13 13:40:52

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

如果我在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'}]

1 个答案:

答案 0 :(得分: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)