Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
python:psycopg [2023/06/30 18:49] root [Server-side binding] |
python:psycopg [2023/07/01 00:00] (aktuell) root [Copy] |
||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
* Postgres-Datenbank-Treiber für Python | * Postgres-Datenbank-Treiber für Python | ||
+ | |||
+ | ===== Transaction-Handling ===== | ||
+ | |||
+ | * jede Aktion in der Datenbank (jedes aufrufen von cursor.execute()) erzeugt eine Transaktion (wenn nicht eine offene besteht) | ||
+ | * per Default ist autocommit=False für Datenbankverbindungen | ||
+ | * nach dem Absetzen von cursor.execute wird die Transaktion also nicht committed | ||
+ | * es muss also explizit committed werden | ||
+ | |||
+ | Es gibt mehrere Möglichkeiten das commit oder rollback über Context-Blöcke zu steuern | ||
+ | |||
+ | <sxh python> | ||
+ | with psycopg.connect(someconnectionninformation) as connection: | ||
+ | cursor = connection.cursor() | ||
+ | cursor.execute(somestatement) | ||
+ | cursor.execute(someotherstatement) | ||
+ | </ | ||
+ | |||
+ | ^ In diesem Fall wird am Ende des Blocks commit gemacht oder rollback (abhängig davon ob eines der Statements fehlgeschlagen ist oder alle durchgelaufen sind). \\ | ||
+ | Außerdem wird die Verbindung zur Datenbank danach geschlossen | ||
+ | |||
+ | |||
+ | <sxh python> | ||
+ | with psycopg.connect(someconnectionninformation) as connection: | ||
+ | cursor = connection.cursor() | ||
+ | with connection.transaction(): | ||
+ | cursor.execute(somestament) | ||
+ | cursor.execute(somestament) | ||
+ | with connection.transaction(): | ||
+ | cursor.execute(somestament) | ||
+ | cursor.execute(somestament) | ||
+ | </ | ||
+ | |||
+ | ^ Die beiden transaction-Blöcke werden unabhängig voneinander commited. | ||
+ | Man muss allerdings aufpassen, wenn außerhalb der transaction-Blöcke Statements ausgeführt werden, dann starten diese implizit Transaktionen und die transaction-Blöcke erstellen dann nur savepoint sub-transactions. \\ | ||
+ | Ggf. sollte man beim connect dann autocommit=True setzen, dann werden alle Statements außerhalb der transaction-Blöcke als eigene Transaktionen gehandhabt, die sofort commited sind. | ||
===== Server-side binding ===== | ===== Server-side binding ===== | ||
Zeile 19: | Zeile 54: | ||
===== Mischen von Transaktions- und Nicht-Transaktionsstatements in einem execute ===== | ===== Mischen von Transaktions- und Nicht-Transaktionsstatements in einem execute ===== | ||
- | * per Default ist für execute Autocommit eingestellt -> man braucht also die Transaktion nicht selbst zu commiten, das macht die Datenbank für einen | + | * |
* die meisten Kommandos (auch DDL-Kommandos) können in einer Transaktion ausgeführt werden | * die meisten Kommandos (auch DDL-Kommandos) können in einer Transaktion ausgeführt werden | ||
* es gibt aber Ausnahmen wie " | * es gibt aber Ausnahmen wie " | ||
* Postgres fasst intern alle Statements eines cursor()-Aufrufs zu einer Transaktion zusammen | * Postgres fasst intern alle Statements eines cursor()-Aufrufs zu einer Transaktion zusammen | ||
+ | * oder auch | ||
* ^ deswegen kann man im gleichen execute()-Aufruf keine Kommandos mischen die Transaktionen unterstützen und solche die es nicht tun | * ^ deswegen kann man im gleichen execute()-Aufruf keine Kommandos mischen die Transaktionen unterstützen und solche die es nicht tun | ||
+ | |||
+ | |||
+ | ===== Temporary tables ====== | ||
+ | |||
+ | * Existieren nur in der Session in der sie erstellt wurden | ||
+ | * verschiedene Verbindungen zur Datenbank können also die gleiche Tabelle ggf. mit unterschiedlichem Inhalt haben | ||
+ | |||
+ | <sxh sql> | ||
+ | create temp table somename (somerow varchar) on commit delete rows; | ||
+ | </ | ||
+ | |||
+ | * "on commit" | ||
+ | * preserve_rows (Default in postgres, aber nicht im Standard) -> erhalte die Tabelle wie sie ist, inkl. Inhalt | ||
+ | * delete rows -> truncate aka. lösche alle Einträge, aber erhalte die leere Tabelle | ||
+ | * drop -> lösche die Tabelle | ||
+ | |||
+ | |||
+ | ===== Copy ====== | ||
+ | |||
+ | * Eigentlich eine Möglichkeit eine größere Menge Einträge aus Dateien (oder stdin) einzulesen oder nach stout/Datei auszugeben | ||
+ | * ist meist effizienter als insert into | ||
+ | * statt einer Tabelle kann man auch sub-select-Statements benutzen | ||
+ | * schlägt das einfügen eines Datensatzes fehl wird alles rückabgewickelt | ||
+ | * im Gegensatz zu insert into gibt es kein "on conflict" | ||
+ | * es dürfen also auch keine Doubletten in den Daten sein (die einen unique-Spalte betreffen würden) | ||
+ | |||
+ | |||
+ | Daten in eine Tabelle einfügen: | ||
+ | <sxh python> | ||
+ | with cursor.copy(" | ||
+ | for record in records: | ||
+ | copy.write_row(record) | ||
+ | </ | ||
+ | * record muss dabei ein iterateable sein, auch wenn nur eine Spalte eingefügt wird | ||
+ | * also zum Beispiel ein tuple | ||
+ | * liegt daran das es auch mehrere Spalten sein könnten -> (spalte1, spalte2 usw.) | ||
+ | |||
+ | Daten aus einer Tabelle auslesen: | ||
+ | <sxh python> | ||
+ | with cursor.copy(" | ||
+ | for record in copy.rows(): | ||
+ | Some action | ||
+ | </ | ||
+ | |||
+ | < | ||
+ | Traceback (most recent call last): | ||
+ | File " | ||
+ | copy.write_row(word) | ||
+ | File "/ | ||
+ | next(self.gen) | ||
+ | File " | ||
+ | raise ex.with_traceback(None) | ||
+ | psycopg.errors.BadCopyFileFormat: | ||
+ | CONTEXT: | ||
+ | </ | ||
+ | |||
+ | ^ Hier wurde vergessen das der Datensatz für die Spalte ein Iterateable sein muss. \\ | ||
+ | Daher wurde " | ||
+ | |||
+ | Fehlschlagender Code (Beispiel): | ||
+ | |||
+ | <sxh python> | ||
+ | names = [" | ||
+ | with cursor.copy(" | ||
+ | for name in names: | ||
+ | copy.write_row(name) | ||
+ | </ | ||
+ | |||
+ | * Daten werden aus einem iterateable names gelesen | ||
+ | * name ist eine Zeichenkette | ||
+ | * copy.write_row erwartet aber einen Datensatz | ||
+ | * ein Datensatz ist ein iterateable -> ein tuple, eine Liste usw. | ||
+ | * hier wurde ein String übergeben -> ein String ist auch ein iterateable -> also wird über den String iteriert | ||
+ | * da der String länger ist als die Menge der Spalten -> es gibt nur eine, kommt es zum Fehler | ||
+ | |||
+ | Hier sollte man auch aufpassen: | ||
+ | <sxh python> | ||
+ | names = [" | ||
+ | with cursor.copy(" | ||
+ | for name in names: | ||
+ | copy.write_row((name)) | ||
+ | </ | ||
+ | |||
+ | * name wird hier bei der Übergabe an copy.write_row scheinbar auf tuple gecastet | ||
+ | * allerdings nicht wirklich, es fehlt ein "," | ||
+ | * es muss (name,) heißen, sonst ist es ein String |