Was ist DuckDB? Das ist gar nicht so einfach zu beantworten. Schauen wir auf die Homepage unter duckdb.org, so finden wir hier, dass es sich bei DuckDB um eine „fast in process database“ handelt. Was das bedeutet, werden wir weiter unten direkt erfahren.
Doch DuckDB kann noch mehr – durch seine Leichtgewichtigkeit erlaubt es Personen, die mit SQL vertraut sind, die verschiedensten Datenquellen anzuzapfen und zu manipulieren, ohne die Daten vorher mühevoll aufzubereiten oder zu importieren. Dabei können nicht nur Datenanalysten, sondern auch Entwickler und Administratoren von den Möglichkeiten profitieren.
Installation
Die Installation in 20 Sekunden ist Wirklichkeit! Man muss lediglich die entsprechende ZIP-Datei von der Homepage herunterladen und entpacken. Aber man sollte sich nicht wundern, dass das ganze ZIP nur eine Datei enthält – mehr Binaries braucht kein Mensch! Listing 1 zeigt die Installation:
(base) ronzon@linux:~> mkdir duckdb
(base) ronzon@linux:~> cd duckdb/
(base) ronzon@linux:~/duckdb> wget https://github.com/duckdb/duckdb/releases/download/v1.1.3/duckdb_cli-linux-amd64.zip
2024-11-12 20:22:42 (801 KB/s) - »duckdb_cli-linux-amd64.zip« gespeichert [16736757/16736757]
(base) ronzon@linux:~/duckdb> unzip duckdb_cli-linux-amd64.zip
Archive: duckdb_cli-linux-amd64.zip
inflating: duckdb
(base) ronzon@linux:~/duckdb> ll duckdb
-rwxr-xr-x 1 ronzon users 49631968 4. Nov 10:01 duckdb
Erste Schritte
Gestartet wird das Ganze nun durch den Aufruf ./duckdb.
Wir wollen nun Daten aus einer CSV-Datei lesen, die ziemlich einfach aufgebaut ist.
FlightDate |UniqueCarrier |OriginCityName |DestCityName
1988-01-01 |AA |New York, NY |Los Angeles, CA
1988-01-02 |AB |New York, NY |Los Angeles, CA
1988-01-03 |AC |New York, NY |Los Angeles, CA
Wie man an Listing 2 leicht erkennen kann, haben wir hier vier Spalten, wobei die erste eine Datumsspalte ist und die anderen drei Strings enthalten. Möchte ich diese per SQL auswerten, so genügt der in Abbildung 1 gezeigte Aufruf.

Abb. 1: Das erste Select
Habe ich aber nun eine zweite CSV-Datei, die zum Beispiel die Carrier enthält, kann ich diese nicht nur wie gezeigt selektieren, sondern auch mit der ersten Datei verbinden, indem ich einfach einen Join in mein Statement einbaue, siehe Listing 3. Das Ergebnis zeigt Abbildung 2.
UniqueCarrier |Name
AA |Carrier 1
AB |Carrier 2
AC |Carrier 3

Abb. 2: Joinen von CSV-Dateien
Das ist ja schon einmal cool – oder? Ohne Import der Daten in eine (temporäre) Datenbank kann ich plötzlich SQL-Operatoren auf CSV-Dateien loslassen.
Temporäre Tabellen
Aber gerade, wenn man mit Daten „herumspielt“, möchte man seine Ergebnisse vielleicht temporär speichern oder mit Synonymen versehen, sodass man leichter wieder darauf zugreifen kann. Auch das ist möglich, indem man einfach die Abfrage benutzt, um eine temporäre Tabelle damit zu erstellen:
CREATE TABLE fluege AS SELECT a.FlightDate,b.Name, a.OriginCityName
FROM read_csv('flights.csv') a, read_csv('carrier.csv') b
WHERE a.UniqueCarrier=b.UniqueCarrier;
Aber spätestens nach ein paar von diesen Tabellen weiß zumindest ich nicht mehr, welche ich denn erstellt habe. Doch wozu haben wir DuckDB – fragen wir die Datenbank. Der Aufruf:
SHOW TABLES;
hilft uns hierbei. Wenn wir dann noch wissen wollen, wie diese aufgebaut sind, können wir auch hier Hilfe erhalten. Der Aufruf:
DESCRIBE fluege;
liefert und das Ergebnis in Abbildung 3.

