SQLAlchemy: Wie filtere ich ein Datumsfeld?

105

Hier ist Modell:

class User(Base):
    ...
    birthday = Column(Date, index=True)   #in database it's like '1987-01-17'
    ...

Ich möchte zwischen zwei Daten filtern, um beispielsweise alle Benutzer im Intervall von 18 bis 30 Jahren auszuwählen.

Wie implementiere ich es mit SQLAlchemy?

Ich denke an:

query = DBSession.query(User).filter(
    and_(User.birthday >= '1988-01-17', User.birthday <= '1985-01-17')
) 

# means age >= 24 and age <= 27

Ich weiß, dass dies nicht korrekt ist, aber wie kann ich es richtig machen?

Vitalii Ponomar
quelle

Antworten:

181

Tatsächlich ist Ihre Abfrage bis auf den Tippfehler richtig: Ihr Filter schließt alle Datensätze aus: Sie sollten das <=for ändern >=und umgekehrt:

qry = DBSession.query(User).filter(
        and_(User.birthday <= '1988-01-17', User.birthday >= '1985-01-17'))
# or same:
qry = DBSession.query(User).filter(User.birthday <= '1988-01-17').\
        filter(User.birthday >= '1985-01-17')

Sie können auch verwenden between:

qry = DBSession.query(User).filter(User.birthday.between('1985-01-17', '1988-01-17'))
van
quelle
28
Übrigens können Sie stattdessen '1985-01-17'auch verwenden datetime.date(1985, 1, 17)- in einigen Umgebungen ist es möglicherweise einfacher, darauf zuzugreifen oder damit zu arbeiten.
Tossbyte
5
@rippleslash: Sie haben Recht, und im Idealfall sollte man den richtigen Datentyp für Parameter verwenden. Alle Datenbanken verstehen jedoch auch das ISO 8601-Format für Datumsangaben, das ebenfalls in lexikografischer Reihenfolge vorliegt. Aus diesem Grund verwende ich für einfache Beispiele im Allgemeinen ISO-formatierte Daten - einfacher zu lesen.
van
4
from app import SQLAlchemyDB as db

Chance.query.filter(Chance.repo_id==repo_id, 
                    Chance.status=="1", 
                    db.func.date(Chance.apply_time)<=end, 
                    db.func.date(Chance.apply_time)>=start).count()

es ist gleich:

select
   count(id)
from
   Chance
where
   repo_id=:repo_id 
   and status='1'
   and date(apple_time) <= end
   and date(apple_time) >= start

Wunsch kann Ihnen helfen.

ein Werkzeug
quelle
3

Wenn Sie den gesamten Zeitraum erhalten möchten :

    from sqlalchemy import and_, func

    query = DBSession.query(User).filter(and_(func.date(User.birthday) >= '1985-01-17'),\
                                              func.date(User.birthday) <= '1988-01-17'))

Das bedeutet Bereich: 1985-01-17 00:00 - 1988-01-17 23:59

MrNinjamannn
quelle
GEFAHR: obwohl dies für einige offensichtlich sein könnte - das funktioniert nur , weil die func.datetut CAST auf der Säule , die aus der Gleichung der Zeit entfernt => das bedeutet NICHT mittleren Bereich mit der Zeit! Dies funktioniert nur, wenn sich die Zeit NICHT in der Spalte befindet - Sie müssen sie wie folgt auf Datum umwandeln oder die Spalte auf Datum setzen, sobald es sich um DateTime oder Zeitstempel handelt - sie wird normalerweise mit 00:00 beendet (sowohl MySQL als auch PostgreSQL tun dies). Eine allgemeinere Lösung besteht nicht darin, sie zu übertragen, sondern das Datum festzulegen, 1988-01-17 23:59:59an das Sie sie senden .endOfDay (), damit Sie sie tatsächlich an die Datenbank senden. Vergleich :)
jave.web