SQLite 3 Wert eines Tables auf anderen Table verweisen

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

FrostByteGER hat geschrieben:Woran lag mein Fehler denn genau?
Daran:

Code: Alles auswählen

WHERE Customers.Customernumber AND Accounts.Customernumber = (?)
Das hättewie folgt aussehen müssen:

Code: Alles auswählen

WHERE Customers.Customernumber =(?) AND Accounts.Customernumber = (?)
Oder auch so:

Code: Alles auswählen

WHERE Customers.Customernumber Accounts.Customernumber AND Accounts.Customernumber = (?)
Persönlich finde ich diese Art des SQL-Statements allerdings mittelprächtig scheußlich und bevorzuge die von mir bereits gepostete aussagekräftigere Variante mit JOIN.
BlackJack

@FrostByteGER: Analog dazu verhält sich übrigens Python bei ``a and b == c``. Du kannst nicht einfach einen umgangssprachlichen Satz zu einem logischen Ausdruck machen. Der muss sich schon an die Auswertungsregeln der Programmiersprache halten.
FrostByteGER
User
Beiträge: 29
Registriert: Mittwoch 25. April 2012, 16:11

Yo verstanden.

Letzte Frage dazu:

Wenn ich jetzt ne Zeile löschen will, also von Customers, dann soll er die dazugehörige(n) Zeilen bei Accounts auch löschen. Ich hab mir das ON DELETE CASCADE Statement schon angeguckt, aber das dürfte doch nur funktionieren, wenn ich etwas von Accounts lösche. Dadurch löscht er ja dann die dazugehörige Customers Zeile.

Gibts dazu ne möglichkeit, oder muss ich das Delete-Statement zweimal , also je Einmal bei Accounts und Customers, anwenden?
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Stichwort "Fremdschlüssel" und "Kaskadierung":

http://www.sqlite.org/foreignkeys.html
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Das sieht dann in etwa so aus:

Code: Alles auswählen

import sqlite3

names = (("sparrow",), ("frosty",), ("hanniball",), ("gangster",))
trans = ((500, 1), (200, 1), (50, 2),  (100, 3), (200, 3), (400, 3),
         (50, 4), (-20, 4), (10, 4))

con = sqlite3.connect(":memory:")
con.execute("PRAGMA foreign_keys = ON") # Activate for everey connection!
con.execute("CREATE TABLE costumers (id INTEGER PRIMARY KEY, name TEXT)")
con.execute(("CREATE TABLE accounts (id INTEGER PRIMARY KEY, "
             "value INTEGER, "
             "costumer REFERENCES costumers(id) ON DELETE CASCADE)"))
con.executemany("INSERT INTO costumers (name) VALUES (?)", names)
con.executemany("INSERT INTO accounts (value, costumer) VALUES (?, ?)", trans)
print "Vor DELETE:", con.execute("SELECT * FROM accounts").fetchall()
con.execute("DELETE FROM costumers WHERE name = ?", ("hanniball",))
print "Nach DELETE:", con.execute("SELECT * FROM accounts").fetchall()
FrostByteGER
User
Beiträge: 29
Registriert: Mittwoch 25. April 2012, 16:11

Strange, ich habs mal getestet, aber es funzt net!

Wie du sagtest, hab ichs mit dem ON DELETE CASCADE Statement getestet. Entweder es wird die Acc oder Customerzeile gelöscht! Je nachdem welche der beiden Methoden ich aufrufe(deleteAcc/Cust)

Ich denke, die Syntax sollte korrekt sein.

Code: Alles auswählen

        self.cur.execute("DELETE FROM Customers WHERE Customernumber = (?)",custid)

Code: Alles auswählen

        self.cur.execute("""CREATE TABLE Accounts (
        Accountnumber TEXT, PIN INTEGER, Value INTEGER, Currency TEXT, Customernumber TEXT, 
        PRIMARY KEY(Accountnumber), FOREIGN KEY(Customernumber) REFERENCES Customers(Customernumber) ON DELETE CASCADE)""")
Beim erstellen des Customertables hab ich soweit nichts geändert. Google meint, ich sollte erstmal "con.execute("PRAGMA foreign_keys = ON")" nutzen, was ich auch getan hab, aber dennoch hat sich nichts getan. Er löscht partout nicht beides, sondern immer nur das eine!

Ich hab mal deinen Code genommen, dabei kommt das raus:

Vor DELETE: [(1, 500, 1), (2, 200, 1), (3, 50, 2), (4, 100, 3), (5, 200, 3), (6, 400, 3), (7, 50, 4), (8, -20, 4), (9, 10, 4)]
Nach DELETE: [(1, 500, 1), (2, 200, 1), (3, 50, 2), (4, 100, 3), (5, 200, 3), (6, 400, 3), (7, 50, 4), (8, -20, 4), (9, 10, 4)]


Ich denke, dass sollte auch so nicht sein xD

Ich kanns mir soweit nicht erklären! Ansonsten mach ich es manuell mit 2 execute Anweisungen. Je eine für Accounts und Customer in einer Methode. Allerdings bin ich mir bei einer Sache nicht sicher. Wenn ich das ON DELETE CASCADE benutze, dann löscht er mir doch nicht auch die Customerzeile weg, wenn ich nur ne Acczeile löschen will, oder?

