【问题标题】:MS SQL Python Datetime later than but not equal '>' returning result as '>='MS SQL Python 日期时间晚于但不等于 '>' 返回结果为 '>='
【发布时间】:2018-09-01 01:46:20
【问题描述】:

我正在尝试查询 Microsoft SQL 数据库以获取上一个循环中返回的日期时间之后的所有记录。我使用“>”运算符,但我看到的结果是“>=”(大于或等于)。

Python (3.6.5 Win10) 脚本:

import pyodbc  # version 4.0.24
import time
import datetime
import json
con = pyodbc.connect("DRIVER={ODBC Driver 17 for SQL Server};SERVER=localhost;DATABASE=mydb;UID=myuid;PWD=mypwd")
cur = con.cursor()
i = 0
lastDatetime = datetime.datetime.strptime('01Jan1970', '%d%b%Y')
while i < 60:
    print("{0}: SELECT * FROM dbo.Data WHERE datetime > '{1}'".format(str(i), lastDatetime))
    cur.execute("SELECT * FROM dbo.Data WHERE datetime > ?", lastDatetime)
    rows = cur.fetchall()
    for k, v in enumerate(rows):
        jsonMsg = json.dumps({
            "transaction_id": v[1],
            "plu": v[2].strip(),
            "qty": int(v[3]),
            "dateTime": str(v[4])
        }, separators=(',', ':'))
        print(str(jsonMsg))
        lastDatetime = v[6]
        print("lastDatetime set to: {0}".format(lastDatetime))
    i = i + 1
    time.sleep(5)

生产:

0: SELECT * FROM dbo.Data WHERE datetime > '1970-01-01 00:00:00'
{"transaction_id":3201,"plu":"3","qty":1,"dateTime":"2018-08-01 10:45:40.560000"}
lastDatetime set to: 2018-08-01 10:45:40.560000
{"transaction_id":3202,"plu":"5","qty":1,"dateTime":"2018-08-01 10:45:51.563000"}
lastDatetime set to: 2018-08-01 10:48:47.653000
{"transaction_id":3230,"plu":"8","qty":2,"dateTime":"2018-08-01 10:48:47.653000"}
lastDatetime set to: 2018-08-01 10:48:47.653000
1: SELECT * FROM dbo.Data WHERE datetime > '2018-08-01 10:48:47.653000'
{"transaction_id":3230,"plu":"8","qty":2,"dateTime":"2018-08-01 10:48:47.653000"}
lastDatetime set to: 2018-08-01 10:48:47.653000
2: SELECT * FROM dbo.Data WHERE datetime > '2018-08-01 10:48:47.653000'
{"transaction_id":3230,"plu":"8","qty":2,"dateTime":"2018-08-01 10:48:47.653000"}
lastDatetime set to: 2018-08-01 10:48:47.653000

我在 SQL Server Management Studio 中运行查询并得到预期结果:

SELECT * FROM dbo.Data WHERE datetime > '2018-08-01 10:48:47.653' 

不返回任何记录。

我遗漏了什么或者我应该在 python 脚本中更改什么以获得预期的结果?

谢谢。

编辑: 将 '' 添加到数据时间周围的 sql 字符串的打印中。对结果没有影响。

【问题讨论】:

  • 您在查询中缺少撇号 (') SELECT * FROM dbo.Data WHERE datetime > 2018-08-01 10:48:47.653000 它应该是 SELECT * FROM dbo.Data WHERE datetime > '2018 -08-01 10:48:47.653000'
  • @maziyank 你是对的,但它只是打印我认为是查询的内容。这不是代码下一行的实际查询。

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


【解决方案1】:

您正在实现 SQL 2016+ 的重大更改:

在数据库兼容级别 130 下,从 datetime 到 datetime2 数据类型通过会计显示提高了准确性 对于小数毫秒,导致不同的转换 价值观。每当混合时使用显式转换为 datetime2 数据类型 存在 datetime 和 datetime2 数据类型之间的比较场景。 如需更多信息,请参阅此Microsoft Support Article

Breaking Changes in SQL Server 2016

问题是您的表仍在使用旧的datetime 数据类型。这是一种非常奇怪的类型。它没有毫秒分辨率。它的分辨率实际上是 1/300 秒。所以实际存储的值并不总是看起来像的那样。存储不精确,例如浮点数。

新的更好的datetime2 具有更高的精度,并且始终存储十进制小数秒。无论如何,SQL 2016 改变了 datetime 转换为 datetime2 的方式,这具有使涉及隐式转换的比较有时会中断的副作用。

要解决此问题,请将表上的数据类型更改为 datetime2(3) 以达到 1 毫秒的精度。或强制参数为日期时间,而不是日期时间2。我不知道 pyodbc 是否允许这样做,但您可以轻松地在查询中强制转换:

cur.execute("SELECT * FROM dbo.Data WHERE datetime >= cast(? as datetime)", lastDatetime)

现在,您无需将表格列转换为datetime2,而是将参数值转换为datetime,并且比较会按预期进行。

或者将您的数据库兼容级别降低到 120。

【讨论】:

    【解决方案2】:

    我已通过在最后一次添加 1 毫秒并将“>”更改为“>=”来解决此问题。由于毫秒是目前MS SQL中最小的时间增量,所以这种方法应该不会丢失任何数据。

    我怀疑它与 python 处理浮点数有关,但不能确定将 1 毫秒作为浮点数添加到秒部分并没有完全增加它。

    修改后的代码:

    import pyodbc  # version 4.0.24
    import time
    import datetime
    import json
    con = pyodbc.connect("DRIVER={ODBC Driver 17 for SQL Server};SERVER=localhost;DATABASE=mydb;UID=myuid;PWD=mypwd")
    cur = con.cursor()
    i = 0
    lastDatetime = datetime.datetime.strptime('01Jan1970', '%d%b%Y')
    while i < 60:
        print("{0}: SELECT * FROM dbo.Data WHERE datetime >= '{1}'".format(str(i), lastDatetime))
        cur.execute("SELECT * FROM dbo.Data WHERE datetime >= ?", lastDatetime)
        rows = cur.fetchall()
        for k, v in enumerate(rows):
            jsonMsg = json.dumps({
                "transaction_id": v[1],
                "plu": v[2].strip(),
                "qty": int(v[3]),
                "dateTime": str(v[4])
            }, separators=(',', ':'))
            print(str(jsonMsg))
            lastDatetime = v[6]
            # work around a bug with datetime > returning as if >=
            lastDatetime = lastDatetime + datetime.timedelta(milliseconds=1)
            print("lastDatetime set to: {0}".format(lastDatetime))
        i = i + 1
        time.sleep(5)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-19
      • 1970-01-01
      • 2020-12-14
      • 1970-01-01
      • 2022-01-24
      相关资源
      最近更新 更多