Mysql sich ändernde Daten auslesen

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Nonickatall
User
Beiträge: 19
Registriert: Dienstag 31. Januar 2017, 21:33

Hallo,

ich steh gerade mächtig auf dem Schlauch.

Ich habe ein Script in Python2 welches in einer MySQL Datenbank nach neuen Datensätzen schauen soll.

Code: Alles auswählen

dbcursor = con.cursor()
bdcursor.execute(Ein SQL-Query)
     for datensatz in dbcursor
Wenn ich das als Loop laufen lasse und dann habe ich nach dem Scriptstart genau einmal aktuelle Daten. Kommen im Hintergrund neue Datensätze dazu, merkt das mein Cursor Objekt nicht.Was Datenbankabfragen in dynamischen Datenbeständen irgendwie total sinnlos macht.. :ugeek:

Code: Alles auswählen

dbcursor.close()
Bringt keinen Unterschied. Gibt es irgendeine Weg um einfach nur den aktuellen Datenstand abzufragen? Vielleicht ein Objekt mit einem Return erzeugen und das Objekt dann killen?

Ich habe mich heute Abend tot gegoogelt... :?

Wäre schön wenn jemand ein Lösung kennen würde.. :wink:

LG
Ralf
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

Python 2 ist tot. Der Support-Zeitraum ist abgelaufen. Steig auf Python 3 um.

Bitte zeig den Code, so wie du ihn ausführst. Inklusive "Loop" und keinen falsch abgetippten Pseudocode. Der Teufel ist ein Eichhörnchen und der Fehler steckt immer im Detail.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich vermute mal hier liegt's an ACID - dein Cursor sieht einen "eingefrorenen" Stand der Datenbank. Das ist ein Feature. Wenn du den immer wieder neu erzeugst (was im Gegensatz zu einer immer wieder neu erzeugten Datenbankverbindung auch voellig ok weil wenig resourcen-intensiv ist), sollte sich das aendern.
Nonickatall
User
Beiträge: 19
Registriert: Dienstag 31. Januar 2017, 21:33

Ich habe gestern lange gegoogelt und das scheint ein Standard Problem eines Cursors unter MySQL und Python zu sein. Ich kann mir nicht vorstellen, dass sich das mit Python 3 geändert hat, das ist auch nicht ein Problem meines tatsächlichen Codes. Denn der funkioniert. Das Problem ist ein Problem des MySQL Cursors denn.

Von der Logik würde ich auch sagen, dass wenn man den Cursor schließt und neu generiert, dass der eigentlich neu lesen müsste. Tut der aber nicht. Und dadurch bekomme ich neue Datensätze in der Datenbank nicht mit. Was ziemlich blöd ist.

Ich hab schon mal überlegt einfach ein Objekt einer Klasse zu generieren, die die Verbindung zur Datenbank erstellt, den Cursor generiert und als Rückgabewert die Datensätze liefert. Und dass ich das Objekt danach einfach kille. Wäre das ein Weg?

Oder kennt jemand eine grundsätzlich andere Art des Zugriffs auf eine MySQL Datenbank?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die Bemerkung zu Python 2 vs 3 hat zwar nichts mit deiner Beobachtung, trotzdem setzt du bei einem aktiv entwickelten Projekt auf ein totes Pferd. Warum? Die Arbeit das zu korrigieren, wenn zb deine Distribution das nicht mehr mitbringt, wird später nur schwerer.

Zu deinem Problem: kann es sein, dass du daten schreibst, ohne sie zu committen? Denn dann sind die bewusst nicht sichtbar.
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

@Nonickatall: Doch, offensichtlich liegt das Problem in deinem Code. Denn wenn du einen Select machst, hat der natürlich die Daten, die aktulll waren, als die Datenbank die Transaktion geöffnet hat. Wie gesagt, zeig, was du wirklich mchst und nicht Psudo-Code mit Tippfehlern. Es ist nämlich durchaus relevant "wo" du den Loop setzt. Und das geht aus deinem Beitrag gar nicht hervor.
Nonickatall
User
Beiträge: 19
Registriert: Dienstag 31. Januar 2017, 21:33

Hallo,

erstmal danke das ihr versucht zu helfen.

