MySQL einzelne Daten ändern

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
Benutzeravatar
Mathmos
User
Beiträge: 36
Registriert: Dienstag 10. August 2010, 21:31

Hi

Ich habe ein Problem beim Updaten einer Datenbank

Hiermit ermittle ich die Zeile die ich brauche

Code: Alles auswählen

topicID_auslesen = "SELECT * FROM webs_forum_topics WHERE topicID = %s" %(topicID)
Diese Zeile hat aber einige Spalten
(topicID, boardID, icon, intern, userID, date, topic, lastdate, lastposter, replys, views, closed, moveID, sticky)
Nun möchte ich die Einträge in "lastdate, lastposter, replys" ändern, wie muss ich da vorgehen?
Ich habe leider keine gescheites Beispiel finden können.
deets

Zum ersten, bitte verwende *nicht* den Code wie du ihn zeigst. Er ist hoch anfaellig fuer ein Sicherheitsleck das "SQL-Injection" heisst.

Um das zu umgehen sollte man unter *allen* Umstaenden die parametrisierte Variante von cursor.execute verwenden:

Code: Alles auswählen

cursor.execute("SELECT * FROM webs_forum_topics WHERE topicID = ?", (topicID))
Wie du siehst, musst du noch nicht mal viel aendern.

Und nun zu der eigentlichen Frage:

Code: Alles auswählen

cursor.execute("UPDATE webs_forum_topics SET lastdate=?, lastposter=?, replys=?  WHERE topicID = ?", (ldate, lposter, replys, topicID))
Das ist aussem Kopp, aber sollte hinkommen.
Benutzeravatar
Mathmos
User
Beiträge: 36
Registriert: Dienstag 10. August 2010, 21:31

Code: Alles auswählen

verbindung = MySQLdb.connect(host='***',port=***,user='***',passwd='***',db='***')
cursor3 = verbindung.cursor()
cursor3.execute("SELECT * FROM webs_forum_topics WHERE topicID = %s" %(topicID))
ergebnis3 = cursor3.fetchone()
ldate = time.time()
lposter = int_ergebnis_user
replys = ergebnis3[0][9] + 1
cursor3.execute("UPDATE webs_forum_topics SET lastdate=?, lastposter=?, replys=?  WHERE topicID = ?", (ldate, lposter, replys, topicID))
so, habe ich es jetzt. aber leider haut das nicht nicht so ganz hin
deets

Das ist schade, aber was noch weniger hinhaut ist dir zu helfen, wenn du nicht preisgibst *was* genau nicht hinhaut.

Und den SQL-Injection-Fehler hast du immer noch drin.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Mathmos hat geschrieben:

Code: Alles auswählen

replys = ergebnis3[0][9] + 1
Was soll dieser Code eigentlich bewirken?

Ich habe den Verdacht, dass du eigentlich einfach replys (was besser korrekt replies hieße) um 1 erhöhen möchtest. Dafür müsstest du allerdings nicht einmal ein SELECT durchführen.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Vermeide "SELECT *" Abfragen aus Datenbanken.
Je nach Datenbank ist nämlich nicht definiert in welcher Reihenfolge die Felder in dem Tupel der Abfrage geordnet sind.

Ein Zugriff mit result[int] ist ab allem mit mehr als einem Feld Abfrage böse Magie und absolut nicht wartbar.


Ansonsten hat /me recht: Das Select-Statement ist überflüssig.
Benutzeravatar
Mathmos
User
Beiträge: 36
Registriert: Dienstag 10. August 2010, 21:31

deets hat geschrieben:Das ist schade, aber was noch weniger hinhaut ist dir zu helfen, wenn du nicht preisgibst *was* genau nicht hinhaut.

Und den SQL-Injection-Fehler hast du immer noch drin.
Ich habe jetzt bei jeder Zeile ein try und except hinzugefügt. Und habe festgestellt, dass schon diese zeile nicht mehr hinhaut.

Code: Alles auswählen

cursor3.execute("SELECT * FROM webs_forum_topics WHERE topicID = ?", (topicID))
/me hat geschrieben:
Mathmos hat geschrieben:

Code: Alles auswählen

replys = ergebnis3[0][9] + 1
Was soll dieser Code eigentlich bewirken?

Ich habe den Verdacht, dass du eigentlich einfach replys (was besser korrekt replies hieße) um 1 erhöhen möchtest. Dafür müsstest du allerdings nicht einmal ein SELECT durchführen.
Ja, das möchte ich. Aber dazu möchte ich auch, wie oben genannt, noch 2 weitere Einträge ändern.
Habe jetzt diese Zeile mal ausprobiert. (ebenfalls mit try und except) Funktionoert leider auch nicht.

Code: Alles auswählen

