Datenbankinteraktion Beispiel

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
the_real_noob
User
Beiträge: 20
Registriert: Donnerstag 26. Januar 2017, 09:57

Guten Tag, allerseits!

Ich arbeite mich gerade in Python ein, bin Autodidakt, also nix von wegen Mathe- und/oder Informatikstudium und in manchen Punkten ist die Lernkurve für mich sehr hoch.

Ich benutze Python 3.5 und PostgreSQL mit Psycopg2 als Anbindung. PyCharm ist meine Entwicklungsumgebung.

Folgendes Fragen habe ich:

1. Ich will eine Klasse schreiben, welche den Verbindungsaufbau und die Datenverarbeitung verwaltet. Ich suche dafür ein Beispiel, dass ich mir zur Vorlage nehmen kann -> hat jemand eine Webadresse wo ich derartige Beispiele finden kann bzw. kann mir jemand ein Beispiel posten?

2. Ich verstehe die Verarbeitung folgender SQL-Anweisung nicht:

cur.execute('CREATE TABLE IF NOT EXISTS "test_db" (id serial PRIMARY KEY, firstname varchar, surname varchar);')

Es geht um die Anführungszeichen. Ich bekomme eine Fehlermeldung, wenn ich die gesamte Anweisung in doppelte Anführungszeichen setze und test_db in einfache Anführungszeichen. Welche Regel bestimmt das? Sonst ist es in Python doch egal, ob ich zuerst einfache und dann doppelte Anführungszeichen nutze, oder?

cur.execute('INSERT INTO \'test_db\' (firstname, surname) VALUES (\'Paul\', \'Meyer\');')

In diesem Fall ist ein ähnliches Problem. Während Python die maskierten einfachen Anführungszeichen der Werte anstandslos verarbeitet, wirft das Programm für die maskierten einfachen Anführungszeichen des Tabellennames wieder eine Fehlermeldung aus, während es doppelte Anführungsstriche anstandslos verarbeitet. Woran liegt das?
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@the_real_noob: Für Klassen zum Verbindungsaufbau und der Datenverarbeitung ist SQLAlchemy empfehlenswert. Der Fehler mit den Anführungszeichen hängt mit der Syntax von SQL zusammen: Doppelte Anführungszeichen maskieren Namen (Sonderzeichen, Groß-Klein-Schreibung, ...); einfache Anführungszeichen sind für Strings da. Weder das eine noch das andere sollte man normalerweise brauchen. Tabellennamen sollten sich an die normalen Konventionen der Datenbank halten, und Strings werden per Platzhalter übergeben.
BlackJack

@the_real_noob: Ad 2.: Das hat nichts mit Python zu tun sondern mit SQL. SQL ist es nicht egal ob man " oder ' verwendet. " ist eine Möglichkeit Bezeichner anzugeben die eventuell Zeichen enthalten die ”nackte” Namen nicht enthalten dürfen, währen ' in SQL für literale Zeichenketten verwendet wird. Und ein Tabellenname muss ein Bezeichner sein und darf keine Zeichenkette sein. "test_db" und test_db sind in SQL das selbe, nämlich ein Bezeichner test_db, während 'test_db' die Zeichenkette mit den Buschtaben t, e, s, … ist.

Beim zweiten Beispiel ist es das gleiche. 'Paul' und 'Meyer' sind Werte, da sind Zeichenketten okay, aber test_db ist der Tabellennam, das muss ein Bezeichner sein.

Ad 1.: Bevor Du eine Untermenge von SQLAlchemy selber implementierst, solltest Du eventuell einen Blick auf diese Bibliothek werfen.
the_real_noob
User
Beiträge: 20
Registriert: Donnerstag 26. Januar 2017, 09:57

Danke für Eure Antworten. Leider ist das Problem ein bisschen tricky zu lösen.

Ich habe jetzt folgende Variante gefunden:

Code: Alles auswählen