Python 2 Code ist das, weil das alter Code ist, der schon lange existiert und ich das gerade umbaue. Da dieses Script als Teil einer größeren Java Anwendung, nichts weiter macht als SMS zu empfangen und in eine Tabelle des SQL-Servers zu schreiben, bzw. in einer anderen Tabelle zu schauen, ob da eine zu versendende SMS steht und diese verschickt, sah ich da jetzt keine größere Notwendigkeit, das von Python 2 auf Python 3 umzustellen.

Im Prinzip passiert folgendes: Eine Java Anwendung will eine SMS senden. Dazu speichert sie einen Datensatz in der Tabelle sms_send auf dem MySQL Server.

Dieses Script soll einfach schauen, ob in der Tabelle was steht und verschickt dann die SMS und löscht den Datensatz. Es kann sein, das da auch zwei SMS stehen, deswegen "min id", weil die nach der Reihenfolge des Entstehens "first in, first out" verschickt werden sollen. Starte ich das Script, verbindet sich der SQL Server, und wenn da ein Datensatz steht, wird die Funktion SMSsenden() aufgerufen, verschickt die SMS und löscht den Datensatz.

Das funktioniert alles so wie es soll.

Schiebe ich dann aber, bei laufendem Script, einen neuen Datensatz in die Tabelle, wird dieser erst nach Neustart, also erneutem Verbinden zur Datenbank erkannt. Ich suche also eine Lösung den Cursor upzudaten oder einfach eine andere Art des Zugriffes auf den Server. Hauptsache das funktioniert.

Hier ist der Original Code von dem Loop. Der ist natürlich etwas unaufgeräumt, weil ich da momentan rum teste. Zum Beispiel die Print Befehle, der fetchall und der Durchlauf stehen da nur zum testen.

Die Funktion SMSsenden und der Rest funktioniert wie gesagt.

Ich habe es auch mit einem con.commit() probiert. Das bringt keine Änderung.

Code: Alles auswählen

#Loop
while 1:

#Pruefen auf SMS Eingang

SMS_holen(1)

#SMS senden
 
 db1cursor = con.cursor()
 db1cursor.execute("select * from sms_send where id = (select min(id) from sms_send)")
 #zeile = db1cursor.fetchall()
 Durchlauf = Durchlauf +1
 print Durchlauf 
 try:
     for datensatz in db1cursor:
       sms_id  = str(datensatz[3])
       sms_status  = str(datensatz[4])
       sms_nummer  = datensatz[2]
       sms_text  = unicode(datensatz[0])
       sms_datum  = datensatz[1]

       print(sms_id)
       print(sms_status)
       print(sms_nummer)
       print(sms_text)
       print(sms_datum)
       SMSsenden(sms_id)
       db1cursor.close()
 except:
       db1cursor.close()
 time.sleep(2)
Zuletzt geändert von Nonickatall am Samstag 17. April 2021, 17:29, insgesamt 1-mal geändert.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das rohe try-except ist schonmal ein Fehler. Denn das schluckt dir einfach jedes Problem weg, egal ob Anwendungsfehler oder Programmierfehler. Schmeiss das raus, und mach eine *gezielte* Fehlerbehandlung. Und dann klaert sich vielleicht auch das Mysterium, warum da nix anzukommen scheint. Denn das siehst du jetzt gar nicht.

Und commits helfen auch nur auf der schreibenden Seite.
Nonickatall
User
Beiträge: 19
Registriert: Dienstag 31. Januar 2017, 21:33

Wenn ich den try entferne geht das auch nicht, schon probiert.

Da kommt auch kein Fehler, der Cursor wird einfach nicht aktualisiert..

Das mit den commit weiß ich, das war die Antwort auf deine Nachricht..
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der try ist trotzdem falsch, weil es dir das Leben schwer macht und Fehler verschleiert.

Und dein select sieht auch komisch aus. Das statement holt doch immer nur eine einzige SMS ab. Mit der *kleinsten* ID, was eher ungewoehnlich waere. Weil die ja von klein nach gross zaehlen. Oder generiert die Java-Anwendung kleiner werdende IDs? Und das Programm ist auch nicht korrekt, wenn mal mehrere SMS erzeugt wurden, denn du liest so ja immer nur das jeweils aelteste/neueste Datum. Oder schreibt da noch wer anderes (ausser der Java-Anwendung) auf der Tabelle rum, so dass da immer nur genau ein Eintrag drin ist? Und wie wird das synchronisiert?
Nonickatall
User
Beiträge: 19
Registriert: Dienstag 31. Januar 2017, 21:33

