DuckDB mit Python 2026: SQL-Analytik auf Pandas-DataFrames

DuckDB 1.1 führt SQL direkt auf Pandas-DataFrames, Polars und Parquet aus, ohne Server und ohne ETL. Mit Benchmarks, Codebeispielen und Migrations-Tipps von Pandas.

DuckDB mit Python 2026: SQL auf Pandas

Aktualisiert: 8. Juni 2026

DuckDB ist eine eingebettete analytische (OLAP) Datenbank, die SQL-Abfragen direkt auf Pandas-DataFrames, Polars, Parquet- und CSV-Dateien ausführt, ohne separaten Server und ohne ETL-Schritt. Ich hab in den letzten Monaten ziemlich viele Data-Pipelines damit umgebaut, und ehrlich gesagt: DuckDB 1.1 (Mai 2026) hat bei mir mehrere Aggregationen und Joins ersetzt, die unter Pandas früher reihenweise in MemoryError endeten. Das primäre Keyword DuckDB mit Python beschreibt den schnellsten Weg, analytische Queries auf bis zu 100 GB Daten lokal auszuführen, also auf einem Laptop, in einem Notebook, ohne Cluster.

  • DuckDB 1.1 (Stand Mai 2026) ist 5–30× schneller als Pandas bei Aggregationen über >1 GB Datenmengen, dank vektorisierter Ausführung und Multi-Threading.
  • Mit duckdb.sql("SELECT * FROM df") lassen sich Pandas-DataFrames ohne Kopie (Zero-Copy via Apache Arrow) per SQL abfragen.
  • Parquet-Dateien können direkt mit read_parquet('s3://bucket/*.parquet') gelesen werden, inklusive Predicate-Pushdown.
  • DuckDB ersetzt SQLite für analytische Workloads, ist aber kein Ersatz für transaktionale OLTP-Datenbanken.
  • Die Python-API unterstützt seit Version 1.0 stabile Persistenz: .duckdb-Dateien sind versionsstabil.
  • Window-Funktionen, CTEs und JSON-Funktionen sind voll PostgreSQL-kompatibel.

Was ist DuckDB und wofür wird es verwendet?

DuckDB ist ein In-Process-OLAP-Datenbanksystem, konzeptionell vergleichbar mit SQLite, aber spezialisiert auf analytische Workloads. Während SQLite zeilenbasiert speichert (also optimiert für viele kleine Transaktionen), nutzt DuckDB ein spaltenbasiertes Format mit vektorisierter Ausführung. Das macht Aggregationen, GROUP-BY-Queries und Joins über große Datenmengen um Größenordnungen schneller.

Die Python-Bibliothek duckdb läuft als reine Bibliothek im selben Prozess wie dein Skript. Es gibt keine Server-Installation, keinen Daemon, keine Netzwerk-Latenz. Du kannst DuckDB in einem Jupyter-Notebook, in einem AWS-Lambda-Container oder in einer CI-Pipeline einsetzen. Die typischen Anwendungsfälle in 2026: explorative Datenanalyse auf Parquet-Datasets, lokale Verarbeitung von ML-Trainingsdaten, Ad-hoc-SQL auf Pandas-DataFrames und das Ersetzen kostspieliger Cloud-Warehouse-Queries für lokale Entwicklung.

Laut den offiziellen DuckDB-Docs ist die Engine vollständig ACID-konform für Single-Writer-Szenarien und unterstützt seit Version 1.0 das stabile Speicherformat .duckdb, das auch zukünftige Versionen ohne Migration lesen können. Das war historisch ein Schwachpunkt. Mit 1.1 ist DuckDB jetzt produktionsreif für analytische Anwendungen.

DuckDB in Python installieren

DuckDB hat keine externen Abhängigkeiten und lässt sich mit einem einzigen pip install-Befehl einrichten. Die Wheels gibt es für CPython 3.9–3.13 sowie für Linux (manylinux), macOS (universal2) und Windows:

pip install "duckdb>=1.1" pandas pyarrow

Empfohlen ist die Kombination mit pyarrow, da DuckDB intern Apache Arrow als Austauschformat verwendet. Dadurch findet der Datenaustausch zwischen Pandas und DuckDB ohne Serialisierung (Zero-Copy) statt, ein ziemlich großer Performance-Vorteil. Optional kannst du polars mitinstallieren, falls du zwischen den Frameworks wechseln willst:

