variable per UPDATE einfügen

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
peko
User
Beiträge: 19
Registriert: Montag 5. Juni 2017, 14:41

Hallo zusammen,
meine Versuche einen UPDATE-Befehl an eine Sqlite DB zu senden, scheitern kläglich.

Code: Alles auswählen

zeit = time.strftime("%d%m%Y-%X")
cursor.execute("UPDATE tabname SET aktwert = '1', turn = zeit WHERE spalte = 17")
conn.commit()
Solange ich nur "feste Werte" wie z.B. "1" nehme, funktioiert das prächtig, aber mit der Variablen zeit geht es überhaupt nicht.

Code: Alles auswählen

Fehlermeldung: sqlite3.OperationalError: no such column:  zeit
Wobei die zeit ja die Variable ist, die Spalte turn gibt es in der Sqlite Tabelle.

Was mache ich falsch?

Gruß
PeKo
Zuletzt geändert von Anonymous am Sonntag 23. Juli 2017, 21:56, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

So ähnlich vermutlich, wobei mir 'Zeit' noch nicht richtig formatiert erscheint.

Code: Alles auswählen

turn = '" + zeit + "'
So wie Du es versuchst einzufügen, wird Zeit wohl nur als String ausgewertet. Probiere einfach, bis es richtig passt.

Code: Alles auswählen

zeit = time.strftime("%d%m%Y-%X")
print("UPDATE tabname SET aktwert = '1', turn = '" + zeit + "' WHERE spalte = 17")

# Oder:

zeit = time.strftime("%d%m%Y-%X")
print("UPDATE tabname SET aktwert = '1', turn = '{0:1}' WHERE spalte = 17".format(zeit))
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du kanns nicht einfach einen Namen in dein SQL statement schreiben und hoffen, dass das durch ein Wert einer Variablen aus dem Kontext des Aufrufs ersetzt wird. Das wäre auch du ziemlich dramatisch, was würde passieren wenn du eine neue Variable anlegst, die zufällig gleich heißt wie eine Spalte?

Stattdesssen musst du den Wert an das execute übergeben, und dafür eigenen Platzhalter angeben im Statement. Leider hängt die konkrete Vorgehensweise von deiner datenbank ab, aber zB SQLite macht es so:

Code: Alles auswählen

cursor.execute("UPDATE Tabelle WHERE spalte = ?", (wert, ))
Der von melewo vorgeschlagene weg ist zu vermeiden, da er dich für SQL injection Angriffe anfällig macht, und außerdem spart man sich mit der von mir gezeigten parametrisierten Form eine Typwandlung - man kann einfach zB ints, floats, oder datetime Objekte übergeben.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Ja, ist richtig wie __deets__ es beschreibt mit ?, benutze ich im Web ebenfalls. Nur allgemein kommt mir die Formatierung von Zeit merkwürdig vor, würde eher dieses Schema erwarten, wenn es einem richtigen Datetime entsprechen soll:

'0000-00-00 00:00:00'

Kommt aber darauf an, wie Du Deine Datenbank eingerichtet hast.
peko
User
Beiträge: 19
Registriert: Montag 5. Juni 2017, 14:41

Danke erstmal für die schnellen Antworten !

@ __deets__
Wenn ich es so schreibe:

Code: Alles auswählen

cursor.execute("UPDATE tabname WHERE spalte = ?", (17,'1',zeit))
oder auch

Code: Alles auswählen

cursor.execute("UPDATE tabname WHERE spalte = 17", ('1',zeit))
kommt immer folgender Fehler:

Code: Alles auswählen

sqlite3.OperationalError: near "WHERE": syntax error
Klappt irgendwie nicht...

@ Melewo: es ist absichtlich kein richtiger Timestamp

Gruß
peko
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@peko: richtiges SQL wäre

Code: Alles auswählen

cursor.execute("UPDATE tabname SET aktwert = ?, turn = ? WHERE spalte = ?", ('1', zeit, 17))
Was ist der Sinn dahinter, absichtlich falsche Timestamps zu benutzen?
peko
User
Beiträge: 19
Registriert: Montag 5. Juni 2017, 14:41

Danke Sirius3 !
So funktioniert es.

Das Zeitformat ist für ein anderes PHP Programm, welche auf die Datenbank zugreift und dieses Format so weiterverarbeitet.
Da will ich nichts dran ändern.

Gruß
peko
nobby46
User
Beiträge: 5
Registriert: Sonntag 23. Juli 2017, 20:14