Nochmal, das try habe ich schon entfernt, das kommen keine Fehler..

Und wie oben beschrieben holt das select selbstverständlich nur einen Datensatz und zwar den mit der kleinsten id, weil die älteste SMS verschickt werden soll.

Sollten da mehrere SMS stehen, kommt danach die nächste id und wird versendet, bis da kein Datensatz mehr ist.

Und nochmal...

Das funktioniert auch alles..

Kommt aber im Hintergrund ein neuer Datensatz dazu, wird der Cursor nicht aktualisiert und damit wird bis zum nächsten Scriptstart nix mehr versendet..

Der Cursor ist mein Problem, nicht das select und auch nicht das try...
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

›while 1‹ sollte ein ›while True‹ sein. Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht mal 1 oder 2 oder 4 Leerzeichen.
Variablennamen und Funktionen schreibt man komplett klein.
Du holst SMS machst aber gar nichts damit? Was soll die 1?
Was soll die 1 bei db1cursor und warum holst Du Dir den Cursor von einer con(sole)?
Warum holst Du Dir nur einen Eintrag aus der Tabelle (und das so umständlich), benutzt dann aber eine for-Schleife? Das ist verwirrend.
Benutze keine *-Select sondern gib Spaltennamen an, so kann es keinen Fehler mit Indizes geben.
Das Schließen des Cursors innerhalb der for-Schleife ist definitiv falsch.
Wenn Du nur die SMS-ID brauchst, warum holst Du dann noch so viel mehr Informationen. Und was fängt eine SMS-Senden-Funktion nur mit der ID an?
Die Datentypen sollten eigentlich schon in der Datenbank richtig abliegen, das Umwandeln also unnötig sein.
Wenn Du Python2 benutzt, dann sind die Klammern bei print Unsinn.
Das ganze sieht also so aus:

Code: Alles auswählen

while True:
    #Pruefen auf SMS Eingang
    SMS_holen(1)

    #SMS senden
    with contextlib.closing(database.cursor()) as cursor:
        cursor.execute("select id, status, nummer, text, datum from sms_send order by id desc limit 1")
        sms_id, status, nummer, text, datum = cursor.fetchone()
        print(sms_id, status, nummer, text, datum)
        SMSsenden(sms_id)
    time.sleep(2)
Und von Löschen eines Datensatzes sehe ich hier nichts.
In dem Teil des Codes, den Du hier zeigst, ist kein Fehler.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da du das Wort nochmal gerne magst:

Nochmal: zeig den WIRKLICHEN Code, nicht irgendetwas, das offensichtlich falsch ist, und irgendwelches Gerede davon, dass ja eigentlich alles tut. Denn offensichtlich tut es das nicht.

Ein permanentes Abfragen der kleinsten ID einer Tabelle fuehrt logischerweise immer nur zum gleichen Ergebnis, solange niemand gleichzeitig aus dieser Tabelle etwas entfernt. Da du aber nichts zeigst, dass etwas entfernt, kann man eben auch nur genau das anmerken. Nochmal und nochmal und nochmal.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das kleine Beispielprogramm hier zeigt, wo deine *beiden* Probleme liegen:

Code: Alles auswählen

import MySQLdb
import time
import sys

SQL = dict(
    min="select id from sms where id = (select min(id) from sms)",
    max="select id from sms where id = (select max(id) from sms)",
    )

connection = MySQLdb.connect(
    host="127.0.0.1",
    user="root",
    password="password",
    db="test"
)

# with connection.cursor() as cursor:
#     cursor.execute("CREATE DATABASE test")
#     cursor.execute("use test")
#     cursor.execute("CREATE TABLE sms (id INT AUTO_INCREMENT PRIMARY KEY)")


