Vergleich von Daten (Datum)

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
NewPython19

Hallo zusammen,

ich habe eine Datenbank (SQLite) mit einer Tabelle und mehreren Datenfeldern.
Eines dieser Datenfelder heißt "Date" und beinhaltet wie der Name schon, ein Datum.

Mit Hilfe einer Funktion möchte ich bestimmte Datensätze löschen, wenn das Datum eine bestimmte Anzahl Tage zurückliegt.

Hier meine Funktion:

Code: Alles auswählen

def delete_data(connection, cursor):
    cursor.execute("SELECT * FROM Table")
    for dataset in cursor:
        datasetDate = dataset[0]
        today = datetime.datetime.now()
        timeDelta = datetime.timedelta(days=30)
        dateDifference = today - timeDelta
        deltaDate = dateDifference.strftime("%d.%m.%Y")
        if datasetDate < deltaDate:
            cursor.execute("DELETE FROM Table WHERE Date = (?)", (datasetDate,))
            connection.commit()
Beim Testen dieser Funktion werden irgendwie alle Datensätze gelöscht. Weiß jemand wo mein Fehler liegt? Ich hab schon ein bisschen rumgespielt aber irgendwie komme ich auf keine Lösung :shock:
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du musst nur EINMAL das Verfallsdatum berechnen. Also quasi „threshold = now() - timedelta()“.

Und dann die sql Datenbank die Arbeit machen lassen.

DELETE FROM Tabelle WHERE date < ?
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Wobei man im Hinterkopf haben sollte, dass sqlite keinen Datentyp für Datum und/oder Uhrzeit hat. Und wenn man tag.monat.Jahr in die Datenbank schreibt, lässt sich das ganz schlecht sortieren / vergleichen, weil der 15.01.2019 möglicherweise vor dem 22.09.2018 einsortiert wird.
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Also laut deinem link benutzen die iso-Format, und da ist dir lexikalische auch die logische Ordnung.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Das ist richtig.
Allerdings macht der TE Dinge mit .strftime() - und wenn er das so wie gezeigt auch tut, wenn er Daten in die Datenbank schreibt, hat er ein Problem.
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Die Frage ist halt wie der OP das in die DB geschrieben hat. Im Beispiel vewendet er ja tag.monat.jahr als Format.

Ich würde ja wie immer an dieser Stelle zu SQLAlchemy raten. Das konvertiert als DATE angegebene Spalten transparent von/nach `datetime.date` – auch bei SQLite.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
NewPython19

Ach Herrje! Stimmt, es reicht aus das Datum nur einmal berechnen zu lassen! Vielen Dank!

Code: Alles auswählen

def delete_data(connection, cursor):
    cursor.execute("SELECT * FROM Table")
    today = datetime.datetime.now()
    timeDelta = datetime.timedelta(days=5)
    dateDifference = today - timeDelta
    deltaDate = dateDifference.strftime("%d.%m.%Y")
    cursor.execute("DELETE FROM Table WHERE Date < (?)", (deltaDate,))
    connection.commit()
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Warum selektierst du erst Sachen, wenn du es hinterher nicht verwendest?
Ich bleibe dabei: Ich bin mir ziemlich sicher, dass der Vergleich mit einem Tag.Monat.Jahr String, den du hier durchführst, falsch ist.

Code: Alles auswählen

> sqlite3.exe
SQLite version 3.29.0 2019-07-10 17:32:03
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> CREATE TABLE test (id INTEGER PRIMARY KEY, datum DATE);
sqlite> INSERT INTO test (id, datum) VALUES (1, '2019-01-15');
sqlite> SELECT * FROM test;
1|2019-01-15
sqlite> DELETE FROM test WHERE datum < '30.06.1986';
sqlite> SELECT * FROM test;
sqlite> INSERT INTO test (id, datum) VALUES (2, '15.01.2019');
sqlite> SELECT * FROM test;
2|15.01.2019
sqlite> DELETE FROM test WHERE datum < '22.12.1968';
sqlite> SELECT * FROM test;
sqlite>
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@NewPython19: Namen werden in Python klein_mit_unterstrichen geschrieben – auch lokale Namen. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Beides kommt eher selten bis nie lokal vor.

