tva
← Insights

DuckDB für Ad-hoc-Datenanalysen: Tausende CSV-Dateien in ein Dashboard verwandeln

Amazon Seller Central erzeugt Daten schneller, als die Werkzeuge der meisten Analysten sie verarbeiten können. Berichte zu Besucherzahlen, Conversion-Daten, Übersichten zum Bestandszustand, Exporte zur Geschäftsentwicklung und Auswertungen von Werbekampagnen – alles wird als CSV- oder TSV-Datei bereitgestellt. Über Monate und Jahre entstehen so Verzeichnisse mit Tausenden Dateien. Üblicherweise wird alles in eine Datenbank importiert. Doch eine PostgreSQL-Instanz für eine vorwiegend lesende Datenanalyse mit wechselndem Schema einzurichten, zu betreiben und abzufragen, verursacht mehr Betriebsaufwand, als das eigentliche Problem rechtfertigt.

Wir bauten eine Berichtspipeline für einen Amazon-Verkäufer auf fünf europäischen Marktplätzen. Die Rohdaten: sechs Jahre Seller-Central-Exporte, 4,200 Dateien mit Geschäftsberichten, Werbeleistungsdaten, Bestandsmomentaufnahmen und FBA-Gebührenaufstellungen. Der erste Ansatz – ein PostgreSQL-Schema mit ETL-Skripten – benötigte drei Wochen, um stabil zu laufen, und erforderte ständige Pflege, da Amazon Spaltennamen und Berichtsstrukturen zwischen API-Versionen änderte. Die Abfragen waren schnell. Die Wartung war es nicht.

DuckDB verändert diese Abwägung. Es ist eine eingebettete analytische Datenbank: Sie läuft im selben Prozess wie Ihr Analysecode, benötigt weder Server noch Daemon oder Verbindungszeichenfolgen und liest direkt aus Dateien auf dem Datenträger. Die Abfrageschnittstelle ist Standard-SQL. Die Leistung entspricht einer spaltenorientierten Engine, die für Aggregationen großer Datenmengen optimiert ist. Für diese Art von Aufgabe ist DuckDB nicht nur ausreichend, sondern das richtige Werkzeug.

Was DuckDB eigentlich ist

Der wichtigste Unterschied ist die spaltenorientierte gegenüber der zeilenorientierten Speicherung. PostgreSQL organisiert Daten in Zeilen: Alle Felder eines Datensatzes liegen zusammenhängend auf dem Datenträger. Bei SELECT revenue, units_sold FROM orders WHERE marketplace = 'DE' liest ein sequenzieller Scan jedes Feld jeder besuchten Zeile, verwirft die nicht angeforderten und liefert die beiden gewünschten zurück. Indizes, einschließlich Index-only-Scans, können den Leseumfang begrenzen, helfen aber selten bei umfassenden Aggregationen über den Großteil einer Tabelle. Das ist für transaktionale Aufgaben effizient: einzelne Datensätze einfügen, ändern und abrufen. Für analytische Abfragen, die Millionen Zeilen aggregieren und nur wenige Spalten benötigen, ist es weitgehend ungeeignet.

DuckDB organisiert Daten nach Spalten. Alle Werte von revenue liegen zusammen, ebenso alle Werte von units_sold. Dieselbe Abfrage liest nur die drei benötigten Spalten (die beiden ausgegebenen sowie marketplace für den Filter) und überspringt den Rest vollständig. Bei einem Datensatz mit vierzig Spalten ist das keine kleine Optimierung: Bei Abfragen, die nur wenige Spalten berühren, kann der Ein-/Ausgabeaufwand um etwa 90 Prozent oder mehr sinken. Zusammen mit vektorisierter Ausführung – der Verarbeitung von standardmäßig jeweils 2.048 Werten mit SIMD-CPU-Befehlen statt einer Zeile nach der anderen – kann DuckDB bei analytischen Aufgaben eine höhere Abfrageleistung als eine gut optimierte PostgreSQL-Instanz auf identischer Hardware erreichen, insbesondere bei spaltenorientierten Formaten wie Parquet.

Ebenso wichtig ist die eingebettete Architektur. Es gibt keine Hin- und Rückübertragung über das Netzwerk, keinen Authentifizierungsaufwand und keinen Verbindungspool zu verwalten. DuckDB öffnet eine Datei oder ein Verzeichnis, erstellt einen Abfrageplan und führt ihn aus. Die Binärdatei ist kleiner als 100 MB. Sie läuft auf einem Laptop, in einer Lambda-Funktion oder in einem Python-Skript, das ein Cronjob aufruft. Für selbst betriebene Analysepipelines geht der Betriebsaufwand gegen null.