if sys.argv[1] == "insert":
    with connection.cursor() as cursor:
        for i in range(100):
            cursor.execute("INSERT INTO sms VALUES ()")
        connection.commit()
    with connection.cursor() as cursor:
        cursor.execute("SELECT COUNT(*) FROM sms")
        print(cursor.fetchone()[0])
else:
    sql = SQL[sys.argv[1]]
    while True:
        with connection.cursor() as cursor:
            cursor.execute(sql)
            print(cursor.fetchall())
            connection.commit()
        time.sleep(1)
min bringt ohne loeschen nix. Von dem weder die Rede noch etwas zu sehen war. Nochmal.

Und wie ich schon vermutet habe: die ACID-Properties sind das Problem, und ein commit fehlt. Aber das war ja auch schon drin & hat nichts gebracht...
Nonickatall
User
Beiträge: 19
Registriert: Dienstag 31. Januar 2017, 21:33

__deets__ hat geschrieben: Samstag 17. April 2021, 18:07 Da du das Wort nochmal gerne magst:

Nochmal: zeig den WIRKLICHEN Code, nicht irgendetwas, das offensichtlich falsch ist, und irgendwelches Gerede davon, dass ja eigentlich alles tut. Denn offensichtlich tut es das nicht.
Das Wort nochmal, mag ich sehr gerne. Das sagt meine vierjährigen Tochter immer zu mir wenn ich mit ihr tobe...

Papa!! Nochmal...

Aber ich schweife ab... :D

Warum soll ich meine WIRKLICHEN Code zeigen? Nicht das der geheim wäre, aber erstens sind die Funktionen für den SMS Versand ziemlich umfangreich und außerdem liegt da definitiv nicht der Fehler....
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Und in dem Code, den Du zeigst, ist auch kein Fehler, weil das wichtige davon fehlt.
Wenn Du also WIRKLICHE Hilfe willst, ...
Nonickatall
User
Beiträge: 19
Registriert: Dienstag 31. Januar 2017, 21:33

Sirius3 hat geschrieben: Samstag 17. April 2021, 17:50 ›while 1‹ sollte ein ›while True‹ sein. Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht mal 1 oder 2 oder 4 Leerzeichen.
1 ist genauso True wie True und wie viele Leerzeichen ist doch wurscht, Hauptsache sie sind gleich eingerückt... Das tut der Funktion des Codes doch keine Abbruch. :wink:
Sirius3 hat geschrieben: Samstag 17. April 2021, 17:50 Variablennamen und Funktionen schreibt man komplett klein.
Ok, mag sein, aber auch das führt nicht zu diesem "Fehler"..
Sirius3 hat geschrieben: Samstag 17. April 2021, 17:50 Du holst SMS machst aber gar nichts damit? Was soll die 1?
Ich hole die SMS und schreibe sie zur weiteren Verwendung in die MySQL Datenbank. Das macht die Funktion SMS_holen() Das funktioniert fehlerfrei. Die 1 ist ein Parameter, der ist historisch und eigentlich nicht mehr nötig.
Sirius3 hat geschrieben: Samstag 17. April 2021, 17:50 Was soll die 1 bei db1cursor und warum holst Du Dir den Cursor von einer con(sole)?
Der Code dafür ist:

Code: Alles auswählen

