Zum Inhalt springen

Python-DB-API

Die standardmäßige DuckDB-Python-API bietet eine SQL-Schnittstelle gemäß der DB-API-2.0-Spezifikation nach PEP 249, ähnlich der SQLite-Python-API.

Verbindung

Um das Modul zu verwenden, müssen Sie zuerst ein DuckDBPyConnection-Objekt erstellen, das eine Verbindung zu einer Datenbank darstellt. Das geschieht über die Methode duckdb.connect.

Das Schlüsselwortargument config kann verwendet werden, um ein dict zu übergeben, das Schlüssel-Wert-Paare mit von DuckDB verstandenen Einstellungen enthält.

In-Memory-Verbindung

Der Sonderwert :memory: kann verwendet werden, um eine In-Memory-Datenbank zu erstellen. Beachten Sie, dass bei einer In-Memory-Datenbank keine Daten auf die Festplatte geschrieben werden (d. h. alle Daten gehen verloren, wenn Sie den Python-Prozess beenden).

Benannte In-Memory-Verbindungen

Der Sonderwert :memory: kann auch um einen Namen ergänzt werden, zum Beispiel: :memory:conn3. Wenn ein Name angegeben wird, erstellen nachfolgende Aufrufe von duckdb.connect eine neue Verbindung zur selben Datenbank und teilen die Kataloge (Views, Tabellen, Makros usw.).

Die Verwendung von :memory: ohne Namen erstellt immer eine neue, getrennte Datenbankinstanz.

Standardverbindung

Standardmäßig erstellen wir eine (unbenannte) In-Memory-Datenbank, die im Modul duckdb lebt. Jede Methode von DuckDBPyConnection ist auch auf dem Modul duckdb verfügbar; diese Verbindung wird von diesen Methoden verwendet.

Der Sonderwert :default: kann verwendet werden, um diese Standardverbindung zu erhalten.

Dateibasierte Verbindung

Wenn database ein Dateipfad ist, wird eine Verbindung zu einer persistenten Datenbank hergestellt. Wenn die Datei nicht existiert, wird sie erstellt (die Dateiendung ist unerheblich und kann .db, .duckdb oder etwas anderes sein).

read_only-Verbindungen

Wenn Sie im Nur-Lese-Modus verbinden möchten, können Sie das Flag read_only auf True setzen. Wenn die Datei nicht existiert, wird sie im Nur-Lese-Modus nicht erstellt. Der Nur-Lese-Modus ist erforderlich, wenn mehrere Python-Prozesse gleichzeitig auf dieselbe Datenbankdatei zugreifen sollen.

import duckdb
duckdb.execute("CREATE TABLE tbl AS SELECT 42 a")
con = duckdb.connect(":default:")
con.sql("SELECT * FROM tbl")
# or
duckdb.default_connection().sql("SELECT * FROM tbl")
┌───────┐
│ a │
│ int32 │
├───────┤
│ 42 │
└───────┘
import duckdb
# to start an in-memory database
con = duckdb.connect(database = ":memory:")
# to use a database file (not shared between processes)
con = duckdb.connect(database = "my-db.duckdb", read_only = False)
# to use a database file (shared between processes)
con = duckdb.connect(database = "my-db.duckdb", read_only = True)
# to explicitly get the default connection
con = duckdb.connect(database = ":default:")

Mit der Methode cursor() können Sie ein weiteres Handle auf eine bestehende Verbindung erhalten. Beachten Sie, dass dadurch ein weiteres Handle auf dieselbe Verbindung erstellt wird und keine neue Verbindung geöffnet wird; von einer Verbindung erstellte Cursor können daher nicht gleichzeitig Abfragen ausführen. Eine einzelne Verbindung ist threadsicher, wird aber für die Dauer jeder Abfrage gesperrt, wodurch der Datenbankzugriff effektiv serialisiert wird. Details finden Sie unter Über cursor().

Verbindungen werden implizit geschlossen, wenn sie außer Scope gehen, oder explizit mit close(). Sobald die letzte Verbindung zu einer Datenbankinstanz geschlossen ist, wird auch die Datenbankinstanz geschlossen.