Abb. 3: Die Beschreibung unserer temporären Tabellen
Und um das Ganze abzurunden, können wir diese Tabellen ganz SQL-like natürlich auch mittels:
DROP TABLE fluege
wieder löschen.
Die Persistierung unserer Arbeit
Nun möchten wir in der Regel nicht, dass unsere Arbeit verloren ist, sobald wir den Computer ausschalten. Hier hilft es, die Daten ins Dateisystem zu exportieren.
Tipp: Lege vorher ein Verzeichnis dafür an!
Auch dies ist ganz einfach:
EXPORT DATABASE './MeineDuckDB';
exportiert alles in das Verzeichnis „MeineDuckDB“. Und wie erwartet, kann ich diese dann in einer neuen Sitzung wieder mittels:
IMPORT DATABASE './MeineDuckDB';
importieren. Aber schauen wir uns einmal an, was genau bei der Exportierung der Daten passiert. Diese erfolgt nämlich nicht wie erwartet binär, sondern im Klartext:
(base) ronzon@localhost:~/projekte/duckdb/MeineDuckDB> ls -l
insgesamt 12
-rw-r--r-- 1 ronzon users 139 21. Nov 10:33 fluege.csv
-rw-r--r-- 1 ronzon users 96 21. Nov 10:33 load.sql
-rw-r--r-- 1 ronzon users 85 21. Nov 10:33 schema.sql
Da sind zunächst einmal die Daten meiner temporären Tabelle fluege
:
(base) ronzon@localhost:~/projekte/duckdb/MeineDuckDB> cat fluege.csv
FlightDate,Name,OriginCityName
1988-01-01,Carrier 1,"New York, NY"
1988-01-02,Carrier 2,"New York, NY"
1988-01-03,Carrier 3,"New York, NY"
Aber auch die Beschreibung, wie diese Tabelle aufgebaut ist:
(base) ronzon@localhost:~/projekte/duckdb/MeineDuckDB> cat schema.sql
CREATE TABLE fluege(FlightDate DATE, "Name" VARCHAR, OriginCityName VARCHAR);
Und die Information, wie die Daten wieder in die Datenbank importiert werden können:
(base) ronzon@localhost:~/projekte/duckdb/MeineDuckDB> cat load.sql
COPY fluege FROM './MeineDuckDB/fluege.csv' (FORMAT 'csv', quote '"', delimiter ',', header 1);
All diese Informationen kann man natürlich auch verwenden, wenn man die Daten nach der Aufbereitung mittels DuckDB weiter verarbeiten möchte.
Die Selektion anderer Dateitypen
Das oben Gezeigte geht natürlich nicht nur mit CSV-Dateien. DuckDB unterstützt noch eine ganze Reihe weitere Dateitypen wie json-Dateien. Man muss nur die Funktion read_json() verwenden. Genaueres findet sich hier: duckdb.org/docs/data/json/overview.html.
Klein, aber erweiterbar: Extensions
Was aber, wenn die Daten nicht lokal auf dem Rechner liegen, sondern irgendwo im Internet. Hierzu muss ich etwas ausholen: Natürlich kann DuckDB nicht alle Funktionalitäten in ein winziges Binary pressen. Aus diesem Grund kennt es das Prinzip der Extensions. Listing 4 zeigt, welche ich in meiner Installation bereits installiert habe.
D SELECT extension_name, loaded, installed
from duckdb_extensions()
ORDER BY installed DESC, loaded DESC;
┌──────────────────┬─────────┬───────────┐
│ extension_name │ loaded │ installed │
│ varchar │ boolean │ boolean │
├──────────────────┼─────────┼───────────┤
│ autocomplete │ true │ true │
│ fts │ true │ true │
│ icu │ true │ true │
│ jemalloc │ true │ true │
│ json │ true │ true │
│ parquet │ true │ true │
│ shell │ true │ true │
│ tpch │ true │ true │
│ arrow │ false │ false │
│ aws │ false │ false │
│ azure │ false │ false │
│ delta │ false │ false │
│ excel │ false │ false │
│ httpfs │ false │ false │
│ iceberg │ false │ false │
│ inet │ false │ false │
│ motherduck │ false │ false │
│ mysql_scanner │ false │ false │
│ postgres_scanner │ false │ false │
│ spatial │ false │ false │
│ sqlite_scanner │ false │ false │
│ substrait │ false │ false │
│ tpcds │ false │ false │
│ vss │ false │ false │
├──────────────────┴─────────┴───────────┤
│ 24 rows 3 columns │
└────────────────────────────────────────┘
Es gibt hier also eine https-Extension, die den Zugriff per https auf Server erlaubt. Installieren wir diese einmal wie in Abbildung 4.

