GOOGLE ADS

Dienstag, 3. Mai 2022

Abfragefilter mit CDate() erzeugen inkonsistente Ergebnisse

Ich versuche, diese Abfrage zum Laufen zu bringen, aber sie funktioniert nicht wie erwartet.

In der Tabelle ist ein als Text formatiertes Datumsfeld (kann es nicht ändern) und ich muss diese Spalte filtern.

Termine aussehen

11/03/2022 (d/m/Y)

Meine Abfrage sieht aus wie

session.query(DBGEUK)\
.filter(DBGEUK.VALIDATOR == '58')\
.filter(func.CDate(DBGEUK.DATE) <= datetime.now())\
.all()

Es gibt insgesamt 24 Einträge. Wenn ich die obige Abfrage ausführe und DBGEUK.DATE + datetime.now ausdrucke, sind diese 9 Einträge mein Ergebnis.

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

Wenn ich die Abfrage auf größer als >= ändere, bekomme ich die anderen 15 Einträge

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

Vielen Dank für Ihre Hilfe im Voraus.


Lösung des Problems

Die CDate()Funktion versucht, Datumszeichenfolgenliterale gemäß den Datumsformatierungseinstellungen in der Windows-Systemsteuerung zu interpretieren. Bei einer „kurzen Datumseinstellung" von MM/dd/yyyywird CDate("03/07/2022")der 7. März 2022 ausgewertet. Bei einer „kurzen Datumseinstellung" von dd/MM/yyyywird CDate("03/07/2022")der 3. Juli 2022 ausgewertet.

Wenn die Datumszeichenfolge jedoch ein ungültiges Datum darstellt CDate(), ist "hilfreich" und gibt ein gültiges Datum mit dem anderen Format zurück. In beiden oben genannten Fällen CDate("14/03/2022")wird der 14. März 2022 ausgewertet.

Das bedeutet leider

  • Wie die Daten interpretiert werden, hängt vom Windows-Datumsformat ab, das von Computer zu Computer und sogar von Benutzer zu Benutzer auf demselben Computer variieren kann.

  • Die Interpretation von Datumsangaben kann zwischen mehrdeutigen und eindeutigen Datumszeichenfolgen inkonsistent sein.

  • Daher müssen wir in diesem Fall vermeiden, CDate()die Datumszeichenfolge selbst zu verwenden und zu analysieren:

    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

    Keine Kommentare:

    Kommentar veröffentlichen

    Warum werden SCHED_FIFO-Threads derselben physischen CPU zugewiesen, obwohl CPUs im Leerlauf verfügbar sind?

    Lösung des Problems Wenn ich das richtig verstehe, versuchen Sie, SCHED_FIFO mit aktiviertem Hyperthreading ("HT") zu verwenden, ...