Datetime in SQL schreiben

Registriere dich jetzt, um exklusive Vorteile zu genießen! Als registriertes Mitglied kannst du Inhalte herunterladen und profitierst von einem werbefreien Forum.
Mach mit und werde Teil unserer Community!
  • Hallo,


    ich versuche die ganze Zeit datetime von Python3 in eine MariaDB zu schreiben. Funktioniert aber nicht.


    Es wird aber nichts in die Datenbank geschrieben. Der SQL Befehl oben ist nicht ganz vollständig. Es fehlen die restlichen Werte, welche in die DB geschrieben werden. Lasse ich das Feld datetime leer und setze es als CURRENT TIMESTAMP in SQL, werden alle Werte geschrieben. Nur wenn ich datetime explizit schreiben will von Python aus, kommt es zu einen Fehler, keine Werte werden geschrieben. Warum, spuckt Python nicht aus. Oder doch?


    Hat wer eine Lösung, wie man von Python aus erzwingen kann, datetime in SQL zu schreiben?

  • miwi1706 Vier Fehler Der Platzhalter in SQL ist "%s" und der hat nichts mit den %-Platzhaltern für den ``%``-Operator auf Zeichenketten zu tun. Und Du versuchst da gar kein `datetime.datetime` zu speichern sondern eine Zeichenkette. Dritter Fehler ist dann das Wörterbuch als `data`. Ausserdem fehlt in dem SQL der Tabellenname in den das eingefügt werden soll.


    Ausserdem wäre es immer hilfreich den Fehler auch zu zeigen statt nur zu sagen da käme es zu einem, denn Fehlermeldungen enthalten in der Regel ja auch Informationen was für ein Fehler das ist, wo der genau auftritt, und so weiter.

    „Eat the rich — the poor are full of preservatives.“ — Rebecca ”Becky” Connor, The Connors

  • Code
    # Bei Fehler
        except:
            error_task()
    
    # Fehlerbehandlung
    def error_task():
        # Programm beenden
        print("Programmfehler")
        sys.exit()

    Daher weiß ich, dass es einen Fehler gibt. Nur welchen bekomme ich nicht ausgegeben :(

    Lasse ich den ganzen Teil, welcher nur in die DB schreibt weg, kommt kein Programmfehler.


    Den Tabellen-namen habe ich bewusst weggelassen.


    Habe aber eine Lösung gerade gefunden, welche funktioniert.


    Code
    mycursor = connection.cursor()
    
            sql = "INSERT INTO test (dt, wert) VALUES (%s, %s)"
            val = (datetimestrg, sqlwert)
            mycursor.execute(sql, val)
    
            connection.commit()
    
            print(mycursor.rowcount, "record inserted.")

    Was meinst du mit datetime.datetime?

  • miwi1706 Die ”Fehlerbehandlung” ist ja auch keine sinnvolle Fehlerbehandlung. Wenn Du in einem ``except`` nichts *sinnvolles* anstellst, dann lass das ``except`` einfach weg. Das Programm wird dann auch abgebrochen, ohne das Du etwas dafür tun musst, und Du erfährt *was* passiert ist, und *wo* das passiert ist. Und der Rückgabecode ist dann auch nicht 0. `sys.exit()` sollte man nur aufrufen wenn da mindestens potentiell ein anderer Rückgabecode als 0 an den Aufrufer gemeldet wird. Ansonsten ist das in der Regel eine Art unsauberer Hack/Notausgang weil man sich nicht um einen sinnvollen Programmfluss kümmern wollte.


    Deine Lösung ist keine weil das nicht funktionieren muss. Das funktioniert zufällig, mit der Datenbank und dem Datenbank-Modul. Muss es aber nicht. Mit `datetime.datetime` meine ich den `datetime`-Datentyp aus dem `datetime`-Modul.


    Namen sollten keine kryptischen Abkürzungen enthalten oder nur daraus bestehen. `my` ist ein unsinniger Präfix. Wenn es nicht auch einen `our_cursor` oder `their_cursor` gibt, um das vom `my_cursor` zu unterscheiden, bringt dieser Namenszusatz überhaupt keinen Mehrwert für den Leser. `strg`? Das ist eine Taste auf deutschsprachigen Tastaturen. Und `val`? Zudem muss man auch nicht jedes kleine Zwischenergebnis überhaupt an einen Namen binden.


    „Eat the rich — the poor are full of preservatives.“ — Rebecca ”Becky” Connor, The Connors

  • contextlib

    Wozu das?


    Ich habe


    Python
    import mysql.connector

    Den Rest habe ich jetzt so:


    Python
    timestamp = DateTime.now().strftime("%Y-%m-%d %H:%M:00")
    
    sql = "INSERT INTO test (datetime, wert) VALUES (%s, %s)"
            val = (timestamp, sqlwert)
            connection.cursor().execute(sql, val)
            connection.commit()

    Es funktioniert soweit ohne Probleme.


    Zum Thema Error-Handling, habe ich mir keine Gedanken gemacht. Nur soviel...


    Python
    except:
            error_task()
    
    
    # Fehlerbehandlung
    def error_task():
        # Programm beenden
        print("Programmfehler")
        sys.exit()

    Muss mich dazu noch weiter belesen ...

  • Nochmals, ein nacktes except:, also ohne Fehlerangabe, "winkt alle Fehler durch". Das hilft Dir im Fehlerfall nicht weiter, weil Du nicht weißt was einen Fehler verursacht hat und wo Du danach suchen musst.


    Lass das weg und das Skript bricht im Fehlerfall mit einem sehr oft aussagekräftigen Traceback ab. Oder nutze das except gezielt, z.B. um den Traceback bei Abbruch durch STRG+c abzufangen, wie in diesem Fall:

    Code
    except KeyboardInterrupt:
        print("Benutzerabbruch")
  • miwi1706 Wenn Du keine Hilfe haben willst dann sag das doch einfach. Dieses „es funktioniert“ ist echt scheisse nervig. Nein, es funktioniert *zufällig*. Haste halt Glück gehabt. Ist halt trotzdem falsch.

    „Eat the rich — the poor are full of preservatives.“ — Rebecca ”Becky” Connor, The Connors

  • Die einfachste Lösung wäre es wahrscheinlich das Logging durch systemd erledigen zu lassen.

    Exceptions, die nicht abgefangen werden, brechen den Code an der Stelle ab und geben den Fehler nach stderr aus.

    systemd hat die Möglichkeit, die Ausgaben nach stdout und stderr in eine Datei umzuleiten.


    Wenn man unbedingt selber in eine Datei loggen will, geht das auch.

    Hier ein Beispiel:



    Ich habe auch gelesen, dass man einfach den Exception-Handler austauschen kann, damit log.exception immer aufgerufen wird.

    Vorsichtig, das funktioniert nicht mit Modulen, die ihren eigenen Exception-Handler nutzen. Wenn man z.B. die Interaktive Shell IPython verwendet, ist bereits ein eigener Exception-Handler durch IPython zugewiesen worden.


    Hier das zweite Beispiel:

  • Ufff............


    So funktioniert es. Jetzt bekomme ich auch Fehlermeldungen. Datenbank nicht erreichbar oder Werte, die nicht geschrieben werden können. Vielleicht ungünstig ausgedrückt im #1 Post, es gibt keinen Programmfehler, ich wusste nicht, warum die Werte in die SQL DB nicht geschrieben werden. Nun habe ich ein wenig herumgespielt im Programm (falsche SQL Verbindung, falscher DB Name, falscher Tabellen-Name und so) und bekomme die gewünschten Fehlermeldungen.


    Das strftime("%Y-%m-%d %H:%M:00") ist Absicht, da ich nur die Minutenwerte haben will, ohne Sekunden oder mit 00s. Hier habe ich keine andere Lösung gefunden.

  • Der Grund, warum du keine Fehlermeldung bekommen hast, ist, weil du die mit

    Python
    try:
        ...
    except:
        sys.exit()

    unterdrückt hast.

    Wenn du einen halbwegs vernünftigen Editor oder eine IDE benutzt (z.B. VSCode oder Pycharm), werden dir solche Sachen schon beim Schreiben angezeigt.


    Wenn du möchtest, dass dein Programm nach einer Exception weiter läuft, solltest du auf jeden Fall das logging-Modul benutzen, um die Exception trotzdem anzuzeigen:

    Python
    import logging
    import mysql.connector
    
    try:
        ...
    except mysql.connector.Error:
        logging.exception("MySQL error")

    logging.exception() gibt nämlich automatisch die Exception aus, die in dem try-except-Block geflogen ist.


    Wenn du das Programm beenden möchtest, wenn eine Exception fliegt, aber mit einer Nachricht wie in Zeile 35, kannst du dafür auch logging benutzen, aber anstelle von sys.exit() einfach die Exception nochmal werfen:


    Python
    import logging
    import mysql.connector
    
    try:
        ...
    except mysql.connector.Error as exc:
        logging.critical("MySQL error")
        raise exc

    Im Normalfall fängt man auch gezielt nur die Exception ab, die man auch erwartet. "Sonstige Fehler" abzufangen, so wie du das im Code auf den Zeilen 12 - 14 gemacht hast, ist immer eine schlechte Idee.



    Für zukünftige Fragen: Zeige am besten den ganzen Code, der mit dem Fehler zusammenhängt, am besten die ganze Datei und nicht nur einzelne Zeilen. Teilweise liegt das Problem an anderen Stellen, die nicht im Stacktrace genannt sind. Auch der komplette Stacktrace ist wichtig, und wenn man alle try-excepts raus nimmt, die nichts machen, bekommt man den in der Regel auch.

    Achte auch ein bisschen darauf, dass die Formatierung durch das kopieren nicht komplett zerstört wird.

  • miwi1706 Die Fehlerbehandlung ist immer noch falsch. Ohne Grund wird dort Information weggeworfen in dem aktiv der Fehler ”behandelt” wird in dem nur der Ausnahmetext ausgegeben wird, und dann so weiter gemacht wird als wäre überhaupt gar nichts falsch gelaufen. Das ist nicht sinnvoll. So gar nicht. Ich frage mich echt warum es einigen anscheinend so verdammt schwer fällt einfach mal *nichts* zu machen, wenn man nichts sinnvolles machen kann. Denn die sinnvollste Art, und auf jeden Fall deutlich sinnvoller als das was da jetzt gemacht wird, ist einfach gar nichts zu machen und sich Python um die Ausgabe kümmern zu lassen. Da steckt all die Information drin, die jetzt auch schon da ist *plus* einem Traceback an dem man sehen kann wo genau der Fehler auftritt und welchen Weg durch die Aufrufe genommen wird.


    Literale Zeichenketten sind keine Kommentare. An bestimmten Stellen haben die für Python und für Dokumentationswerkzeuge eine Bedeutung als Docstrings. Dafür sollte man sie dann auch verwenden. Nicht als Ersatz für normale ``#``-Kommentare. Docstrings für Funktionen stehen *nach* der ``def``-Anweisung als erstes im Block der den Funktionskörper bildet. Nicht vor der ``def``-Anweisung.


    Zeitstempel sind immer noch keine Zeichenketten. Und das nicht speichern wollen von Anteilen die unterhalb von Minuten liegen ist zumindest mal komisch. Man schmeisst beim aufzeichenen von Daten nicht ohne Not Informationen weg. Das ganze auf ein Minutenraster abbilden kann man später immer noch bei der Abfrage/Auswertung. Präzision die man einmal weg geworfen hat, bekommt man dagegen nie wieder zurück.


    Falls Du das trotzdem unbeding machen möchtest gibt es zwei Wege: a) `datetime`-Objekte haben eine Methode um Bestandteile durch andere Werte zu ersetzen, so auch 0. Oder b) man kombiniert sich ein neues `datetime`-Object aus dem Datum des alten und einem neuen `datetime.time`-Objekt das man aus Stunde und Minute vom alten `datetime`-Objekt erzeugt.


    Die Namen der beiden Funktionen sind nicht so gut. `rpi_connection()` überhaupt nicht, weil das keine Tätigkeit beschreibt. Das wäre ein guter Name für ein Objekt das eine Verbindung zu einem RPi repräsentiert, aber es beschreibt nicht den Vorgang der Verbindung. `save_rpi_db()` beschreibt zwar eine Tätigkeit, die klingt aber so als würde dort die Datenbank gespeichert und nicht einzelne Werte *in* die Datenbank. Das `rpi` ist in beiden Namen auch nicht so sinnvoll, denn nichts an den Funktionen erfordert wirklich, dass es ein RPi sein muss.


    Wenn man die Funktionen sinnvoll benennt, kann man sich auch die Docstrings in der jetzigen Form sparen weil die nur noch mal genau das gleiche sagen was schon im Funktionsnamen steht.


    Auch bei der Datenbankverbindung würde ``with`` mit `closing()` Sinn machen, statt sich darauf zu verlassen, dass am Ende schon irgendwie automatisch alles sauber geschlossen wird.


    Es macht in der Regel wenig bis gar keinen Sinn das Ergebnis eines Ausdrucks an einen Namen zu binden, der dann ausschliesslich in der nächsten Zeile dafür verwendet wird hinter einem ``return`` zu stehen.


    Zwischenstand (ungetestet):

    „Eat the rich — the poor are full of preservatives.“ — Rebecca ”Becky” Connor, The Connors