Hallo und guten Abend.

Ich mache meine ersten Gehversuche mit Python und SQL. Dabei habe ich ein Problem mit der Eingabe einer Variablen in eine DB.
Das erstellen CREATE TABLE und INSERT INTO habe ich hinbekommen. Nur komme ich nicht weiter mit UPDATE.

Hier ein Ausschnitt:

Code: Alles auswählen

 test = 2
 sql = """
 UPDATE temperaturen SET fuehler = test WHERE id = 1
 """
 conn.execute(sql)
Wenn ich anstelle der Variable test, einen Wert als Dezimalzahl z.B. 2 eingebe funktioniert es sauber.
Sonst bekomme ich die Meldung: OperationalError: no such column: test

Ich hoffe es kann mir jemand zeigen was ich falsch mache. Habe leider im Netz nichts gefunden. Hier geht man immer von Dezimalzahlen aus.

Norbert
Zuletzt geändert von Anonymous am Sonntag 23. Juli 2017, 22:05, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dein Fehler liegt darin zu glauben, das einfach nur einen Namen im SQL zu benutzen, der auch außerhalb bekannt ist, dazu führt, das dann daraus eine Variable wird. Das ist so nicht, und wäre auch fatal, wenn es so wäre.

In den Posts vorher in diesem Thread den du gemopst hast wird aber auch genau das schon erklärt. Bitte lesen.
nobby46
User
Beiträge: 5
Registriert: Sonntag 23. Juli 2017, 20:14

@ __deets__
danke für Deine Antwort, leider komme ich trotz mehrfachen lesen des Thread nicht weiter.
Ich habe versucht Deinen Tipp "cursor.execute("UPDATE Tabelle WHERE spalte = ?", (wert, ))"
bei mir zu übertragen, bekomme aber dann Fehlermeldung: near "WHERE": syntax error.

Mein angepasster UPDATE-Eintrag:

Code: Alles auswählen

cursor.execute("UPDATE temperaturen WHERE fuehler = ?", (Aussenf, )
Meine Tabelle habe ich so erstellt

Code: Alles auswählen

sql = """
CREATE TABLE temperaturen (
 id INTEGER PRIMARY KEY,
 name   TEXT,
 fuehler REAL
)"""
Was mache ich falsch?

Norbert
Zuletzt geändert von Anonymous am Montag 24. Juli 2017, 10:07, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist kein gültiges SQL. Und sieht anders aus als das, was du vorher gepostet hast. Warum?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nachtrag: es war schon urspruenglich mein Fehler, das SQL hat nicht gestimmt. Aber es ging auch darum die Uebergabe der Parameter zu illustrieren. Ich habe das ja nicht selbst laufen gelassen.

Sirius hat meinen Fehler dann auch korrigiert, es steht als - nach wie vor - alles hier. Und du wirst auch nicht besonders weit kommen, wenn du ohne nachzudenken einfach Dinge kopierst. Du musst die Muster und Vorgehensweisen schon auf das adaptieren, was *du* tust.
nobby46
User
Beiträge: 5
Registriert: Sonntag 23. Juli 2017, 20:14

@__deets__
mein erster Eintrag

Code: Alles auswählen

     test = 2
     sql = """
    UPDATE temperaturen SET fuehler = test WHERE id = 1
    """
     conn.execute(sql)
sollte zeigen wie es funktioniert wenn ich anstelle test eine Zahl z.B 25.3 eintrage.
Bei Eintragung test bekomme ich Fehlermeldung: sqlite3.OperationalError: no such column: test

Auf Anraten von Dir habe ich dann Deinen Vorschlag

Code: Alles auswählen

  cursor.execute("UPDATE Tabelle WHERE spalte = ?", (wert, ))
bei mir so

Code: Alles auswählen

  cursor.execute("UPDATE temperaturen WHERE fuehler = ?", (Aussenf, )
umgesetzt mit Fehlermeldung OperationalError: near "WHERE": syntax error

Um zu zeigen wie meine Tabelle aufgebaut ist,

Code: Alles auswählen

    sql = """
    CREATE TABLE temperaturen (
    id INTEGER PRIMARY KEY,
    name   TEXT,
    fuehler REAL
    )"""
habe ich diese mit angehängt

Norbert
Zuletzt geändert von Anonymous am Montag 24. Juli 2017, 13:12, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
nobby46
User
Beiträge: 5
Registriert: Sonntag 23. Juli 2017, 20:14

@--deets--
wo wir schon dabei sind, von mir auch ein Nachtrag. Deiner ist gekommen während ich geschrieben habe :)
Ich habe mich erst seit ein paar Tagen mit SQL beschäftigt und muss noch viel lernen. Deshalb habe ich noch nicht verstanden, wie ich den Eintrag von sirius3 bei mir umsetzen kann.
Werde weiter lernen :)