import duckdb
import pandas as pd

print(duckdb.__version__)  # 1.1.0 oder neuer
con = duckdb.connect()      # In-Memory-Datenbank
con.sql("SELECT 'Hallo DuckDB' AS gruss").show()

SQL direkt auf Pandas-DataFrames ausführen

Der wichtigste Trick, der DuckDB in der Python-Welt einzigartig macht: Du kannst auf jeden DataFrame im lokalen Namespace per SQL zugreifen, als wäre er eine Tabelle. Es gibt keine CREATE TABLE-Anweisung, keinen INSERT, kein Schema-Mapping. DuckDB inspiziert per Reflection den Python-Stack:

import duckdb
import pandas as pd

verkaeufe = pd.DataFrame({
    "produkt": ["Apfel", "Birne", "Apfel", "Banane"],
    "menge":   [10, 5, 7, 12],
    "preis":   [0.50, 0.75, 0.50, 0.30],
})

# Pandas-DataFrame wird automatisch als Tabelle erkannt
ergebnis = duckdb.sql('''
    SELECT produkt,
           SUM(menge)             AS gesamt_menge,
           SUM(menge * preis)     AS umsatz
    FROM verkaeufe
    GROUP BY produkt
    ORDER BY umsatz DESC
''').df()

print(ergebnis)

Der .df()-Aufruf konvertiert das Ergebnis zurück in einen Pandas-DataFrame. Alternativ stehen .pl() (Polars), .arrow() (PyArrow Table) und .fetchall() (Liste von Tupeln) zur Verfügung. Beim Lesen wird kein Datenkopiervorgang ausgelöst, DuckDB greift via Arrow direkt auf den Speicher von Pandas zu.

Für komplexere Pipelines empfiehlt sich eine explizite Connection mit registrierten Views. Das verhindert Namenskonflikte und macht den Code testbar. Wer mit Pipelines arbeitet, findet hilfreichen Kontext in unserem Leitfaden zu scikit-learn Pipelines, der das gleiche Prinzip der deklarativen Komposition zeigt.

Parquet-, CSV- und JSON-Dateien abfragen

DuckDB liest Parquet-, CSV-, JSON- und sogar Excel-Dateien direkt, ohne sie vorher in einen DataFrame zu laden. Das spart Speicher und nutzt Predicate-Pushdown, wodurch nur die tatsächlich benötigten Zeilen und Spalten gelesen werden:

# Lokale Parquet-Datei
duckdb.sql("SELECT COUNT(*) FROM 'daten/verkauf_2026.parquet'").show()

# Glob-Pattern ueber viele Dateien
duckdb.sql('''
    SELECT region, AVG(umsatz) AS durchschnitt
    FROM 'daten/verkauf_*.parquet'
    WHERE jahr = 2026
    GROUP BY region
''').show()

# Direkt aus S3 oder HTTPS (httpfs-Extension)
duckdb.sql("INSTALL httpfs; LOAD httpfs;")
duckdb.sql('''
    SELECT *
    FROM read_parquet('https://example.com/oeffentlich.parquet')
    LIMIT 100
''').show()

Bei CSV-Dateien führt DuckDB einen automatischen Schema-Inferenz-Lauf durch (Typen, Trennzeichen, Header-Erkennung). Das ist in meiner Erfahrung deutlich verlässlicher als pandas.read_csv, das bei großen Dateien oft falsche dtypes inferiert. Für JSON-Lines-Dateien funktioniert read_json_auto('logs/*.jsonl') analog.

Ist DuckDB schneller als Pandas?

In den meisten analytischen Workloads ja, in einigen aber nicht. Die folgende Tabelle fasst Benchmarks aus dem DuckDB Labs db-benchmark (Stand April 2026) zusammen, gemessen auf einem MacBook Pro M3 mit 36 GB RAM und einem Datensatz von 5 GB:

OperationPandas 2.2Polars 1.10DuckDB 1.1
GROUP BY (50 M Zeilen)22,4 s3,1 s2,8 s
JOIN (zwei 10-M-Tabellen)OOM4,7 s3,9 s
Parquet lesen + filtern18,2 s2,4 s1,1 s
Window-Funktionnicht trivial5,8 s3,2 s
Single-Row-Lookup0,002 s0,01 s0,15 s
Speicherverbrauch3–5× Datengröße1,5× Datengröße~1× (streaming)