Cursor sind eher etwas kurzlebiges, den würde ich nicht als Argument herum reichen solange sich die Aufrufe nicht auf die selbe Transaktion beziehen – was hier hoffentlich nicht der Fall ist, denn das wäre etwas was man *deutlich* dokumentieren sollte.

Man muss auch nicht wirklich *jedes* Zwischenergebnis von noch so kleinen Trippelschrittchen an einen eigenen Namen binden.

Ich würde in den Funktionsnamen eine Hinweis aufnehmen was da gelöscht wird.

Ungetestet:

Code: Alles auswählen

def delete_old_data(connection, days_back=5):
    timestamp = datetime.datetime.now() - datetime.timedelta(days=days_back)
    connection.cursor().execute(
        "DELETE FROM table WHERE date < ?", (timestamp.date(),)
    )
    connection.commit()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
NewPython19

Ich hab eure Kommentare und Rückmeldungen noch mit umgesetzt und letzte Fehler ausgebessert:

Code: Alles auswählen

def delete_data(connection):
    today = datetime.datetime.now()
    time_delta = datetime.timedelta(days=30)
    date_difference = today - time_delta
    connection.cursor().execute("DELETE FROM Table WHERE Date = (?)", (date_difference.date(),))
    connection.commit()
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

sqlite3.PARCE_DECLTYPES und sqlite3.PARSE-COLNAMES machen einem das Leben sehr viel leichter. Dann sorgt das sqlite3-Modul unter anderem dafür, dass datetime.date-Objekte korrekt geparst in der Datenbank landen und nach dem Select auch wieder datetime.date-Objekte sind.
Es muss nicht zwangsläufig gleich SQLAlchemy sein.

Code: Alles auswählen

import sqlite3
from datetime import date, timedelta


con = sqlite3.connect(":memory:",
                detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)


con.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, datum DATE)")

today = date.today()
con.execute("INSERT INTO test (id, datum) VALUES (?, ?)", (1, today))
for entry in con.execute("SELECT id, datum FROM test"):
    print(type(entry[0]), type(entry[1]))
    # <class 'int'> <class 'datetime.date'>
tomorrow = today + timedelta(1)
con.execute("DELETE FROM test WHERE datum < ?", (tomorrow,))
NewPython19

Guten Morgen,

mir ist nochmal ein Fehler aufgefallen glaube ich...
Ich habe in meiner Tabelle das Datum, bestehend aus Datum und Uhrzeit in zwei Datenfelder aufgeteilt...
Damit ist doch mein Datum in meinem Datenfeld Date ein String und kein Datumsobjekt mehr oder? Kann es sein, dass daher meine Datumsberechnung nicht 100% funktioniert?
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Wie ich bereits schrieb: in Sqlite gibt es kein Datumsfeld. Das ist immer ein String.

Wenn du Datum und Zeit trennst, wird es mit Sortierungen, etc, wieder schwierig.

Mach es wie in meinem letzten Beispiel gezeigt. Gib der Spalte den Typ datetime und nimm direkt datetime-Objekt für alles. Das sqlite3-Modul kümmert sich um den Rest.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Für das Speichern von Datumsangaben bietet sich isoformat() an, sofern das Datenformat keinen eigenen Typen dafür hat. Dann muss man sich keine großen Gedanken mehr um die Formatierung machen. Dieses Format ist geeignet zum Sortieren und wird auch von anderen Programmen leicht verstanden, wenn sie ein Datum verarbeiten können. Seit Python 3.7 gibt es mit fromisoformat() auch die passende Umkehrfunktion dazu.
Antworten