SQL direkt auf Dateien

Für Seller-Central-Daten ist besonders das Einlesen von Dateien über Glob-Muster praktisch. Eine einzige SQL-Anweisung fragt Tausende CSV-Dateien gleichzeitig ab:

SELECT
  date_trunc('month', "Date") AS month,
  SUM("Units Ordered")          AS units,
  SUM("Ordered Product Sales")  AS revenue
FROM read_csv_auto('/data/seller-central/business-reports/*.csv')
GROUP BY 1
ORDER BY 1;

read_csv_auto leitet das Schema aus den Dateiinhalten ab: Es prüft Stichproben von Zeilen, erkennt Spaltentypen, verarbeitet unterschiedliche Trennzeichen und vereinigt mit union_by_name=true Spalten anhand ihrer Namen, wenn sich die Spaltenbestände der Dateien unterscheiden (standardmäßig übernimmt es das Schema der ersten Datei und ordnet spätere Dateien nach Spaltenposition zu). Umbenannte Spalten gleicht es nicht selbstständig ab; dafür ist weiterhin ein Alias oder COALESCE in der Abfrage nötig. Bei Seller-Central-Daten ist das wichtig, denn Amazon hat Spaltennamen in den vergangenen drei Jahren mindestens viermal geändert. Die Abfrage greift auf die Dateien zu, die zum Ausführungszeitpunkt im Verzeichnis liegen – ohne Import, Schemamigration oder veralteten Cache, der invalidiert werden müsste.

Für TSV-Exporte aus der Werbekonsole gilt derselbe Ansatz mit einer ausdrücklichen Formatangabe:

FROM read_csv('/data/advertising/*.tsv', delim='	', header=true)

Das ist mehr als eine Komfortfunktion. Es ist ein anderes Modell für die Arbeit mit dateibasierten Daten: Das Dateisystem wird zur Datenbank. Das Schema wird abgeleitet statt deklariert. Abfragen lassen sich ohne Vorverarbeitung über Dateigrenzen hinweg kombinieren. Für Ad-hoc-Analysen, bei denen sich die Fragestellung schneller ändert, als eine ETL-Pipeline angepasst werden kann, ist diese Architektur die richtige Standardwahl.

Leistungseigenschaften

Bei unserem Referenzdatensatz – 4,200 Dateien, etwa 2.8 GB unkomprimiert – dauert eine vollständige Umsatzaggregation nach Marktplatz, ASIN und Monat auf einem gewöhnlichen Laptop (Apple M2, 16 GB RAM) 4.1 Sekunden. Die entsprechende PostgreSQL-Abfrage auf importierten und indizierten Daten dauert 2.8 Sekunden. Dieser Vergleich lässt jedoch den vierzigminütigen Erstimport, die bei jeder Änderung eines Amazon-Spaltennamens nötigen Schemamigrationen und die laufende ETL-Wartung außer Acht. Bei den gesamten Betriebskosten gewinnt DuckDB für diese Aufgabe deutlich.

Wird die Abfrageleistung zum Engpass – typischerweise bei mehreren Terabyte –, kann DuckDB Parquet nativ lesen. Die Umwandlung vorhandener CSV-Dateien in Parquet ist ein einmaliger Vorgang:

COPY (FROM read_csv_auto('/data/**/*.csv'))
TO '/data/consolidated.parquet'
(FORMAT PARQUET);

Spätere Abfragen der Parquet-Datei laufen 8 bis 12 Mal schneller als Abfragen der ursprünglichen CSV-Dateien. Parquet speichert Spaltenstatistiken – Mindest- und Höchstwerte je Zeilengruppe –, mit denen DuckDB ganze Datenbereiche überspringt, ohne sie zu lesen. Ein Filter auf marketplace = 'DE' kann bei einer sortierten Parquet-Datei weniger als zehn Prozent der gespeicherten Daten lesen. Diese frühzeitige Anwendung von Filterbedingungen ist bei einfachen CSV-Dateien nicht möglich.

Ein eigenständiges Dashboard mit Chart.js aufbauen