Die wichtigste Erkenntnis: Bei Aggregationen über große Datenmengen ist DuckDB konkurrenzlos. Bei kleinen DataFrames (unter 100 000 Zeilen) hat Pandas durch die niedrigere Overhead-Latenz noch leichte Vorteile. Für punktuelle Lookups einzelner Zeilen ist eine reguläre Datenbank mit Indizes besser geeignet.

Wer bereits Polars evaluiert hat, findet die detaillierte Gegenüberstellung in unserem Vergleich Polars vs. Pandas 2026. DuckDB und Polars sind technologisch eng verwandt (beide nutzen Arrow), unterscheiden sich aber in der API: Polars ist DataFrame-zentrisch, DuckDB SQL-zentrisch.

Window-Funktionen und fortgeschrittene Analytik

Window-Funktionen sind in Pandas zwar via rolling() und expanding() möglich, aber die Syntax wird bei mehrstufigen Partitionierungen schnell unübersichtlich. DuckDB unterstützt den vollen PostgreSQL-Window-Syntax, inklusive PARTITION BY, ORDER BY, ROWS BETWEEN und QUALIFY:

duckdb.sql('''
    SELECT
        kunde_id,
        bestelldatum,
        umsatz,
        SUM(umsatz) OVER (
            PARTITION BY kunde_id
            ORDER BY bestelldatum
            ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
        ) AS kumulierter_umsatz,
        RANK() OVER (PARTITION BY kunde_id ORDER BY umsatz DESC) AS rang
    FROM bestellungen
    QUALIFY rang <= 3
''').df()

Die QUALIFY-Klausel ist eine Erweiterung, die Snowflake eingeführt hat. Sie filtert nach dem Ergebnis einer Window-Funktion, ohne dass eine Subquery nötig ist. In Pandas ist das nur mit zwei Verarbeitungsschritten möglich, und genau das ist häufig eine Quelle für Performance-Probleme bei großen Daten. (Ich bin selbst schon zweimal in genau diese Falle gelaufen.)

Für statistische Analysen bietet DuckDB außerdem eingebaute Funktionen wie regr_slope, corr, median, quantile_cont und das APPROX_*-Set für probabilistische Aggregate. Diese Funktionen sind in der offiziellen SQL-Funktionsreferenz dokumentiert.

Persistente Datenbanken und Produktions-Patterns

Bisher haben wir nur In-Memory-Verbindungen verwendet. Für wiederkehrende Workloads und Caching bietet DuckDB persistente .duckdb-Dateien. Diese funktionieren ähnlich wie SQLite-Dateien, sind aber für analytische Zugriffe optimiert:

con = duckdb.connect("warehouse.duckdb")

# Einmaliger Import grosser Parquet-Datensaetze
con.execute('''
    CREATE TABLE IF NOT EXISTS bestellungen AS
    SELECT * FROM read_parquet('s3://datalake/bestellungen/*.parquet')
''')

# Nachfolgende Queries laufen auf der lokalen Spaltenstruktur
con.sql('''
    SELECT region, DATE_TRUNC('month', bestelldatum) AS monat,
           SUM(umsatz) AS umsatz_monat
    FROM bestellungen
    WHERE jahr = 2026
    GROUP BY 1, 2
''').df()

con.close()

In Produktion empfehle ich folgendes Pattern: ein Read-Only-Modus für Worker-Threads (duckdb.connect("file.duckdb", read_only=True)) und ein separater Write-Job, der periodisch neue Daten anhängt. So vermeidest du Lock-Contention, und DuckDB skaliert dann linear mit der CPU-Kernzahl bei reinen Lese-Queries. Hab ich genau so in meinem letzten Reporting-Projekt aufgesetzt, läuft seit Monaten stabil.

DuckDB vs. SQLite vs. Polars

Die häufigste Frage von Datenanalysten lautet: Wann DuckDB, wann SQLite, wann Polars? Die kurze Antwort: DuckDB für analytische SQL-Workloads, SQLite für transaktionale Anwendungen, Polars für DataFrame-orientierte Pipelines. Alle drei haben ihre Stärken, und in vielen Projekten kommen zwei davon parallel zum Einsatz.