Abfragen

SQL-Abfragen können mit der Methode execute() von Verbindungen an DuckDB gesendet werden. Nachdem eine Abfrage ausgeführt wurde, können Ergebnisse mit den Methoden fetchone und fetchall auf der Verbindung abgerufen werden. fetchall holt alle Ergebnisse und schließt die Transaktion ab. fetchone holt bei jedem Aufruf eine einzelne Ergebniszeile, bis keine Ergebnisse mehr verfügbar sind. Die Transaktion wird erst geschlossen, wenn fetchone aufgerufen wird und keine Ergebnisse mehr übrig sind (der Rückgabewert ist dann None). Als Beispiel: Bei einer Abfrage, die nur eine Zeile zurückgibt, sollte fetchone einmal aufgerufen werden, um die Ergebnisse abzurufen, und ein zweites Mal, um die Transaktion zu schließen. Unten einige kurze Beispiele:

# create a table
con.execute("CREATE TABLE items (item VARCHAR, value DECIMAL(10, 2), count INTEGER)")
# insert two items into the table
con.execute("INSERT INTO items VALUES ('jeans', 20.0, 1), ('hammer', 42.2, 2)")
# retrieve the items again
con.execute("SELECT * FROM items")
print(con.fetchall())
# [('jeans', Decimal('20.00'), 1), ('hammer', Decimal('42.20'), 2)]
# retrieve the items one at a time
con.execute("SELECT * FROM items")
print(con.fetchone())
# ('jeans', Decimal('20.00'), 1)
print(con.fetchone())
# ('hammer', Decimal('42.20'), 2)
print(con.fetchone()) # This closes the transaction. Any subsequent calls to .fetchone will return None
# None

Die Eigenschaft description des Verbindungsobjekts enthält die Spaltennamen gemäß dem Standard.

Vorbereitete Anweisungen

DuckDB unterstützt in der API außerdem vorbereitete Anweisungen mit den Methoden execute und executemany. Die Werte können als zusätzlicher Parameter nach einer Abfrage übergeben werden, die Platzhalter ? oder $1 (Dollarzeichen und eine Zahl) enthält. Die Notation ? fügt die Werte in derselben Reihenfolge ein, in der sie im Python-Parameter übergeben wurden. Die Notation $ erlaubt die Wiederverwendung von Werten innerhalb der SQL-Anweisung anhand der Nummer und des Index des im Python-Parameter gefundenen Werts. Werte werden gemäß den Konvertierungsregeln konvertiert.

Hier einige Beispiele. Zuerst eine Zeile mit einer vorbereiteten Anweisung einfügen:

con.execute("INSERT INTO items VALUES (?, ?, ?)", ["laptop", 2000, 1])

Zweitens mehrere Zeilen mit einer vorbereiteten Anweisung einfügen:

con.executemany("INSERT INTO items VALUES (?, ?, ?)", [["chainsaw", 500, 10], ["iphone", 300, 2]] )

Die Datenbank mit einer vorbereiteten Anweisung abfragen:

con.execute("SELECT item FROM items WHERE value > ?", [400])
print(con.fetchall())
[('laptop',), ('chainsaw',)]

Abfrage mit der Notation $ für eine vorbereitete Anweisung und wiederverwendeten Werten:

con.execute("SELECT $1, $1, $2", ["duck", "goose"])
print(con.fetchall())
[('duck', 'duck', 'goose')]

Warning Verwenden Sie executemany nicht, um große Datenmengen in DuckDB einzufügen. Bessere Optionen finden Sie auf der Seite zum Datenimport.

Benannte Parameter

Neben den standardmäßigen unbenannten Parametern wie $1, $2 usw. können Sie auch benannte Parameter wie $my_parameter übergeben. Bei benannten Parametern müssen Sie im Argument parameters ein Dictionary-Mapping von str auf den Wert bereitstellen. Ein Beispiel:

import duckdb
res = duckdb.execute("""
SELECT
$my_param,
$other_param,
$also_param
""",
{
"my_param": 5,
"other_param": "DuckDB",
"also_param": [42]
}
).fetchall()
print(res)
[(5, 'DuckDB', [42])]