a = 'Annegret'
b = 'Schmidt'
cur.execute('INSERT INTO Daten (firstname, surname) VALUES (\'' + a + '\' , \'' + b + '\');')
Man muss tatsächlich beim Einfügen der Strings die Anführungszeichen maskieren. Wie man das mit Zahlen macht, ist mir noch nicht ganz klar.

Zwar funktioniert das:

Code: Alles auswählen

id = 6
z1 = 6
z2 = 8
cur.execute('INSERT INTO Zahlen VALUES (' + str(id) + ',' + str(z1) + ',' + str(z2) + ');')
Dabei muss ich aber die id selbst eingeben, während sie mir bei obigem Beispiel automatisch generiert wird. Vielleicht habt Ihr eine Idee oder eine Erklärung.

SQLAlchemy habe ich mir mal auf die Agenda geschrieben.
BlackJack

@the_real_noob: Das ”Problem” ist keines weil man keine Werte in SQL-Zeichenketten hinein formatiert. In die SQL-Anweisung schreibt man für Werte den Platzhalter den das verwendete Datenbankmodul dafür vorsieht und die Werte selbst werden der `execute()`-Methode als zweites Argument übergeben. Alles andere ist bestenfalls ineffizient, ansonsten *unsicher* (safety), und im schlimmsten Fall *richtig unsicher* (security).
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@the_real_noob: Wo hast Du diese Varianten gefunden? In der Dokumentation wohl kaum.

Nochmal zum Mitschreiben:

Code: Alles auswählen

a = 'Annegret'
b = 'Schmidt'
cur.execute('INSERT INTO Daten (firstname, surname) VALUES (%s, %s)', (a, b))

z1 = 6
z2 = 8
cur.execute('INSERT INTO Zahlen VALUES (default, %s, %s)', (z1, z2))
the_real_noob
User
Beiträge: 20
Registriert: Donnerstag 26. Januar 2017, 09:57

@Sirius3

Wohl doch, ich habe nur in die falsche Dokumentation geschaut. :D

Danke
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Wohl doch, ich habe nur in die falsche Dokumentation geschaut.
Wie die Platzhalterersetzung funktioniert ist in der Python DB API 2.0 beschrieben. Das findest du nicht in der Doku des RDBMS, sondern wenn in der Doku deiner Datenbankanbindung, in deinem Fall also dem `Psycopg2`Modul.

Gruß, noisefloor
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

the_real_noob hat geschrieben:Wohl doch, ich habe nur in die falsche Dokumentation geschaut. :D
Das ist durchaus korrektes SQL. Das Python-Modul bietet jedoch die Möglichkeit, die Texteinsetzungen (sprich: Werte, die man vorher nicht kennt) lesbarer zu gestalten und möglichen Sicherheitslücken beim stumpfen Einsetzen von "problematischem" Text aus dem Weg zu gehen. Von diesen Möglichkeiten sollte man Gebrauch machen. Gerade dein Ansatz ist so ziemlich das Umständlichste, das man in Sachen (Un-)Lesbarkeit machen kann...
the_real_noob
User
Beiträge: 20
Registriert: Donnerstag 26. Januar 2017, 09:57

snafu hat geschrieben:Gerade dein Ansatz ist so ziemlich das Umständlichste, das man in Sachen (Un-)Lesbarkeit machen kann...
Deswegen habe ich nachgefragt. Erschien mir auch zu umständlich. Wie gesagt: Anfänger!

Bleibt nochmal die Frage:

Hat jemand eine Beispieldatei, wie man die Datenbankinteraktion gut gestalten kann oder kann mir jemand einen Tipp geben, wo ich so eine Datei finden kann?
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@the_real_noob: Beispiele sind doch bei mir oder in der Dokumentation.
the_real_noob
User
Beiträge: 20
Registriert: Donnerstag 26. Januar 2017, 09:57

Ich muss vielleicht mein Ansinnen noch einmal präzisieren, denn es ist dadurch missverständlich geworden, dass ich gleich zwei Probleme thematisiert habe. Das eine bezüglich der Anführungszeichen, hat eigentlich gar nichts mit meinem Ansinnen zu tun. Es hat sich einfach ergeben, dass ich es gleichzeitig zur Sprache brachte.