Abb. 4: Installation einer Extension
Nun können wir auch auf entfernte Dateien zugreifen, siehe Listing 5.
D SELECT * FROM 'https://oedi-data-lake.s3.amazonaws.com/pvdaq/csv/systems.csv';
┌───────────┬──────────────────────┬─────────┬──────────────────────┬───┬───────────────┬────────────────┬────────────────┐
│ system_id │ system_public_name │ site_id │ site_public_name │ … │ site_latitude │ site_longitude │ site_elevation │
│ int64 │ varchar │ int64 │ varchar │ │ double │ double │ double │
├───────────┼──────────────────────┼─────────┼──────────────────────┼───┼───────────────┼────────────────┼────────────────┤
│ 4902 │ NIST_Ground_1 │ 3197 │ s_NIST_G1 │ … │ 39.1319 │ -77.2141 │ 138.0 │
│ 4903 │ NIST_Roof_1 │ 3198 │ s_NIST_R1 │ … │ 39.1354 │ -77.2156 │ 149.0 │
│ 4901 │ NIST_Canopy_1 │ 3196 │ s_NIST_C1 │ … │ 39.1385 │ -77.2155 │ 137.0 │
│ 2 │ Residential 1a │ 1 │ Residential 1 │ … │ 39.7214 │ -105.0972 │ 1675.0 │
│ 50 │ NREL x-Si 6 │ 3 │ SERF East │ … │ 39.742 │ -105.1727 │ 1994.7 │
│ 1332 │ NREL Parking Garage │ 5 │ NREL Parking Garage │ … │ 39.7388 │ -105.1732 │ 1770.0 │
│ 33 │ Silicor Materials │ 11 │ NREL OTF Silicor │ … │ 39.7404 │ -105.1772 │ 1794.0 │
│ 3 │ Residential 1b │ 1 │ Residential 1 │ … │ 39.7214 │ -105.0972 │ 1675.0 │
│ 3 │ Residential 1b │ 1 │ Residential 1 │ … │ 39.7214 │ -105.0972 │ 1675.0 │
│ 51 │ NREL x-Si 7 │ 4 │ SERF West │ … │ 39.7416 │ -105.1734 │ 1994.7 │
│ 1208 │ NREL Visitors Park… │ 9 │ NREL Visitors Park… │ … │ 39.7407 │ -105.1694 │ 1780.0 │
│ 2 │ Residential 1a │ 1 │ Residential 1 │ … │ 39.7214 │ -105.0972 │ 1675.0 │
│ 1302 │ [1302] St. Petersb… │ 1284 │ [1302] St. Petersb… │ … │ 27.7743 │ -82.6423 │ 5.0 │
│ 1296 │ [1296] St. Petersb… │ 1278 │ [1296] St. Petersb… │ … │ 27.7427 │ -82.663 │ 5.0 │
│ 1432 │ [1432] NREL S and TF │ 1400 │ [1432] NREL S and TF │ … │ 39.7422 │ -105.1719 │ 1780.0 │
│ 1423 │ [1423] RTC-NV-Base… │ 1393 │ [1423] RTC-NV-Base… │ … │ 36.0275 │ -114.9215 │ 697.0 │
│ 1289 │ [1289] NREL CIGS-12 │ 1273 │ [1289] NREL CIGS-12 │ … │ 39.7405 │ -105.1774 │ 1793.7 │
│ 1430 │ [1430] NREL Mesa 1… │ 1398 │ [1430] NREL Mesa 1… │ … │ 39.7438 │ -105.1779 │ 1831.0 │
│ 36 │ [36] NREL low-X x-… │ 42 │ [36] NREL low-X x-… │ … │ 39.704 │ -105.1773 │ 1794.7 │
│ 1256 │ Residential - NOLA… │ 1244 │ Residential - NOLA… │ … │ 29.9288 │ -90.1273 │ 10.0 │
│ · │ · │ · │ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │ · │ · │ · │
│ 1231 │ [1231] FSEC - Zero… │ 1221 │ [1231] FSEC - Zero… │ … │ 29.0258 │ -80.9274 │ 2.0 │
│ 1273 │ Residential - NOLA… │ 1261 │ Residential - NOLA… │ … │ 29.9288 │ -90.1273 │ 10.0 │
│ 1338 │ Residential - NOLA… │ 1319 │ Residential - NOLA… │ … │ 29.951 │ -90.0812 │ 10.0 │
│ 1229 │ [1229] FSEC - Zero… │ 1219 │ [1229] FSEC - Zero… │ … │ 28.0392 │ -81.95 │ 58.0 │
│ 1266 │ Residential - NOLA… │ 1254 │ Residential - NOLA… │ … │ 29.9809 │ -90.0023 │ 10.0 │
│ 1304 │ [1304] St. Petersb… │ 1286 │ [1304] St. Petersb… │ … │ 27.7589 │ -82.67 │ 5.0 │
│ 1225 │ [1225] Mercury Sol… │ 1216 │ [1225] Mercury Sol… │ … │ 40.995 │ -73.668 │ 75.0 │
│ 1226 │ [1226] Mercury Sol… │ 1217 │ [1226] Mercury Sol… │ … │ 40.995 │ -73.668 │ 75.0 │
│ 1433 │ [1433] NREL RSF1 │ 1401 │ [1433] NREL RSF1 │ … │ 39.7404 │ -105.1719 │ 1780.0 │
│ 1431 │ [1431] NREL Windsi… │ 1399 │ [1431] NREL Windsi… │ … │ 39.9094 │ -105.2311 │ 1850.0 │
│ 1364 │ Residential - NOLA… │ 1345 │ Residential - NOLA… │ … │ 30.0017 │ -90.0926 │ 10.0 │
│ 1346 │ Residential - NOLA… │ 1327 │ Residential - NOLA… │ … │ 29.9288 │ -90.1273 │ 10.0 │
│ 1315 │ [1315] St. Petersb… │ 1297 │ [1315] St. Petersb… │ … │ 27.7427 │ -82.663 │ 5.0 │
│ 1201 │ [1201] Distributed… │ 1199 │ [1201] Distributed… │ … │ 39.9283 │ -75.0481 │ 18.0 │
│ 1320 │ [1320] St. Petersb… │ 1302 │ [1320] St. Petersb… │ … │ 27.714 │ -82.6525 │ 5.0 │
│ 1359 │ Residential - NOLA… │ 1340 │ Residential - NOLA… │ … │ 29.9809 │ -90.0023 │ 10.0 │
│ 1244 │ [1244] Residential… │ 1232 │ [1244] Residential… │ … │ 30.0017 │ -90.0926 │ 10.0 │
│ 1419 │ [1419] Clark Count… │ 1390 │ [1419] Clark Count… │ … │ 36.1534 │ -115.0256 │ 779.0 │
│ 34 │ [34] Andre Agassi … │ 40 │ [34] Andre Agassi … │ … │ 36.1952 │ -115.1582 │ 620.0 │
│ 1294 │ [1294] St. Petersb… │ 1276 │ [1294] St. Petersb… │ … │ 27.7374 │ -82.6311 │ 5.0 │
├───────────┴──────────────────────┴─────────┴──────────────────────┴───┴───────────────┴────────────────┴────────────────┤
│ 158 rows (40 shown) 8 columns (7 shown) │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
D
Tipps aus der Praxis
Hier die versprochenen Tipps.
Logfileanalyse mit DuckDB
Wer kennt das Problem nicht: Man sucht einen Fehler und muss dazu nicht nur verschiedene Logfiles durchsuchen, sondern möchte am liebsten wissen, was zu einem bestimmten Zeitpunkt auf verschiedenen Rechnern passiert ist. Dabei liegen die Logfiles in der Regel als Textdateien ohne feste Breite vor. Oft haben diese auch noch unterschiedliche Formate, sodass es gar nicht so einfach ist, diese zu joinen – es sei denn man hat DuckDB.
Listing 6 zeigt ein Beispiel aus einem Linux-Kernel-Logfile (/var/log/messages).
2024-10-24T21:03:14.495846+02:00 localhost systemd[1]: Starting Network Manager Script Dispatcher Service...
2024-10-24T21:03:14.503483+02:00 localhost dbus-daemon[1188]: [system] Successfully activated service 'org.freedesktop.nm_dispatcher'
2024-10-24T21:03:14.504326+02:00 localhost systemd[1]: Started Network Manager Script Dispatcher Service.
2024-10-24T21:03:14.633802+02:00 localhost systemd[1]: Reloading Automounts filesystems on demand...
2024-10-24T21:03:14.635545+02:00 localhost systemd[1]: Reloaded Automounts filesystems on demand.
2024-10-24T21:03:14.657531+02:00 localhost dns-dnsmasq.sh[15389]: <debug> NETWORKMANAGER_DNS_FORWARDER is not set to "dnsmasq" in /etc/sysconfig/network/config -> exit
2024-10-24T21:03:14.666663+02:00 localhost systemd[1]: tmp-auto8snUxE.mount: Deactivated successfully.
Leider scheint es bisher keine Extension zu geben, die es erlaubt, quasi „tokenbasierte“ Dateien zu lesen, wie man diesem Foren-Eintrag entnehmen kann [1]. Nun widerspricht es vielleicht ein wenig der Philosophie von DuckDB, die Daten vorher aufzubereiten, aber „was soll's“ …
Als Unix-Veteran nehme ich natürlich awk – aber erlaubt ist natürlich, was gefällt:
cat messages | awk {'print NR"|"$1"|"$2"|"$3"|"substr($0,index($0,$4))"|"'} > messages.csv
Und so habe ich eine CSV-Datei, die ich wieder wie oben beschrieben auswerten kann, siehe Listing 7.
11250|2024-10-24T21:03:14.495846+02:00|localhost|systemd[1]:|Starting Network Manager Script Dispatcher Service...|
11251|2024-10-24T21:03:14.503483+02:00|localhost|dbus-daemon[1188]:|[system] Successfully activated service 'org.freedesktop.nm_dispatcher'|
11252|2024-10-24T21:03:14.504326+02:00|localhost|systemd[1]:|Started Network Manager Script Dispatcher Service.|
11253|2024-10-24T21:03:14.633802+02:00|localhost|systemd[1]:|Reloading Automounts filesystems on demand...|
11254|2024-10-24T21:03:14.635545+02:00|localhost|systemd[1]:|Reloaded Automounts filesystems on demand.|
11255|2024-10-24T21:03:14.657531+02:00|localhost|dns-dnsmasq.sh[15389]:|<debug> NETWORKMANAGER_DNS_FORWARDER is not set to "dnsmasq" in /etc/sysconfig/network/config -> exit|
11256|2024-10-24T21:03:14.666663+02:00|localhost|systemd[1]:|tmp-auto8snUxE.mount: Deactivated successfully.|
Aber das ist gar nicht das Besondere. Es ist, dass das zweite Feld ein Datumsfeld ist. Wenn ich nun zwei verschiedene Rechner habe, die nicht per Zeitserver synchronisiert sind, so muss ich nur ermitteln, wie weit diese auseinanderliegen.
Durch Addieren der Differenz bei der WHERE-Klausel des Joins kann ich so diese – zumindest für die Analyse – wieder synchronisieren. Somit kann ich dann mittels SQL ermitteln, was parallel auf den Systemen los war!
Manchmal braucht man einfach einen kleinen Nummerngenerator – warum nicht DuckDB verwenden? Wie Listing 8 zeigt, werden hier ganzzahlige Nummern im Abstand von 15 bis 100 generiert.
D SELECT * FROM generate_series(0,100,15);
┌─────────────────┐
│ generate_series │
│ int64 │
├─────────────────┤
│ 0 │
│ 15 │
│ 30 │
│ 45 │
│ 60 │
│ 75 │
│ 90 │
└─────────────────┘
Wo bekomme ich Infos?
Neben der Website gibt es das Buch „DuckDB in Action“ [2]. Das bietet einen wirklich guten und tiefen Einblick in das Thema. Derzeit läuft eine Aktion, bei der das Buch unter motherduck.com kostenlos als PDF heruntergeladen werden kann.
Kleinere Tipps und Snipplets zum Thema DuckDB finden sich unter duckdbsnippets.com.
Fazit
Dass ich fasziniert von DuckDB bin, merkt man sicherlich, denn mir ist in den letzten Jahren kein anderes Tool untergekommen, das so klein, so praktisch, so mächtig und so gut dokumentiert ist. Probiert es besser nicht aus: DuckDB macht süchtig. Je mehr man sich damit beschäftigt, desto mehr Einsatzmöglichkeiten findet man.
Literaturhinweise
[1] S. Zimmer, Feature Request: Read/Import Fixed Width Files #13777, github.com/duckdb/duckdb/discussions/13777
[2] M. Needham, M. Hunger, M. Simons, DuckDB in Action, Manning Publications, 2024