angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

SQLAlchemy datetime berekeningen aan de serverzijde

Waarom je zou moeten proberen om client-side datetime berekeningen te vermijden met SQLAlchemy tenzij....

24 juni 2019
post main image
Original photo unsplash.com/@nputra.

U vindt er veel SQLAlchemy datetime rekenvoorbeelden met behulp van bijvoorbeeld Python's timedelta functie. Waarom? Ik begrijp dit niet, behalve dat dit gemakkelijk is. Maar is het juist?

Stel we willen alle gebruikersrecords of objecten die twee uur geleden zijn aangemaakt en de record / object definitie is:

class User(Base):

    __tablename__ = 'user'

    id = Column(Integer, primary_key=True)
    created_on = Column(DateTime, server_default=func.now(), index=True)
    email = Column(String(100), server_default='', index=True)

Als we dan Python de laatste 10 minuten de toegevoegde records / objecten kunnen selecteren, kunnen we zoiets als dit doen:

from datetime import datetime, timedelta

now = datetime.now()
two_hours_ago = now - timedelta(hours=2)

# return all users created less then 2 hours ago
db.query(User).filter(User.created_on > two_hours_ago).all()

De gegenereerde SQL is:

SELECT user.id AS user_id, user.created_on AS user_created_on, user.email AS user_email
FROM user 
WHERE user.created_on > %(created_on_1)s
INFO sqlalchemy.engine.base.Engine {'created_on_1': datetime.datetime(2019, 6, 25, 7, 31, 58, 630959)}

Dit werkt alleen, geeft geldige resultaten, als:

  • de databaseserver draait op dezelfde server waar de Python code wordt uitgevoerd
  • de databaseserver draait op een andere server dan de Python codeserver en de tijd op beide servers is perfect gesynchroniseerd.

Stel je hebt een aparte database server en de tijd van deze server is 2 minuten uit-synchronisatie. Dan heb je verkeerde, onvolledige resultaten. Ik schrijf al vele jaren server -side queries en ben verbaasd dat er weinig aandacht is voor deze SQLAlchemy vragen en antwoorden.

De enige manier om de juiste resultaten te krijgen is door gebruik te maken van de datetime stempels van de database server records en deze toe te voegen datetime aan of af te trekken datetime van deze records. Met MariaDB / MySQL kunt u de intervalopgave gebruiken:

SELECT user.* FROM user WHERE created_on > (NOW() - INTERVAL 2 HOUR)

Helaas kon ik niet een oplossing vinden SQLAlchemy die geldig zou zijn voor alle databases. SQLAlchemy heeft het text() object, het geeft de waarde door aan de query. Met text(), de vraag wordt de SQLAlchemy vraag:

from sqlalchemy import text

two_hours_ago = text('NOW() - INTERVAL 2 HOURS')

# return all users created less then 2 hours ago
db.query(User).filter(User.created_on > two_hours_ago).all()

De gegenereerde SQL is:

SELECT user.id AS user_id, user.created_on AS user_created_on, user.email AS user_email
FROM user 
WHERE user.created_on > NOW() - INTERVAL 2 HOUR

Houd er rekening mee dat deze zoekopdracht mogelijk niet op alle databasesystemen werkt. Het werkt met MariaDB / MySQL maar het werkt zeker niet met SQLite.

Als u een applicatie ontwikkelt en alles op één computer draait, houd er dan altijd rekening mee dat u in de toekomst de database misschien op een aparte server wilt draaien. Het is dus geen slecht idee om uw vragen voor deze situatie te ontwikkelen.

Links / credits

Flask-sqlalchemy query datetime intervals
https://stackoverflow.com/questions/30495935/flask-sqlalchemy-query-datetime-intervals

SQLAlchemy datetime operations on server side
https://stackoverflow.com/questions/12540175/sqlalchemy-datetime-operations-on-server-side

SQLAlchemy default DateTime
https://stackoverflow.com/questions/13370317/sqlalchemy-default-datetime

Using DATEADD in sqlalchemy
https://stackoverflow.com/questions/15572292/using-dateadd-in-sqlalchemy/15573750#15573750

Lees meer

SQLAlchemy

Laat een reactie achter

Reageer anoniem of log in om commentaar te geven.

Opmerkingen (5)

Laat een antwoord achter

Antwoord anoniem of log in om te antwoorden.

avatar

Another great post. I have to agree it is surprising most solutions on the web ignore the potential Python app versus DBMS clock difference problem. The kind of bug that waits patiently until its time!
FWIW `sqlalchemy.sql.expression.func.now() - timedelta(minutes=2)` seems to work for Postgres and I would expect others with a NOW() function.

avatar
Anonieme bezoeker (niet ingelogd) 3 jaar geleden Anonieme bezoeker (niet ingelogd)

I just noticed the post above immediately displayed as posted 1 hour ago . . . I'm on GMT (London).

avatar
Anonieme bezoeker (niet ingelogd) 3 jaar geleden Anonieme bezoeker (niet ingelogd)

Me again! But after posting the second post, the first changed to "2 minutes ago", the second started at "0 seconds ago"!

avatar
Anonieme bezoeker (niet ingelogd) 3 jaar geleden Anonieme bezoeker (niet ingelogd)

Then after the third post all three now say "1 hour" . . . had to wait 5 minutes to post this one :-)

avatar
peter 3 jaar geleden Anonieme bezoeker (niet ingelogd)

Thank you for reporting this. I am still working on many parts of this website ... will fix this soon.