Wonach ich suche ist globalerer Natur:

So hat z. B. @BlackJack in einem Beitrag hier im Forum geschrieben, dass es notwendig ist, den Cursor nach einem Datenbankeintrag immer zu schließen, weil es DBMS gibt, die nicht damit klarkommen, wenn dieser offen bleibt (ich hoffe, ich habe das so richtig wiedergegeben). Zudem habe ich mühselig herausgefunden, dass ebenso die commit-Anweisung ausgeführt werden muss, weil ansonsten der eingetragene Datensatz nicht bestätigt und damit nicht abgeschlossen wird (richtig?)

Wie gesagt, ich habe das mühselig herausgefunden und wenn mir nicht jemand einen Tipp gegeben hätte, würde ich wahrscheinlich immer noch über dem Problem brüten, warum ich meine INSERT-Anweisung nicht in die DB bekomme. Meine Überlegung war halt, dass wenn ich so weiter mache, ich irgendwann verzweifelt die Flinte ins Korn werfe und da kam mir die Idee, es wäre doch am besten, ich würde es machen, wie ein Kind. Ich ahme nach, ich mache nach und lerne dadurch.

Wenn ich nun ein Beispiel für eine Datenbankinteraktion hätte, die von einem erfahrenen Programmierer erstellt wurde, dann würde ich z. B. lesen, dass da bzw. wann eine commit-Anweisung ausgeführt wurde bzw. ein Cursor geschlossen wurde. Ich verstehe in diesem Moment vielleicht nicht, warum das gemacht wurde und welche Funktionalität dahintersteht, aber ich komme erst einmal weiter. Außerdem weiß ich dann auch, wonach ich suchen muss. Ohne dieses Wissen ist es sehr mühselig.

In den entsprechenden Dokumentationen findet man immer nur Codeschnipsel. Wie aber sieht das umgesetzt aus?

Anderes Beispiel:

Soll man den Aufbau der Verbindung zur Datenbank in einer eigenen Klasse definieren, die dann als Basisklasse für die erbenden Unterklassen dient oder reicht eine Methode aus, die man z. B. in eine Klasse DB_Management schreibt?

Mir geht es bei meinem ursprünglichen Ansinnen hier vor allem um die Herangehensweise. Ich dachte mir, es gibt hier vielleicht Programmierer, die das schon 100mal gemacht haben und mir einfach mal so ein codiertes Beispiel zeigen können.

Dann, und ich misch gleich wieder was anderes ein:

Ich habe mittlerweile acht Stunden damit verbracht, herauszufinden, wie ich die Existenz einer Tabelle ermitteln kann. Gedanke dahinter war:

Ich will herausfinden, ob eine DB besteht, welche Tabellen sie besitzt, mit welchen Datenfeldern bzw. deren Datentypen, wieviele Einträge sie hat etc.

Wenn ich die gängigen Suchmaschinen anwerfe und Schlagworte wie 'Tabelle existiert' oder 'Wie finde ich heraus, ob ...', bekomme ich jede Menge Datenmüll zurück, der mit meiner Problemstellung nichts zu tun hat. Oder aber ich bekomme einen Hinweis, wie z. B. bei stackoverflow, mit dem ich nicht klarkomme, wie z. B. das hier:

Code: Alles auswählen

cur.execute('SELECT exists(select * from information_schema.tables where table_name=%s)' , ('Daten' ,))  
a = cur.fetchall()
Wenn ich z. B. eine Tabelle anlegen will, wenn sie nicht existiert (und ja, ich weiß, das es CREATE TABLE IF NOT EXISTS gibt, aber das ist nicht die Problemstellung) oder eben andernfalls eine andere Anweisung ausführen will, scheitere ich mit schöner Regelmäßigkeit daran, dass ich nicht weiß, ob das überhaupt richtig ist und wie ich fetchall() verarbeiten muss.

