2023-02-13
Ankündigung von DuckDB 0.7.0
Mark Raasveldt

Das DuckDB-Team freut sich, die neueste DuckDB-Version (0.7.0) bekannt zu geben. Diese Version heißt „Labradorius“ nach der Labradorente (Camptorhynchus labradorius), die in Nordamerika beheimatet war.
Zur Installation der neuen Version siehe die Installationsanleitung. Die vollständigen Release Notes finden Sie auf GitHub.
Was steckt in 0.7.0
Die neue Version enthält viele Verbesserungen der JSON-Unterstützung, neue SQL-Funktionen, Verbesserungen beim Datenimport und -export sowie weitere neue Features. Im Folgenden eine Zusammenfassung der wichtigsten Änderungen, jeweils mit den verlinkten PRs, die die Features umsetzen.
Verbesserungen beim Datenimport und -export
JSON-Import. Diese Version führt die Methoden read_json und read_json_auto ein. Damit lassen sich JSON-Dateien in ein tabellarisches Format einlesen. Analog zu read_csv verlangt read_json ein Schema, während read_json_auto das Schema der JSON-Datei per Sampling automatisch ableitet. Sowohl newline-delimited JSON als auch normales JSON werden unterstützt.
FROM 'data/json/with_list.json';| id | name |
|---|---|
| 1 | [O, Brother,, Where, Art, Thou?] |
| 2 | [Home, for, the, Holidays] |
| 3 | [The, Firm] |
| 4 | [Broadcast, News] |
| 5 | [Raising, Arizona] |
Partitionierter Parquet-/CSV-Export. DuckDB kann Hive-partitionierte Parquet- und CSV-Dateien schon länger einlesen. Ab diesem Release kann DuckDB Hive-partitionierte Daten auch schreiben, über die Klausel PARTITION_BY. Die Dateien lassen sich lokal oder remote auf S3-kompatiblen Speicher exportieren. Ein lokales Beispiel:
COPY orders TO 'orders' (FORMAT parquet, PARTITION_BY (year, month));Dadurch werden die Parquet-Dateien in folgender Verzeichnisstruktur geschrieben:
orders├── year=2021│ ├── month=1│ │ ├── file1.parquet│ │ └── file2.parquet│ └── month=2│ └── file3.parquet└── year=2022 ├── month=11 │ ├── file4.parquet │ └── file5.parquet └── month=12 └── file6.parquetParalleles Schreiben von Parquet/CSV. Das Schreiben von Parquet und CSV wird in diesem Release durch die Unterstützung paralleler Parquet- und CSV-Writer deutlich beschleunigt.
| Format | Alt | Neu (8T) |
|---|---|---|
| CSV | 2.6 s | 0.4 s |
| Parquet | 7.5 s | 1.3 s |
Hinweis: Das parallele Schreiben ist derzeit auf Fälle beschränkt, die die Einfügereihenfolge nicht erhalten – steuerbar über preserve_insertion_order = false. In einem künftigen Release wollen wir diese Einschränkung aufheben und auch paralleles Schreiben mit erhaltener Einfügereihenfolge unterstützen.
Multi-Datenbank-Unterstützung
Attach-Funktionalität. Dieses Release fügt die Möglichkeit hinzu, mehrere Datenbanken an dieselbe DuckDB-Instanz anzuhängen. So lassen sich Daten leicht zwischen getrennten DuckDB-Dateien übertragen und in einzelnen Abfragen kombinieren. Auch entfernte DuckDB-Instanzen (etwa auf einem netzwerkzugänglichen Ort wie GitHub) können angehängt werden.
ATTACH 'new_db.db';CREATE TABLE new_db.tbl (i INTEGER);INSERT INTO new_db.tbl SELECT * FROM range(1000);DETACH new_db;Siehe die Dokumentation für weitere Informationen.
SQLite-Storage-Backend. Zusätzlich zum Anhängen von DuckDB-Datenbanken unterstützt dieses Release austauschbare Datenbank-Engines. Erweiterungen können eigene Datenbank- und Katalog-Engines definieren und ans System anhängen. Einmal angehängt, kann eine Engine sowohl lesen als auch schreiben. Die SQLite-Erweiterung nutzt das, um natives Lesen und Schreiben von SQLite-Datenbankdateien in DuckDB zu ermöglichen.
ATTACH 'sqlite_file.db' AS sqlite (TYPE sqlite);CREATE TABLE sqlite.tbl (i INTEGER);INSERT INTO sqlite.tbl VALUES (1), (2), (3);SELECT * FROM sqlite.tbl;Damit lassen sich SQLite-Dateien anhängen, abfragen und ändern, als wären sie native DuckDB-Dateien. Daten können schnell zwischen SQLite und DuckDB übertragen werden – und Sie können DuckDBs reichen SQL-Dialekt auf Daten in SQLite-Tabellen anwenden.
Neue SQL-Features
Upsert-Unterstützung. Upsert kommt in diesem Release über die Klausel ON CONFLICT sowie die SQLite-kompatible Syntax INSERT OR REPLACE/INSERT OR IGNORE.
CREATE TABLE movies (id INTEGER PRIMARY KEY, name VARCHAR);INSERT INTO movies VALUES (1, 'A New Hope');FROM movies;| id | name |
|---|---|
| 1 | A New Hope |
INSERT OR REPLACE INTO movies VALUES (1, 'The Phantom Menace');FROM movies;| id | name |
|---|---|
| 1 | The Phantom Menace |
Siehe die Dokumentation für weitere Informationen.
Laterale Joins. Unterstützung für laterale Joins kommt in diesem Release. Laterale Joins sind eine flexiblere Variante korrelierter Subqueries und erleichtern die Arbeit mit verschachtelten Daten, weil sie das Entpacken vereinfachen.
Positionale Joins. SQL modelliert formal ungeordnete Mengen, in der Praxis hat die Reihenfolge von Datensätzen aber oft eine Bedeutung. DuckDB garantiert, dass die Zeilenreihenfolge beim Laden in Tabellen, beim Export in Dateien und bei Abfragen wie LIMIT ohne ORDER BY erhalten bleibt.
Für diesen Anwendungsfall führt dieses Release den POSITIONAL JOIN ein. Statt über Werte zu joinen, verknüpft dieser Join-Typ Zeilen anhand ihrer Position in der Tabelle.
CREATE TABLE t1 AS FROM (VALUES (1), (2), (3)) t(i);CREATE TABLE t2 AS FROM (VALUES (4), (5), (6)) t(k);SELECT * FROM t1 POSITIONAL JOIN t2;| i | k |
|---|---|
| 1 | 4 |
| 2 | 5 |
| 3 | 6 |
Verbesserungen der Python-API
Query Building. Dieses Release erleichtert den schrittweisen Aufbau von Abfragen über die Python-API, indem Relationen selbst abgefragt werden können. Lange SQL-Abfragen lassen sich in mehrere kleinere zerlegen, und Zwischenstände sind einfach zu prüfen.
>>> import duckdb>>> lineitem = duckdb.sql('FROM lineitem.parquet')>>> lineitem.limit(3).show()┌────────────┬───────────┬───────────┬───┬───────────────────┬────────────┬──────────────────────┐│ l_orderkey │ l_partkey │ l_suppkey │ … │ l_shipinstruct │ l_shipmode │ l_comment ││ int32 │ int32 │ int32 │ │ varchar │ varchar │ varchar │├────────────┼───────────┼───────────┼───┼───────────────────┼────────────┼──────────────────────┤│ 1 │ 155190 │ 7706 │ … │ DELIVER IN PERSON │ TRUCK │ egular courts abov… ││ 1 │ 67310 │ 7311 │ … │ TAKE BACK RETURN │ MAIL │ ly final dependenc… ││ 1 │ 63700 │ 3701 │ … │ TAKE BACK RETURN │ REG AIR │ riously. regular, … │├────────────┴───────────┴───────────┴───┴───────────────────┴────────────┴──────────────────────┤│ 3 rows 16 columns (6 shown) │└────────────────────────────────────────────────────────────────────────────────────────────────┘>>> lineitem_filtered = duckdb.sql('FROM lineitem WHERE l_orderkey>5000')>>> lineitem_filtered.limit(3).show()┌────────────┬───────────┬───────────┬───┬────────────────┬────────────┬──────────────────────┐│ l_orderkey │ l_partkey │ l_suppkey │ … │ l_shipinstruct │ l_shipmode │ l_comment ││ int32 │ int32 │ int32 │ │ varchar │ varchar │ varchar │├────────────┼───────────┼───────────┼───┼────────────────┼────────────┼──────────────────────┤│ 5024 │ 165411 │ 444 │ … │ NONE │ AIR │ to the expre ││ 5024 │ 57578 │ 84 │ … │ COLLECT COD │ REG AIR │ osits hinder caref… ││ 5024 │ 111009 │ 3521 │ … │ NONE │ MAIL │ zle carefully saut… │├────────────┴───────────┴───────────┴───┴────────────────┴────────────┴──────────────────────┤│ 3 rows 16 columns (6 shown) │└─────────────────────────────────────────────────────────────────────────────────────────────┘>>> duckdb.sql('SELECT min(l_orderkey), max(l_orderkey) FROM lineitem_filtered').show()┌─────────────────┬─────────────────┐│ min(l_orderkey) │ max(l_orderkey) ││ int32 │ int32 │├─────────────────┼─────────────────┤│ 5024 │ 6000000 │└─────────────────┴─────────────────┘Alles wird lazy ausgewertet. Die Parquet-Datei wird erst bei der finalen Abfrage von der Platte gelesen – und Abfragen werden als Ganzes optimiert. Die zerlegte Abfrage ist genauso schnell wie die lange SQL-Abfrage auf einmal.
Python-Import-APIs. Dieses Release fügt mehrere vertraute APIs für Datenimport und -export hinzu, die den Konventionen anderer Bibliotheken folgen. Diese Funktionen liefern ebenfalls Relationen – die direkt weiter abgefragt werden können.
>>> lineitem = duckdb.read_csv('lineitem.csv')>>> lineitem.limit(3).show()┌────────────┬───────────┬───────────┬───┬───────────────────┬────────────┬──────────────────────┐│ l_orderkey │ l_partkey │ l_suppkey │ … │ l_shipinstruct │ l_shipmode │ l_comment ││ int32 │ int32 │ int32 │ │ varchar │ varchar │ varchar │├────────────┼───────────┼───────────┼───┼───────────────────┼────────────┼──────────────────────┤│ 1 │ 155190 │ 7706 │ … │ DELIVER IN PERSON │ TRUCK │ egular courts abov… ││ 1 │ 67310 │ 7311 │ … │ TAKE BACK RETURN │ MAIL │ ly final dependenc… ││ 1 │ 63700 │ 3701 │ … │ TAKE BACK RETURN │ REG AIR │ riously. regular, … │├────────────┴───────────┴───────────┴───┴───────────────────┴────────────┴──────────────────────┤│ 3 rows 16 columns (6 shown) │└────────────────────────────────────────────────────────────────────────────────────────────────┘>>> duckdb.sql('SELECT min(l_orderkey) FROM lineitem').show()┌─────────────────┐│ min(l_orderkey) ││ int32 │├─────────────────┤│ 1 │└─────────────────┘Polars-Integration. Dieses Release fügt eine enge Integration mit der Polars-DataFrame-Bibliothek hinzu, analog zur Pandas-Integration. Ergebnisse lassen sich mit .pl() in Polars-DataFrames umwandeln.
import duckdbduckdb.sql('SELECT 42').pl()shape: (1, 1)┌─────┐│ 42 ││ --- ││ i32 │╞═════╡│ 42 │└─────┘Außerdem können Polars-DataFrames direkt über die SQL-Schnittstelle abgefragt werden.
import duckdbimport polars as pldf = pl.DataFrame({'a': 42})duckdb.sql('SELECT * FROM df').pl()shape: (1, 1)┌─────┐│ a ││ --- ││ i64 │╞═════╡│ 42 │└─────┘Unterstützung für fsspec-Dateisysteme. Dieses Release unterstützt die fsspec-Dateisystem-API. fsspec erlaubt es, ein eigenes Dateisystem zu definieren und an DuckDB zu übergeben. DuckDB nutzt es dann zum Lesen und Schreiben. So werden Storage-Backends möglich, die DuckDB noch nicht nativ unterstützt, etwa FTP.
import duckdbfrom fsspec import filesystem
duckdb.register_filesystem(filesystem('gcs'))
data = duckdb.query("SELECT * FROM read_csv_auto('gcs:///bucket/file.csv')").fetchall()Mehr dazu im Guide.
Speicherverbesserungen
Delta-Kompression. Die Kompression numerischer Werte im Storage verbessert sich durch die neue Delta- und Delta-Constant-Kompression. Besonders wirksam ist das bei gleichmäßig beabstandeten Werten, etwa Zahlenfolgen (1, 2, 3, ...) oder Zeitstempeln mit festem Intervall (12:00:01, 12:00:02, 12:00:03, ...).
Schlussbemerkung
Die vollständigen Release Notes finden Sie auf GitHub. Wir danken allen Beitragenden für die Arbeit an DuckDB.