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 | ||