Norbert
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@nobby46: Vergleiche
[codebox=sql file=Unbenannt.sql]UPDATE temperaturen SET fuehler = test WHERE id = 1[/code]
mit
[codebox=sql file=Unbenannt.sql]UPDATE temperaturen WHERE fuehler = ?[/code]
und nenne 7 Unterschiede.
nobby46
User
Beiträge: 5
Registriert: Sonntag 23. Juli 2017, 20:14

@Irius3
danke für Deinen heißen Tipp. Ich hatte mir gerade schon Deinen Eintrag
cursor.execute("UPDATE tabname SET aktwert = ?, turn = ? WHERE spalte = ?", ('1', zeit, 17))
angesehen und auf meine Bedürfnisse angepasst.

Code: Alles auswählen

 cursor = conn.cursor()
 cursor.execute("UPDATE temperaturen SET fuehler = ? WHERE id = 1", (Aussenf, ))
 cursor.execute("UPDATE temperaturen SET fuehler = ? WHERE id = 2", (Innenf, ))
 conn.commit()
und alles funktioniert wie es. Hat mir sehr geholfen. Ich muss aber noch viel lernen was Datenbanken und SQL unter Python angeht. Vor vielen Jahren als ich noch Berufstätig war habe ich viel in Assembler und C programmiert, aber lang ist's her :)
Nochmal danke.

Norbert
Zuletzt geändert von Anonymous am Montag 24. Juli 2017, 13:13, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@nobby46: Welches PHP-Programm speichert denn Zeitangaben nicht als DATETIME oder TIMESTAMP, sondern als TEXT/CHAR/VARCHAR in so einem komischen Format? Datenbanken haben doch extra Datentypen für so etwas. Zum Beispiel um Messdaten in einem bestimmten Zeitraum abzufragen. Das funktioniert mit Zeiten als Zeichenketten entweder nicht richtig oder mindestens ineffizienter.

Tabellennamen würde ich in Einzahl benennen. Denn so ein CREATE beschreibt *einen* Datensatz, also woraus *eine* Temperatur besteht. Das ist äquivalent zu einer Klassendefinition, oder in C eine Struktur. Hier würde `temperaturen` für die Struktur ja auch irreführend sein:
[codebox=c file=Unbenannt.c]typedef struct {
int id;
char *name;
double wert;
} temperatur;[/code]

Ich habe da `wert` statt `fuehler` als Bezeichnung für die Temperatur genommen, denn ``temperatur.wert = 42.23;`` macht mehr Sinn als ``temperatur.fuehler = 42.23;``. Das ist bei SQL nicht anders.

Zur Namenschreibweise in Python gibt's den Style Guide for Python Code. Ausser Konstanten (ALLES_GROSS) und Klassen (MixedCase) schreibt man Namen klein_mit_unterstrichen. Also `aussenf` statt `Aussenf`. Wobei mir das `f` hier unklar ist (Fahrenheit? Bitte, bitte nicht `float`!), und `aussen` etwas zu unpräzise. `aussentemperatur` wäre passend wenn es die Aussentemperatur ist.

`connection` würde ich auch nicht abkürzen. Oder wenn dann vielleicht als `db` schreiben, denn das ist wenigstens eine gängige Abkürzung.

Und ich plädiere immer wieder gerne dafür SQLAlchemy als Abstraktionsschicht zu verwenden. Mindestens den Teil der vermeidet das man SQL von Hand schreiben muss, aber auch gerne das ORM.

Ohne ORM (ungetestet):

Code: Alles auswählen

    for id_, temperatur in [(1, aussentemperatur), (2, innentemperatur)]:
        connection.execute(
            temperature_table.update()
              .where(temperature_table.c.id=id_)
              .values(wert=temperatur)
        )
    connection.commit()
Mit ORM (ungetestet):

Code: Alles auswählen

    for id_, temperaturwert in [(1, aussentemperatur), (2, innentemperatur)]:
        (
            session.query(Temperatur)
              .filter_by(id=id_)
              .update({Temperatur.wert: temperaturwert})
        )
    session.commit()
Antworten