DuckDB-Abfragen liefern Daten. Die praktische Frage ist, wie sie genutzt werden. Für die interne Berichterstellung kleiner bis mittlerer Amazon-Geschäfte ist eine eigenständige HTML-Datei mit Chart.js oft nützlicher als ein gehostetes Dashboard: kein Serverbetrieb, keine Zugangsdatenverwaltung und keine Superset- oder Metabase-Instanz, die gewartet werden muss.

Die Pipeline besteht aus drei Schritten. Zuerst exportiert DuckDB Abfrageergebnisse als JSON:

COPY (
  SELECT
    strftime(date_trunc('week', "Date"), '%Y-%m-%d') AS week,
    SUM("Units Ordered")         AS units,
    SUM("Ordered Product Sales") AS revenue
  FROM read_csv_auto('/data/**/*.csv')
  GROUP BY 1 ORDER BY 1
) TO 'weekly.json' (FORMAT JSON, ARRAY true);

Zweitens liest ein Python-Skript diese JSON-Dateien und bettet die Daten direkt in eine HTML-Vorlage ein, die Chart.js über ein CDN einbindet. Drittens wird die HTML-Datei ohne Webserver im Browser geöffnet.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Seller Dashboard</title>
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
  <canvas id="revenue"></canvas>
  <script>
    const rows = /* JSON_DATA_HERE */;
    new Chart(document.getElementById('revenue'), {
      type: 'line',
      data: {
        labels: rows.map(r => r.week),
        datasets: [{ label: 'Revenue', data: rows.map(r => r.revenue), tension: 0.3 }]
      }
    });
  </script>
</body>
</html>

Das Ergebnis ist eine einzelne HTML-Datei (sie lädt Chart.js von einem CDN, sodass zum Anzeigen eine Internetverbindung nötig ist, sofern Sie die Bibliothek nicht direkt einbetten), die per E-Mail geteilt, in einem privaten S3-Bucket abgelegt oder direkt aus einem lokalen Verzeichnis geöffnet werden kann. Für operative Teams, die wöchentliche Berichte ohne den Aufwand einer laufenden Dashboard-Infrastruktur benötigen, ist dieser Ansatz pragmatisch und wartbar. Ein Shell-Skript erzeugt das Dashboard neu: DuckDB fragt die CSV-Dateien ab, Python erstellt das HTML, fertig. Keine Serverinfrastruktur, keine laufenden Hostingkosten und ein geringer Wartungsaufwand: die Abfragen und das Vorlagenskript.

Wann DuckDB PostgreSQL übertrifft

DuckDB ersetzt PostgreSQL nicht. Der Vergleich ist nur sinnvoll, wenn die Aufgaben tatsächlich analytisch sind – große Datenbestände durchlaufen, aggregieren und gruppieren – und die Daten vorwiegend gelesen statt geschrieben werden. Bei gleichzeitigen Schreibzugriffen, referenzieller Integrität, Zugriffskontrolle auf Zeilenebene und den umfassenden Betriebsanforderungen einer produktiven Anwendung ist PostgreSQL klar überlegen. Es ist die richtige Wahl für ein Mehrbenutzersystem, in dem Anwendungscode Datensätze einfügt, ändert und abfragt.

DuckDB ist überlegen, wenn:

  • die Daten bereits als Dateien auf dem Datenträger vorliegen und kein Import geplant ist;
  • das Schema im Voraus nicht vollständig bekannt ist oder häufig wechselt;
  • Abfragen spontan entstehen und Zugriffsmuster nicht vorhersehbar sind;
  • Laufzeit oder Größe des Projekts den Betriebsaufwand eines Datenbankservers nicht rechtfertigen.

Amazon-Seller-Central-Daten erfüllen alle vier Kriterien genau. Das Schema ändert sich mit jeder Seller-Central-Version. Nach dem Export liegen die Dateien bereits lokal vor. Die geschäftlichen Fragen ändern sich von Woche zu Woche, je nachdem, was optimiert wird. Und die Alternative – eine gepflegte PostgreSQL-ETL-Pipeline – bedeutet Wochen Entwicklungsarbeit für ein Problem, das DuckDB an einem Nachmittag löst.

Die praktische Regel: Wenn Sie diese Daten normalerweise mit Excel oder einem Jupyter-Notebook analysieren würden, verwenden Sie stattdessen DuckDB. SQL ist ausdrucksstärker, die Leistung ist bei mehr als wenigen Tausend Zeilen in der Regel deutlich besser, und dank der dateibasierten Architektur bleiben Ihre Daten dort, wo sie bereits liegen.

Weiterführende Beiträge

Weiterführende Beiträge