DuckDB 0.7.0 发布公告

Author Avatar
Mark Raasveldt
2023-02-13 · 6 分钟

Image of the labrador duck

DuckDB 团队很高兴地宣布最新版本的 DuckDB (0.7.0) 已发布。此版本的 DuckDB 以“拉布拉多”命名,名称来源于北美洲原生的拉布拉多鸭 (Camptorhynchus labradorius)

要安装新版本,请访问安装指南。完整的发行说明可以在 GitHub 上找到。

0.7.0 中的新内容

新版本包含对 JSON 支持的许多改进,新的 SQL 功能,数据摄取和导出的改进以及其他新功能。以下是最重要的更改的摘要,以及实现这些功能的已链接 PR。

数据摄取/导出改进

JSON 摄取。 此版本引入了 read_jsonread_json_auto 方法。 这些可用于将 JSON 文件摄取为表格格式。 类似于 read_csv, read_json 方法需要指定模式,而 read_json_auto 使用采样从文件中自动推断 JSON 的模式。 支持 换行符分隔的 JSON 和常规 JSON。

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]

分区 Parquet/CSV 导出。 DuckDB 已经可以摄取 Hive 分区 Parquet 和 CSV 文件一段时间了。 在此版本之后,DuckDB 也将能够使用 PARTITION_BY 子句写入 Hive 分区数据。 这些文件可以本地或远程导出到 S3 兼容的存储。 这是一个本地示例

COPY orders TO 'orders' (FORMAT parquet, PARTITION_BY (year, month));

这将导致 Parquet 文件写入以下目录结构中

orders
├── year=2021
│    ├── month=1
│    │   ├── file1.parquet
│    │   └── file2.parquet
│    └── month=2
│        └── file3.parquet
└── year=2022
     ├── month=11
     │   ├── file4.parquet
     │   └── file5.parquet
     └── month=12
         └── file6.parquet

并行 Parquet/CSV 写入。 通过并行 Parquet 和 CSV 写入器支持,此版本中 Parquet 和 CSV 写入速度大大提高。

格式 新 (8T)
CSV 2.6 秒 0.4 秒
Parquet 7.5 秒 1.3 秒

请注意,目前并行写入目前仅限于非插入顺序保留 - 可以通过将 preserve_insertion_order 设置为 false 来切换。 在未来的版本中,我们的目标是减轻这种限制,并对并行插入顺序保留写入进行排序。

多数据库支持

附加功能。 此版本增加了将多个数据库附加到同一 DuckDB 实例的支持。 这可以轻松地在单独的 DuckDB 数据库文件之间传输数据,并且还允许将来自单独数据库文件的数据合并到单个查询中。 也可以附加远程 DuckDB 实例(例如,存储在 GitHub 等网络可访问位置)。

ATTACH 'new_db.db';
CREATE TABLE new_db.tbl (i INTEGER);
INSERT INTO new_db.tbl SELECT * FROM range(1000);
DETACH new_db;

请参阅文档以获取更多信息

SQLite 存储后端。 除了增加对附加 DuckDB 数据库的支持外,此版本还增加了对可插拔数据库引擎的支持。 这允许扩展定义自己的数据库和目录引擎,这些引擎可以附加到系统中。 附加后,引擎可以支持读取和写入。SQLite 扩展利用此功能为 SQLite 数据库文件添加对 DuckDB 的本机读/写支持。

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;

使用此功能,可以附加,查询和修改 SQLite 数据库文件,就像它们是本机 DuckDB 数据库文件一样。 这允许在 SQLite 和 DuckDB 之间快速传输数据,并允许您使用 DuckDB 丰富的 SQL 方言来查询存储在 SQLite 表中的数据。

新的 SQL 功能

Upsert 支持。 通过 ON CONFLICT 子句以及与 SQLite 兼容的 INSERT OR REPLACE/INSERT OR IGNORE 语法,此版本增加了Upsert 支持

CREATE TABLE movies (id INTEGER PRIMARY KEY, name VARCHAR);
INSERT INTO movies VALUES (1, 'A New Hope');
FROM movies;
id name
1 星球大战:新希望
INSERT OR REPLACE INTO movies VALUES (1, 'The Phantom Menace');
FROM movies;
id name
1 星球大战:魅影危机

请参阅文档以获取更多信息

Lateral Joins。 此版本增加了对lateral joins的支持。 Lateral joins 是相关子查询的更灵活的变体,可以更轻松地处理嵌套数据,因为它们允许更容易地展开嵌套数据。

Positional Joins。 虽然 SQL 正式建模无序集,但实际上数据集的顺序确实经常具有意义。 当将数据加载到表中或将数据导出回文件时,以及在没有相应的 ORDER BY 子句的情况下执行诸如 LIMIT 之类的查询时,DuckDB 提供了围绕维护行顺序的保证。

为了提高对此用例的支持,此版本引入了 POSITIONAL JOIN。 这种新的连接类型不是连接行的值,而是基于它们在表中的位置连接行。

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

Python API 改进

查询构建。 通过允许查询关系,此版本引入了使用 Python API 更轻松地进行增量查询构建。 这使您可以将长 SQL 查询分解为多个较小的 SQL 查询,并允许您轻松检查查询中间结果。

>>> 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 │
└─────────────────┴─────────────────┘

请注意,所有内容都是延迟评估的。 在执行最终查询之前,不会从磁盘读取 Parquet 文件 - 并且查询会完全优化。 执行分解的查询将与一次执行长 SQL 查询一样快。

Python 摄取 API。 此版本增加了几个熟悉的,遵循其他库使用的标准约定的数据摄取和导出 API。 这些函数也发出关系 - 可以再次直接查询。

>>> 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 集成。 此版本增加了与 Polars DataFrame 库紧密集成的支持,类似于我们与 Pandas DataFrames 的集成。 可以使用 .pl() 函数将结果转换为 Polars DataFrames。

import duckdb
duckdb.sql('SELECT 42').pl()
shape: (1, 1)
┌─────┐
│ 42  │
│ --- │
│ i32 │
╞═════╡
│ 42  │
└─────┘

此外,可以使用 SQL 接口直接查询 Polars DataFrames。

import duckdb
import polars as pl
df = pl.DataFrame({'a': 42})
duckdb.sql('SELECT * FROM df').pl()
shape: (1, 1)
┌─────┐
│ a   │
│ --- │
│ i64 │
╞═════╡
│ 42  │
└─────┘

fsspec 文件系统支持。 此版本增加了对fsspec 文件系统 API的支持。fsspec 允许用户定义自己的文件系统,他们可以将其传递给 DuckDB。 然后,DuckDB 将使用此文件系统来读取和写入数据。 这支持 DuckDB 尚未原生支持的存储后端,例如 FTP。

import duckdb
from fsspec import filesystem

duckdb.register_filesystem(filesystem('gcs'))

data = duckdb.query("SELECT * FROM read_csv_auto('gcs:///bucket/file.csv')").fetchall()

有关更多信息,请查看指南

存储改进

Delta 压缩。 使用新的 delta 和 delta 常量压缩改进了存储中数值的压缩。 当压缩等间距的值时,此压缩方法特别有效。 例如,数字序列(1, 2, 3, ...)或时间戳,它们之间具有固定的间隔(12:00:01, 12:00:02, 12:00:03, ...)。

结语

完整的发行说明可以在GitHub 上找到。 我们要感谢所有贡献者为改进 DuckDB 所做的辛勤工作。