Ich will das jetzt gar nicht im Detail ausführen, was ich da alles probiert habe, auch hier würde ich mir einfach nur mal ein Beispiel wünschen, an dem ich mich orientieren kann.

Vielleicht kann mir ja jemand helfen.

@Sirius3

Was heißt denn bei Dir?
BlackJack

@the_real_noob: Ich kann das nicht so ganz nachvollziehen weil Sirius3 ja auch schon die Dokumentation erwähnt hat. Die Reihenfolge wie man die Grundlegenden Funktionen und Methoden verwendet, das man beispielsweise `commit()`\en muss, und das schliessen des Cursors und der Verbindung sind da gleich ganz am Anfang in einem Beispiel zu sehen. Ziemlich am Anfang ist auch der Begriff DB-API 2.0 auf das entsprechende PEP verlinkt. Das sollte man sich eventuell auch mal anschauen.

Einiges von dem Vokabular wie Transaktion, Commit, Rollback, gehört auch nicht direkt zu Python sondern zu SQL-Datenbanken allgemein. Das wird wahrscheinlich in keiner Python-Moduldokumentation so wirklich in die Tiefe gehend behandelt werden, weil SQL und relationale Datenbanken von Python unabhängige und recht umfangreiche Themen sind. Ein Buch oder Tutorial zu SQL/relationale Datenbanken ersetzen die Dokumentationen zu Anbindungen an solche Datenbanken eher nicht.

Praktisch würde ich eigentlich immer SQLAlchemy verwenden statt selber etwas in der Richtung nach zu programmieren. Ausnahme wäre eine Django-Anwendung weil dort ein eigenes ORM mitgeliefert wird.

Wie man die Existenz einer Tabelle ermittelt hat nichts mit Python zu tun. Das ist im Grunde nicht einmal SQL denn AFAIK gibt SQL selbst keine Möglichkeit dazu. Allerdings bietet wohl jedes DBMS so eine Möglichkeit. Womit das eine Frage an die Dokumentation von PostgreSQL wäre, in Deinem Fall. Oder SQLAlchemy, was dann wieder von den konkreten DBMS abstrahiert und eine allgemeine Möglichkeit bietet „reflection“ zu betreiben. Allerdings ist das ein bisschen komisch diese Frage zu haben, denn ein Datenbankentwurf ist ja so etwas wie eine Deklaration in Deinem Programm. Die braucht man nicht abfragen, sondern die schreibt man selbst. Ausnahme wären bereits bestehende Datenbanken von anderen Anwendungen, wo „reflection“ ganz nützlich sein kann.
the_real_noob
User
Beiträge: 20
Registriert: Donnerstag 26. Januar 2017, 09:57

Hm, PEP? Gebe ich mir mal.

Was mein Ansinnen bezüglich der Abfrage des DBMS angeht, bezog sie sich eher auf die Administration, als die Programmierung dessen. Ich dachte, dass es doch on the fly möglich sein müsste, ein DBMS abzufragen, welche Datenbanken nebst Inhalten es hat.
BlackJack

@the_real_noob: Du administierst das DBMS ja nicht mit Python. Üblicherweise bringen DBMS einen Kommandozeilenclient mit und es gibt grafische (Web)Oberflächen, die einem so etwas anzeigen. Auf der Kommandozeile bringt PostgreSQL ``psql`` mit. Alternativ ist ``pgcli`` ganz nett (bunt und mit Autovervollständigung). Grafisch gibt es beispielsweise pgAdmin3 (GUI) oder Adminer.php (Weboberfläche für verschiedene DBMS).

Wie schon gesagt ist so eine Abfrage bei den meisten DBMS möglich, aber das ist DBMS-spezifisch. Das ist nichts was in SQL definiert ist, das macht jedes DBMS anders. Weil letztendlich auch jedes DBMS frei ist Datenbankschemata so zu verwalten und modellieren wie es möchte.
Antworten