Langsam tendiere ich echt dazu, einfach 2 Statements in der deleteCustRow Methode zu machen...
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Das hier muss für jede Connection aufgerufen werden (steht in dem von mir verlinkten Dokument und auch in meinem Beispiel:

Code: Alles auswählen

con.execute("PRAGMA foreign_keys = ON") # Activate for everey connection!
Wenn du mein Beispiel ausführst wird folgendes heraus kommen:

Code: Alles auswählen

Vor DELETE: [(1, 500, 1), (2, 200, 1), (3, 50, 2), (4, 100, 3), (5, 200, 3), (6, 400, 3), (7, 50, 4), (8, -20, 4), (9, 10, 4)]
Nach DELETE: [(1, 500, 1), (2, 200, 1), (3, 50, 2), (7, 50, 4), (8, -20, 4), (9, 10, 4)]
Wenn da bei dir etwas anderes steht führst du etwas anderes aus.
Versuch erst einmal an einem Minimalbeispiel ob es funktioniert. Wenn es das nicht tut, zeig es damit wir die Fehler finden können. Mangels Beschreibung kann ich den bei dir nämlich nicht erkennen.

EDIT: Bei dem Syntax für dein Löschen: die Klammern um das Fragezeichen sind falsch, die gehören da nicht hin. Außerdem bin ich mir ziemlich sicher, dass das 2. Argument von execute eine Liste o.ä. sein muss, keine Ahnung ob es reicht da einfach eine Zeichenkette zu nehmen.
EDITEDIT: Nein, reicht natürlich nicht. Deine Anweisung kann so nicht funktionieren. Ich habe dir ein lauffähiges Beispiel gegeben, das muss du eigentlich nur noch minimal abwandeln damit es bei dier passt.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

sparrow hat geschrieben: EDITEDIT: Nein, reicht natürlich nicht. Deine Anweisung kann so nicht funktionieren.
Es ist ja nicht so, dass wir den OP dahingehend nicht schon mehrfach hingewiesen haben: Thread. Ein wenig mehr Sorgfalt beim Programmieren wäre da hilfreich ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
FrostByteGER
User
Beiträge: 29
Registriert: Mittwoch 25. April 2012, 16:11

Ich kann dem Delete-Statement nur ein Tuple mit einem Element zuweisen, was ich auch getan hab. Diesmal hab ich sehr wohl darauf geachtet. Das ist wohl ein Missverständis. Ich hab custid vorher in ein Tuple umgewandelt. Ich bitte um Entschuldigung :oops:

Ich habs mal einfach mit ner Liste oder einem Tuple mit mehreren Elementen getestet. Das funktioniert natürlich nicht. Es darf nur ein Listen/tuple-element sein.

Das mit den Klammern wusste ich nicht, funktioniert aber trotzdem nicht! Es wird halt nur die gewünschte Customerzeile gelöscht, nicht aber die Accountszeile. Das mit con.execute("PRAGMA foreign_keys = ON") hab ich auch getestet(direkt bei der Datenbankverbindungsinitialisierung aufgerufen). Ebenso wenig Erfolg. Wie im letzten Post schon erwähnt, hab ich dein Beispiel auch mal getestet, in einer extra Datei. Dabei kommt das dabei raus:

Code: Alles auswählen

Vor DELETE: [(1, 500, 1), (2, 200, 1), (3, 50, 2), (4, 100, 3), (5, 200, 3), (6, 400, 3), (7, 50, 4), (8, -20, 4), (9, 10, 4)]
Nach DELETE: [(1, 500, 1), (2, 200, 1), (3, 50, 2), (4, 100, 3), (5, 200, 3), (6, 400, 3), (7, 50, 4), (8, -20, 4), (9, 10, 4)]
Du siehst, irgendwas stimmt nicht. Liegt es eventuell an dem SQlite Modul selbst?

Leider kann ich zurzeit nicht meinen Lehrer fragen, da der momentan fehlt :/

Hier nochmal ein Screen, was passiert, wenn ich den Code von dir ausführe: http://s14.directupload.net/file/d/2926 ... m3_png.htm
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Um Probleme mit der Eclipe-Umgebung zu vermeiden:

Kopier meinen Code mal in eine Datei, führ die Datei einfach mit dem Python-Interpreter aus.

Welche Python-Version verwendest du?
FrostByteGER
User
Beiträge: 29
Registriert: Mittwoch 25. April 2012, 16:11

2.5, weil wir das in der Schule nutzen. Daher verwende ich das auch zuhause, um Probleme zu vermeiden!

Selbst mit dem MED Editor kommt dasselbe Ergebnis raus. Er löscht bei deinem Beispiel garnichts!
FrostByteGER
User
Beiträge: 29
Registriert: Mittwoch 25. April 2012, 16:11

Okay, vermutlich liegts hierran: http://stackoverflow.com/questions/3296 ... ys-working
Meine Version mit sqlite3.sqlite_version ist 3.3.4

Gibts ne Möglichkeit, das manuell upzudaten, ohne gleich ne neue Python-Version zu nutzen?
BlackJack

@FrostByteGER: Ich würde sagen nein, und ich verstehe auch nicht was das bringen soll dann gleichzeitig bei Python 2.5 zu bleiben. Damit hättest Du eine Kombination die wohl ausser Dir niemand anders benutzt.
FrostByteGER
User
Beiträge: 29
Registriert: Mittwoch 25. April 2012, 16:11

Puuuuh das war ein rumgefummele mit dem konvertieren der Files zur Python 3 Syntax...(Lib2to3 sei dank!)

Wie dem auch sei, es funktioniert nun einwandfrei! Damit ist meine Datenbankklasse endlich fertig. Vielen Dank für die ganzen Tipps und Hilfestellungen!

Vielleicht mach ich demnächst ein richtiges Modul draus, damit ich mir das neuschreiben von diesen nervigen Funktionen ersparen kann^^
Antworten