cursor3.execute('update webs_forum_topics set replys=replys+? where topicID=?', (1, 'topicID'))
sparrow hat geschrieben:Vermeide "SELECT *" Abfragen aus Datenbanken.
Je nach Datenbank ist nämlich nicht definiert in welcher Reihenfolge die Felder in dem Tupel der Abfrage geordnet sind.

Ein Zugriff mit result[int] ist ab allem mit mehr als einem Feld Abfrage böse Magie und absolut nicht wartbar.


Ansonsten hat /me recht: Das Select-Statement ist überflüssig.
Ich habe das bisher immer so gemacht. Und bei der Datenbank, die ich verwende gab es auch noch nie Probleme damit =)


EDIT:
Ich bin eben auf `except Exception, e:` gestoßen und habe das gleich mal ausprobiert.
Die Fehlermeldung die ich bekommen lautet:
"not all arguments converted during string formatting"
Allerdings kann ich damit irgendwie nicht so recht was anfangen
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Mathmos hat geschrieben:Ich habe das bisher immer so gemacht. Und bei der Datenbank, die ich verwende gab es auch noch nie Probleme damit =)
Komm, wir gehen jetzt mal das Problem richtig an.
Was willst du eigentlich machen? Wie gesagt: das SELECT-Statement ist mit 95% Wahrscheinlichkeit überflüssig.

Code: Alles auswählen

import sqlite3

with sqlite3.connect(':memory:') as con:

    cur = con.cursor()

    cur.execute("CREATE TABLE webs_forum_topics " \
                "(replys INTEGER, topicID INTEGER)")
    cur.execute("INSERT INTO webs_forum_topics (replys, topicID) " \
                "VALUES (?, ?)", (5, 1))
    cur.execute("INSERT INTO webs_forum_topics (replys, topicID) " \
                "VALUES (?, ?)", (8, 2))
    cur.execute("INSERT INTO webs_forum_topics (replys, topicID) " \
                "VALUES (?, ?)", (11, 3))    

    print cur.execute("SELECT * FROM webs_forum_topics").fetchall()

    cur.execute("UPDATE webs_forum_topics SET replys = replys + 1 " \
                "WHERE topicID = ?", (2,))

    print cur.execute("SELECT * FROM webs_forum_topics").fetchall()

    cur.close()
BlackJack

@Mathmos: Schau Dir doch die Ausnahmen mal an, die sagen ja nicht nur "irgendwas ist falsch", sondern haben in der Regel auch einen Typ und eine Nachricht, die einem verraten sollen was zu der Ausnahme geführt hat. Und es gibt auch einen Traceback der die betroffenen Programmzeilen auflistet -- man muss also nicht ein ``try``/``except`` um jede einzelne Zeile setzen um die problematische zu finden. Eigentlich sollte man gar kein ``try``/``except`` dafür setzen müssen und die Ausnahme einfach bis auf die oberste Ebene "wandern" lassen -- dann wird sie ja automatisch ausgegeben.

Das zweite Argument von `Cursor.execute()` sollte ein Tupel mit den Werten sein. Wenn es nur ein Wert ist, braucht man trotzdem ein Tupel mit eben diesem einen Wert darin. Ausserdem ist der Platzhalter für `MySQLdb` '%s' und nicht '?'.

Verbesserungsvorschläge, die dazu noch Code betreffen der nicht robust ist, mit einem "das habe ich schon immer so gemacht" abzubügeln, kommt übrigens nicht bei allen gut an.
deets

Also, ich hab' mal nen kleines script gebaut, dass dein Problem in sqlite loest.

Code: Alles auswählen

from sqlite3 import *

print "paramstyle:", paramstyle

conn = connect('/tmp/example.db')


c = conn.cursor()

c.execute("select count(*) from sqlite_master WHERE name=?", ("webs_forum_topics",))
table_exists = c.next()[0]
if not table_exists:
    c.execute("""create table webs_forum_topics (
    topicID int,
    replys int,
    lastpost int)""")

    c.execute("""insert into webs_forum_topics values (?, ?, ?)""",
              (1, 0, 1))

    conn.commit()

topicID = 1

c.execute("""update webs_forum_topics set replys = replys + ? where topicID = ?""",
          (1, topicID))

c.execute("""select * from webs_forum_topics where topicID = ? """, (topicID,))
for row in c:
    print row

conn.commit()

*ACHTUNG*: der paramstyle deiner DB ist wichtig, wenn der nicht "qmark" ist, muessen statt Fragezeichen was anderes in die Statements!

Ausserdem verstehe ich das ganze try/except-gedoens nicht, wenn du gar nix machst, sollten die exceptions doch fliegen?!? dann kannst du auch sehen, was passiert...
Benutzeravatar
Mathmos
User
Beiträge: 36
Registriert: Dienstag 10. August 2010, 21:31

