使用 CDate() 的查询过滤器产生不一致的结果
Query filter using CDate() produces inconsistent results
我尝试获取此查询 运行ning 但它没有像我预期的那样工作。
table 中有一个格式化为文本的日期字段(无法更改),我需要过滤此列。
日期看起来像
11/03/2022 (d/m/Y)
我的查询看起来像
session.query(DBGEUK)\
.filter(DBGEUK.VALIDATOR == '58')\
.filter(func.CDate(DBGEUK.DATE) <= datetime.now())\
.all()
共有 24 个条目。当我 运行 上面的查询并打印出 DBGEUK.DATE + datetime.now 时,这 9 个条目是我的结果。
27/03/2022 2022-03-28 19:06:49.465406
27/03/2022 2022-03-28 19:06:49.480988
27/03/2022 2022-03-28 19:06:49.480988
27/03/2022 2022-03-28 19:06:49.480988
28/03/2022 2022-03-28 19:06:49.480988
28/03/2022 2022-03-28 19:06:49.480988
28/03/2022 2022-03-28 19:06:49.480988
28/03/2022 2022-03-28 19:06:49.481612
28/03/2022 2022-03-28 19:06:49.481727
如果我将查询更改为大于 >= 我得到了其他 15 个条目
04/03/2022 2022-03-28 19:09:09.030659
04/03/2022 2022-03-28 19:09:09.031659
04/03/2022 2022-03-28 19:09:09.031659
04/03/2022 2022-03-28 19:09:09.031659
04/03/2022 2022-03-28 19:09:09.031659
05/03/2022 2022-03-28 19:09:09.031659
05/03/2022 2022-03-28 19:09:09.031659
05/03/2022 2022-03-28 19:09:09.031659
05/03/2022 2022-03-28 19:09:09.032657
11/03/2022 2022-03-28 19:09:09.032657
12/03/2022 2022-03-28 19:09:09.032657
11/03/2022 2022-03-28 19:09:09.032657
11/03/2022 2022-03-28 19:09:09.032657
09/03/2022 2022-03-28 19:09:09.033654
09/03/2022 2022-03-28 19:09:09.033654
感谢您的帮助。
CDate()
函数尝试根据 Windows 控制面板中的日期格式设置来解释日期字符串文字。使用 MM/dd/yyyy
的“短日期”设置,CDate("03/07/2022")
计算为 2022 年 3 月 7 日。使用 dd/MM/yyyy
的“短日期”设置,CDate("03/07/2022")
计算为 7 月 3 日, 2022.
但是,如果日期字符串表示无效日期,则 CDate()
将“有帮助”,并且 return 使用其他格式的有效日期。在上述两种情况下,CDate("14/03/2022")
将评估为 2022 年 3 月 14 日。
不幸的是,这意味着
- 如何解释日期取决于 Windows 日期格式,这可能因机器而异,甚至在同一台机器上因用户而异。
- 日期的解释在不明确和不明确的日期字符串之间可能不一致。
因此在这种情况下,我们需要避免使用 CDate()
并自行解析日期字符串:
from datetime import datetime
from sqlalchemy import create_engine, Column, String, select, func
from sqlalchemy.engine import URL
from sqlalchemy.orm import declarative_base, Session
accdb_path = r"C:\Users\Public\test\sqlalchemy-access\gord_test.accdb"
connection_string = (
"DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};"
f"DBQ={accdb_path};"
"ExtendedAnsiSQL=1;"
)
connection_url = URL.create(
"access+pyodbc", query={"odbc_connect": connection_string}
)
engine = create_engine(connection_url)
Base = declarative_base()
class DBGEUK(Base):
__tablename__ = "so71651145"
DATE = Column(String(10), primary_key=True)
def __repr__(self):
return f"<DBGEUK(DATE='{self.DATE}')>"
# create test environment
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
with Session(engine) as session:
# example data
session.add_all(
[
# How CDate() interprets the string
# based on Windows' short date format:
#
# dd/MM/yyyy MM/dd/yyyy
# ------------ ------------
DBGEUK(DATE="07/03/2022"), # Mar 7, 2022 Jul 3, 2022
DBGEUK(DATE="14/03/2022"), # Mar 14, 2022 Mar 14, 2022
DBGEUK(DATE="03/07/2022"), # Jul 3, 2022 Mar 7, 2022
DBGEUK(DATE="31/12/2022"), # Dec 31, 2022 Dec 31, 2022
]
)
session.commit()
# (for future readers of this answer)
print(datetime.now()) # 2022-03-29 08:44:00.512366
# original query
qry = select(DBGEUK).filter(func.CDate(DBGEUK.DATE) <= datetime.now())
results = session.scalars(qry).all()
print(results)
# [<DBGEUK(DATE='14/03/2022')>, <DBGEUK(DATE='03/07/2022')>]
#
# with Windows' short date format set to MM/dd/yyyy, CDate() interprets
# the second value as March 7, not July 3
# corrected query
qry = select(DBGEUK).filter(
func.DateSerial(
func.CInt(func.Mid(DBGEUK.DATE, 7, 4)), # year
func.CInt(func.Mid(DBGEUK.DATE, 4, 2)), # month
func.CInt(func.Mid(DBGEUK.DATE, 1, 2)), # day
)
<= datetime.now()
)
results = session.scalars(qry).all()
print(results)
# [<DBGEUK(DATE='07/03/2022')>, <DBGEUK(DATE='14/03/2022')>]
#
# all good
我尝试获取此查询 运行ning 但它没有像我预期的那样工作。
table 中有一个格式化为文本的日期字段(无法更改),我需要过滤此列。
日期看起来像
11/03/2022 (d/m/Y)
我的查询看起来像
session.query(DBGEUK)\
.filter(DBGEUK.VALIDATOR == '58')\
.filter(func.CDate(DBGEUK.DATE) <= datetime.now())\
.all()
共有 24 个条目。当我 运行 上面的查询并打印出 DBGEUK.DATE + datetime.now 时,这 9 个条目是我的结果。
27/03/2022 2022-03-28 19:06:49.465406
27/03/2022 2022-03-28 19:06:49.480988
27/03/2022 2022-03-28 19:06:49.480988
27/03/2022 2022-03-28 19:06:49.480988
28/03/2022 2022-03-28 19:06:49.480988
28/03/2022 2022-03-28 19:06:49.480988
28/03/2022 2022-03-28 19:06:49.480988
28/03/2022 2022-03-28 19:06:49.481612
28/03/2022 2022-03-28 19:06:49.481727
如果我将查询更改为大于 >= 我得到了其他 15 个条目
04/03/2022 2022-03-28 19:09:09.030659
04/03/2022 2022-03-28 19:09:09.031659
04/03/2022 2022-03-28 19:09:09.031659
04/03/2022 2022-03-28 19:09:09.031659
04/03/2022 2022-03-28 19:09:09.031659
05/03/2022 2022-03-28 19:09:09.031659
05/03/2022 2022-03-28 19:09:09.031659
05/03/2022 2022-03-28 19:09:09.031659
05/03/2022 2022-03-28 19:09:09.032657
11/03/2022 2022-03-28 19:09:09.032657
12/03/2022 2022-03-28 19:09:09.032657
11/03/2022 2022-03-28 19:09:09.032657
11/03/2022 2022-03-28 19:09:09.032657
09/03/2022 2022-03-28 19:09:09.033654
09/03/2022 2022-03-28 19:09:09.033654
感谢您的帮助。
CDate()
函数尝试根据 Windows 控制面板中的日期格式设置来解释日期字符串文字。使用 MM/dd/yyyy
的“短日期”设置,CDate("03/07/2022")
计算为 2022 年 3 月 7 日。使用 dd/MM/yyyy
的“短日期”设置,CDate("03/07/2022")
计算为 7 月 3 日, 2022.
但是,如果日期字符串表示无效日期,则 CDate()
将“有帮助”,并且 return 使用其他格式的有效日期。在上述两种情况下,CDate("14/03/2022")
将评估为 2022 年 3 月 14 日。
不幸的是,这意味着
- 如何解释日期取决于 Windows 日期格式,这可能因机器而异,甚至在同一台机器上因用户而异。
- 日期的解释在不明确和不明确的日期字符串之间可能不一致。
因此在这种情况下,我们需要避免使用 CDate()
并自行解析日期字符串:
from datetime import datetime
from sqlalchemy import create_engine, Column, String, select, func
from sqlalchemy.engine import URL
from sqlalchemy.orm import declarative_base, Session
accdb_path = r"C:\Users\Public\test\sqlalchemy-access\gord_test.accdb"
connection_string = (
"DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};"
f"DBQ={accdb_path};"
"ExtendedAnsiSQL=1;"
)
connection_url = URL.create(
"access+pyodbc", query={"odbc_connect": connection_string}
)
engine = create_engine(connection_url)
Base = declarative_base()
class DBGEUK(Base):
__tablename__ = "so71651145"
DATE = Column(String(10), primary_key=True)
def __repr__(self):
return f"<DBGEUK(DATE='{self.DATE}')>"
# create test environment
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
with Session(engine) as session:
# example data
session.add_all(
[
# How CDate() interprets the string
# based on Windows' short date format:
#
# dd/MM/yyyy MM/dd/yyyy
# ------------ ------------
DBGEUK(DATE="07/03/2022"), # Mar 7, 2022 Jul 3, 2022
DBGEUK(DATE="14/03/2022"), # Mar 14, 2022 Mar 14, 2022
DBGEUK(DATE="03/07/2022"), # Jul 3, 2022 Mar 7, 2022
DBGEUK(DATE="31/12/2022"), # Dec 31, 2022 Dec 31, 2022
]
)
session.commit()
# (for future readers of this answer)
print(datetime.now()) # 2022-03-29 08:44:00.512366
# original query
qry = select(DBGEUK).filter(func.CDate(DBGEUK.DATE) <= datetime.now())
results = session.scalars(qry).all()
print(results)
# [<DBGEUK(DATE='14/03/2022')>, <DBGEUK(DATE='03/07/2022')>]
#
# with Windows' short date format set to MM/dd/yyyy, CDate() interprets
# the second value as March 7, not July 3
# corrected query
qry = select(DBGEUK).filter(
func.DateSerial(
func.CInt(func.Mid(DBGEUK.DATE, 7, 4)), # year
func.CInt(func.Mid(DBGEUK.DATE, 4, 2)), # month
func.CInt(func.Mid(DBGEUK.DATE, 1, 2)), # day
)
<= datetime.now()
)
results = session.scalars(qry).all()
print(results)
# [<DBGEUK(DATE='07/03/2022')>, <DBGEUK(DATE='14/03/2022')>]
#
# all good