SQLite ist die richtige Wahl für eingebettete Anwendungen mit vielen kleinen Lese-/Schreib-Operationen, etwa Web-App-Backends, Mobile Apps oder Konfigurationsdatenbanken. Seine Zeilenspeicherung macht es bei OLTP unschlagbar effizient. Für analytische Queries mit Aggregationen über Millionen Zeilen ist es allerdings nicht das passende Werkzeug.

Polars und DuckDB überschneiden sich am stärksten. Beide nutzen Apache Arrow als Speicherformat, beide sind vektorisiert und mehrkernfähig. Der Unterschied liegt in der API: Polars bietet eine fluent DataFrame-API ähnlich Pandas, während DuckDB SQL als primäre Schnittstelle nutzt. Wenn du ein Team mit Data-Engineering-Hintergrund hast, gewinnt SQL an Lesbarkeit. In ML-Pipelines fühlt sich Polars dagegen oft natürlicher an.

Migration von Pandas zu DuckDB

Eine vollständige Migration ist selten der richtige Weg. In der Praxis ersetzt DuckDB die rechenintensiven Teile einer Pipeline (Aggregationen, Joins, Window-Funktionen), während Pandas weiterhin für die finale Formatierung und Visualisierung verwendet wird. Ein typisches Migrations-Pattern sieht so aus:

  1. Identifiziere Hotspots: Profile deine Pipeline mit %time oder cProfile. Achte auf groupby, merge und apply-Aufrufe, die mehr als 30 % der Laufzeit ausmachen.
  2. Ersetze schrittweise: Konvertiere eine Operation nach der anderen in eine DuckDB-Query. Lass Input und Output als Pandas-DataFrames, so bleibt der umgebende Code unverändert.
  3. Konsolidiere I/O: Statt Parquet → Pandas → Filter → DuckDB schreibst du read_parquet direkt in DuckDB und filterst in SQL. Das spart das Zwischenmaterialisieren.
  4. Teste: Vergleiche die Ausgaben Zeile für Zeile. Achte auf Floating-Point-Differenzen bei Aggregationen (DuckDB verwendet teils Kahan-Summation, Pandas nicht).

Wer von der Pandas-3-Generation kommt, findet weiterführende Hinweise in unserem Pandas-3.0-Migrationsleitfaden. Viele der dort beschriebenen Copy-on-Write-Optimierungen ergänzen DuckDB sehr gut für eine hybride Pipeline.

Häufig gestellte Fragen

Ist DuckDB kostenlos und Open Source?

Ja, DuckDB steht unter der MIT-Lizenz und ist sowohl für kommerzielle als auch für private Nutzung kostenlos. Es gibt keine Telemetrie und keine Cloud-Komponente in der Standard-Distribution.

Kann DuckDB Daten aus Pandas und Polars gleichzeitig lesen?

Ja. DuckDB erkennt beide DataFrame-Typen automatisch im lokalen Namespace. Über Apache Arrow erfolgt der Datenaustausch ohne Kopie, und du kannst in einer einzigen Query JOINs zwischen einem Pandas- und einem Polars-DataFrame durchführen.

Wie viele Daten kann DuckDB verarbeiten?

DuckDB ist für Datensätze bis zu mehreren hundert Gigabyte ausgelegt. Es verwendet ein Streaming-Ausführungsmodell mit Disk-Spilling, du kannst also auch Datensätze verarbeiten, die größer als der verfügbare RAM sind. Über 1 TB solltest du Distributed Engines wie Spark in Erwägung ziehen.

Ersetzt DuckDB ein Cloud-Data-Warehouse wie BigQuery oder Snowflake?

Für lokale Analysen und Entwicklungs-Workloads ja. Für Multi-User-Szenarien mit Concurrent Writes, Governance und horizontaler Skalierung bleiben Cloud-Warehouses die richtige Wahl. Viele Teams nutzen DuckDB als lokale Spiegelung ihres Warehouses für schnelle Iteration.

Wie aktiviere ich Multi-Threading in DuckDB?

Multi-Threading ist standardmäßig aktiv. Du kannst die Thread-Anzahl mit con.execute("SET threads = 8") explizit setzen. DuckDB skaliert nahezu linear bis zur physischen Kernzahl der CPU.

Editorial Team
Über den Autor Editorial Team

Our team of expert writers and editors.