con = mysql.connector.connect(host = "192.168.0.1"... usw..
Ich habe an anderer Stelle auch Cursor und die hießen immer dbcursor. Für Testzwecke habe ich den dann einfach mal db1cursor genannt, nur damit er einen eigenen Namen hat.
Sirius3 hat geschrieben: Samstag 17. April 2021, 17:50 Warum holst Du Dir nur einen Eintrag aus der Tabelle (und das so umständlich), benutzt dann aber eine for-Schleife? Das ist verwirrend.
Ja, stimmt, das war historisch und ich hatte es noch nicht entfernt.
Sirius3 hat geschrieben: Samstag 17. April 2021, 17:50 Benutze keine *-Select sondern gib Spaltennamen an, so kann es keinen Fehler mit Indizes geben.
Das Schließen des Cursors innerhalb der for-Schleife ist definitiv falsch.
Ok
Sirius3 hat geschrieben: Samstag 17. April 2021, 17:50 Wenn Du nur die SMS-ID brauchst, warum holst Du dann noch so viel mehr Informationen. Und was fängt eine SMS-Senden-Funktion nur mit der ID an?
Ich hatte die Informationen während des Testens da mal rein geschrieben, um zu schauen, wo, was ankommt. Aber eigentlich gebe ich an die Funktion nur die id weiter, über die die Funktion sich selbst alle Daten aus der SQL Tabelle holt. Ich könnte die natürlich auch als globale Variablem weitergeben, zumal es ja einfach nur Nummer und Text ist.
Sirius3 hat geschrieben: Samstag 17. April 2021, 17:50 Wenn Du Python2 benutzt, dann sind die Klammern bei print Unsinn.
Das ganze sieht also so aus:

Und von Löschen eines Datensatzes sehe ich hier nichts.
In dem Teil des Codes, den Du hier zeigst, ist kein Fehler.
Ich habe versucht auf die Schnelle mal auf Python 3 umzustellen, aber das geht nicht. Ich verwende Gammu und da ist die Python 3 Bibliothek nicht kompatibel. Deswegen funktioniert dein Code auch nicht, denn in Python 2 gibt es kein contextlib und Gammu funktioniert, zumindest nicht spontan auf Python 3.

Da da aber eine komplette Haussteuerung läuft, will ich da jetzt nix spontan uminstallieren und dann die nächsten Tage damit verbringen, alles wieder ans Laufen zu bringen. :wink:

Ich setze jetzt erst mal einen weiteren Rechner auf und werde das dann mal mit deinem Code und Python 3 versuchen. Vielleicht komme ich dann weiter..

Vielen Dank erstmal bis hierhin..

Ich melde mich die Tage, wenn ich soweit bin oder ich es ans Laufen bekommen habe wieder.

Bis dahin an alle liebe Grüße und bleibt gesund.

Ralf
Nonickatall
User
Beiträge: 19
Registriert: Dienstag 31. Januar 2017, 21:33

So, nachdem mich das nicht los gelassen hat, hier die Lösung:

Der Aufruf der Datenbank selbst muss in der Schleife stehen, dann geht es...
Trotzdem danke an alle...

Code: Alles auswählen

# -*- coding: utf8' -*-
import time
import mysql.connector

Startzeit = time.strftime("%d.%m.%Y %H:%M:%S")

print ("Start des SMS Tools am :" + time.strftime("%d.%m.%Y %H:%M:%S"))

#Loop
while True:

#SMS senden
    con = mysql.connector.connect(host = "192.168.0.1",
        user = "user",
        passwd = "irgendeinpasswort",
        db = "meine Datenbank")
    
    dbcursor = con.cursor()
    dbcursor.execute("select id, status, nummer, text, datum from sms_send order by id asc limit 1")
    try:
        sms_id, status, nummer, text, datum =dbcursor.fetchone()
        print (sms_id)
        print (nummer)
        print (text)
        
    except:
        print ("kein Datensatz")

    time.sleep(2)
   
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Seufz. Oder ein commit. Wie ich ja gezeigt habe. Denn die Transaktion isoliert dich von Änderungen. Siehe https://dev.mysql.com/doc/refman/8.0/en ... evels.html

Alternativ das isolation level ändern.
Nonickatall
User
Beiträge: 19
Registriert: Dienstag 31. Januar 2017, 21:33

__deets__ hat geschrieben: Samstag 17. April 2021, 22:48 Seufz. Oder ein commit. Wie ich ja gezeigt habe. Denn die Transaktion isoliert dich von Änderungen. Siehe https://dev.mysql.com/doc/refman/8.0/en ... evels.html

Alternativ das isolation level ändern.
Ich verstehe nicht, was du mir sagen willst. Ein Commit beschränkt sich doch auf das Schreiben in einer Datenbank, nicht aber auf das Lesen. Außerdem habe ich weiter oben doch beschrieben, dass ich das dennoch versucht habe. Ohne Erfolg..

Und was mir ein "isolation level" bringen soll, erschließt sich mir nicht. Ich habe keine Massendaten und verschiedenste Clients, die auf die gleichen Daten schreibend zugreifen und ich muss nicht für Daten Konsistenz sorgen..

Ich wollt einfach nur einen neu dazu gekommen Datensatz sehn.. :roll:
Antworten