⌘+k ctrl+k
1.4 (LTS)
搜索快捷键 cmd + k | ctrl + k
模式

类型

使用正确的类型对列进行编码(例如 BIGINTDATEDATETIME)非常重要。虽然总是可以使用字符串类型(VARCHAR 等)来编码更具体的值,但这并不推荐。字符串会占用更多空间,且在过滤、连接 (join) 和聚合等操作中处理速度较慢。

在加载 CSV 文件时,您可以利用 CSV 读取器的 自动检测机制 来获取 CSV 输入的正确类型。

如果您在内存受限的环境中运行,使用较小的数据类型(例如 TINYINT)可以减少完成查询所需的内存和磁盘空间。DuckDB 的 位压缩 (bitpacking compression) 意味着存储在较大类型中的小数值在磁盘上不会占用更大的空间,但在处理过程中它们会占用更多内存。

最佳实践:在创建列时,尽量使用最严格的类型。避免使用字符串来编码更具体的数据项。

微基准测试:使用时间戳

我们使用 规模因子 (scale factor) 为 300 的 LDBC Comment 表中的 creationDate 来展示聚合速度的差异。该表约有 5.54 亿个无序的时间戳值。我们运行一个简单的聚合查询,计算两种配置下时间戳中每月对应的平均日期。

首先,我们使用 DATETIME 对值进行编码,并使用 extract 日期时间函数 运行查询。

SELECT avg(extract('day' FROM creationDate)) FROM Comment;

其次,我们使用 VARCHAR 类型并使用字符串操作。

SELECT avg(CAST(creationDate[9:10] AS INTEGER)) FROM Comment;

微基准测试结果如下:

列类型 存储大小 查询时间
DATETIME 3.3 GB 0.9 秒
VARCHAR 5.2 GB 3.9 秒

结果表明,使用 DATETIME 值可获得更小的存储空间和更快的处理速度。

微基准测试:基于字符串的连接

我们通过计算 规模因子为 100 的 LDBC Comment 表 的自连接,来说明不同类型对连接操作的影响。该表使用 64 位整数标识符作为每一行的 id 属性。我们执行以下连接操作:

SELECT count(*) AS count
FROM Comment c1
JOIN Comment c2 ON c1.ParentCommentId = c2.id;

在第一个实验中,我们使用正确的(最严格的)类型,即将 idParentCommentId 列都定义为 BIGINT。在第二个实验中,我们将所有列定义为 VARCHAR 类型。虽然两个实验的查询结果相同,但运行时间差异显著。以下结果显示,在 BIGINT 列上进行连接的速度比在编码相同值的 VARCHAR 类型列上进行相同连接的速度快约 1.8 倍。

连接列载荷类型 连接列模式类型 示例值 查询时间
BIGINT BIGINT 70368755640078 1.2 秒
BIGINT VARCHAR '70368755640078' 2.1 秒

最佳实践:避免将数值表示为字符串,特别是当您打算对其执行连接操作时。

约束

DuckDB 允许定义 约束 (constraints),如 UNIQUEPRIMARY KEYFOREIGN KEY。这些约束有助于确保数据完整性,但会对加载性能产生负面影响,因为它们需要构建索引并执行检查。此外,它们 极少能提高查询性能,因为 DuckDB 在进行连接和聚合操作时并不依赖这些索引(有关详细信息,请参阅 索引)。

最佳实践:除非您的目标是确保数据完整性,否则请勿定义约束。

微基准测试:主键的影响

我们使用 规模因子为 300 的 LDBC Comment 表 来展示使用主键的效果。该表约有 5.54 亿条条目。在第一个实验中,我们创建 带有 主键的模式,然后加载数据。在第二个实验中,我们创建 不带 主键的模式,然后加载数据。在第三个实验中,我们创建 不带 主键的模式,加载数据,然后添加主键约束。在所有情况下,我们都从 .csv.gz 文件中提取数据,并测量执行加载所需的时间。

操作 执行时间
带主键加载 461.6 秒
不带主键加载 121.0 秒
不带主键加载后添加主键 242.0 秒

对于此数据集,主键仅在高度选择性的查询(例如根据单个标识符进行过滤)上会有(微小的)正面影响。定义主键(或索引)不会对连接和聚合算子产生影响。

最佳实践:为了获得最佳批量加载性能,请避免使用主键约束。如果确实需要,请在批量加载完成后再定义它们。

© 2025 DuckDB 基金会,阿姆斯特丹,荷兰
行为准则 商标使用指南