Erstmal Danke
Es funktioniert jetzt. Ich habe nun solange rumprobiert bis es funktioniert hat =)

naja, ich habe nicht mit IDLE gearbeitet, da IDLE immer ne Fehlermeldung raushaut wenn man ein Icon setzt ;)
aber ich werds mal wieder mit IDLE versuche.

@BlackJack und deets: Ja diese bösesn Fragezeichen haben bei mir nicht so ganz hingehauen ;)

@sparrow und deets: Funktioniert leider bei mir nicht, ich benutze Python 2.5

@BlackJack: Ich habe die Vorschläge nicht abgebügelt. Ich wollte lediglich darauf hinweisen, dass mir bisher niemand gesagt hat, dass es scheiße ist, so wie ich das mache ;)
Siehe hier: http://www.python-forum.de/viewtopic.php?f=1&t=23773

Mein Code sieht nun so aus.
Gibts daran noch was zu verbessern?

Code: Alles auswählen

		cursor3 = verbindung.cursor()
		ldate = time.time()
		lposter = int_ergebnis_user
		cursor3.execute('UPDATE webs_forum_topics set lastdate=%d, lastposter=%d, replys=replys+%d where topicID=%d'% (ldate, lposter, 1, topicID))
BlackJack

@Mathmos: Du benutzt schon wieder ``%`` um die Werte in die SQL-Anfrage zu formatieren und nicht das zweite Argument von `execute()`.
Benutzeravatar
Mathmos
User
Beiträge: 36
Registriert: Dienstag 10. August 2010, 21:31

ja, das ist auch irgendwie das einzige was geht.

Code: Alles auswählen

1. cursor3.execute('UPDATE webs_forum_topics set lastdate=%d, lastposter=%d, replys=replys+%d where topicID=%d', (ldate, lposter, 1, topicID))
2. cursor3.execute('UPDATE webs_forum_topics set lastdate=?, lastposter=?, replys=replys+? where topicID=?', (ldate, lposter, 1, topicID))
1.

Code: Alles auswählen

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python25\lib\lib-tk\Tkinter.py", line 1403, in __call__
    return self.func(*args)
  File "C:\Python25\xXBeispeileXx\GTS-VideoUpper.py", line 338, in UploadPics
    cursor3.execute('UPDATE webs_forum_topics set lastdate=%d, lastposter=%d, replys=replys+%d where topicID=%d', (ldate, lposter, 1, topicID))
  File "C:\Python25\lib\site-packages\MySQLdb\cursors.py", line 151, in execute
    query = query % db.literal(args)
TypeError: int argument required

2.

Code: Alles auswählen

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python25\lib\lib-tk\Tkinter.py", line 1403, in __call__
    return self.func(*args)
  File "C:\Python25\xXBeispeileXx\GTS-VideoUpper.py", line 338, in UploadPics
    cursor3.execute('UPDATE webs_forum_topics set lastdate=?, lastposter=?, replys=replys+? where topicID=?', (ldate, lposter, 1, topicID))
  File "C:\Python25\lib\site-packages\MySQLdb\cursors.py", line 151, in execute
    query = query % db.literal(args)
TypeError: not all arguments converted during string formatting
deets

Woher kommen denn die "%d"? Die sind Unfug. Ich habe kein MySQLdb zur Verfuegung, darum kann ich das nicht ueberpruefen. Aber IMHO ist das "%s", egal, was man fuer einen Wert reintut.

Und "geht nicht, ich habe Python2.5" ist keine Antwort auf etwas, dass ich geschrieben habe - denn alles, was ich dir gezeigt habe, habe ich mit Python2.5 gemacht.
BlackJack

@Mathmos: Der Platzhalter bei `MySQLdb` ist ja auch '%s' und nicht '%d' oder '?'. Das hatten wir aber schon mal…
Benutzeravatar
Mathmos
User
Beiträge: 36
Registriert: Dienstag 10. August 2010, 21:31

args... das kommt davon wenn man C mit python vermischt^^

naja jetzt funktioniert =)

@ deets: also in IDLE bekomme ich dann immer folgende Meldung
Warning: 'with' will become a reserved keyword in Python 2.6
deets

Oh, es gibt %d auch in Python - aber nicht an dieser Stelle.

Und was das with angeht - das geht auch in Python2.5, dazu muss man

Code: Alles auswählen

from __future__ import with_statement
an den Anfang der Datei schreiben.
Benutzeravatar
Mathmos
User
Beiträge: 36
Registriert: Dienstag 10. August 2010, 21:31

ah, ok
scheint nun zu funktionieren =)

Danke
Antworten