- 安装
- 文档
- 入门
- 连接
- 数据导入
- 客户端 API
- 概览
- ADBC
- C
- C++
- CLI
- Dart
- Go
- Java (JDBC)
- Julia
- Node.js (已弃用)
- Node.js (Neo)
- ODBC
- PHP
- Python
- R
- Rust
- Swift
- Wasm
- SQL
- 介绍
- 语句
- 概览
- ANALYZE
- ALTER TABLE
- ALTER VIEW
- ATTACH 和 DETACH
- CALL
- CHECKPOINT
- COMMENT ON
- COPY
- CREATE INDEX
- CREATE MACRO
- CREATE SCHEMA
- CREATE SECRET
- CREATE SEQUENCE
- CREATE TABLE
- CREATE VIEW
- CREATE TYPE
- DELETE
- DESCRIBE
- DROP
- EXPORT 和 IMPORT DATABASE
- INSERT
- LOAD / INSTALL
- PIVOT
- 性能分析
- SELECT
- SET / RESET
- SET VARIABLE
- SUMMARIZE
- 事务管理
- UNPIVOT
- UPDATE
- USE
- VACUUM
- 查询语法
- SELECT
- FROM 和 JOIN
- WHERE
- GROUP BY
- GROUPING SETS
- HAVING
- ORDER BY
- LIMIT 和 OFFSET
- SAMPLE
- 展开嵌套
- WITH
- WINDOW
- QUALIFY
- VALUES
- FILTER
- 集合操作
- 预处理语句
- 数据类型
- 表达式
- 函数
- 概览
- 聚合函数
- 数组函数
- 位字符串函数
- Blob 函数
- 日期格式化函数
- 日期函数
- 日期部分函数
- 枚举函数
- 间隔函数
- Lambda 函数
- 列表函数
- 映射函数
- 嵌套函数
- 数值函数
- 模式匹配
- 正则表达式
- 结构体函数
- 文本函数
- 时间函数
- 时间戳函数
- 带时区时间戳函数
- 联合函数
- 实用函数
- 窗口函数
- 约束
- 索引
- 元查询
- DuckDB 的 SQL 方言
- 示例
- 配置
- 扩展
- 核心扩展
- 概览
- 自动补全
- Avro
- AWS
- Azure
- Delta
- DuckLake
- 编码
- Excel
- 全文搜索
- httpfs (HTTP 和 S3)
- Iceberg
- ICU
- inet
- jemalloc
- MySQL
- PostgreSQL
- 空间
- SQLite
- TPC-DS
- TPC-H
- UI
- VSS
- 指南
- 概览
- 数据查看器
- 数据库集成
- 文件格式
- 概览
- CSV 导入
- CSV 导出
- 直接读取文件
- Excel 导入
- Excel 导出
- JSON 导入
- JSON 导出
- Parquet 导入
- Parquet 导出
- 查询 Parquet 文件
- 使用 file: 协议访问文件
- 网络和云存储
- 概览
- HTTP Parquet 导入
- S3 Parquet 导入
- S3 Parquet 导出
- S3 Iceberg 导入
- S3 Express One
- GCS 导入
- Cloudflare R2 导入
- 通过 HTTPS / S3 使用 DuckDB
- Fastly 对象存储导入
- 元查询
- ODBC
- 性能
- Python
- 安装
- 执行 SQL
- Jupyter Notebooks
- marimo Notebooks
- Pandas 上的 SQL
- 从 Pandas 导入
- 导出到 Pandas
- 从 Numpy 导入
- 导出到 Numpy
- Arrow 上的 SQL
- 从 Arrow 导入
- 导出到 Arrow
- Pandas 上的关系型 API
- 多个 Python 线程
- 与 Ibis 集成
- 与 Polars 集成
- 使用 fsspec 文件系统
- SQL 编辑器
- SQL 功能
- 代码片段
- 故障排除
- 术语表
- 离线浏览
- 操作手册
- 开发
- 内部结构
- 为什么选择 DuckDB
- 行为准则
- 发布日历
- 路线图
- 站点地图
- 在线演示
类型
为列编码使用正确的数据类型(例如,BIGINT
、DATE
、DATETIME
)非常重要。虽然始终可以使用字符串类型(VARCHAR
等)来编码更具体的值,但我们不推荐这样做。字符串占用更多空间,并且在过滤、连接和聚合等操作中处理速度较慢。
加载 CSV 文件时,您可以利用 CSV 读取器的自动检测机制来获取 CSV 输入的正确数据类型。
如果您在内存受限的环境中运行,使用较小的数据类型(例如,TINYINT
)可以减少完成查询所需的内存和磁盘空间量。DuckDB 的位打包压缩意味着存储在较大类型中的小值在磁盘上不会占用更大的空间,但在处理过程中会占用更多内存。
最佳实践 创建列时,请尽可能使用限制性最强的数据类型。避免使用字符串来编码更具体的数据项。
微基准测试:使用时间戳
我们使用LDBC Comment 表中比例因子为 300 的 creationDate
列来说明聚合速度的差异。该表包含约 5.54 亿个无序时间戳值。我们运行一个简单的聚合查询,以两种配置从时间戳中返回月中平均日期。
首先,我们使用 DATETIME
类型编码值,并使用extract
datetime 函数运行查询。
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 表执行自连接,来说明在不同类型上执行连接操作所造成的差异。该表包含用作每行 id
属性的 64 位整数标识符。我们执行以下连接操作:
SELECT count(*) AS count
FROM Comment c1
JOIN Comment c2 ON c1.ParentCommentId = c2.id;
在第一个实验中,我们使用了正确(限制性最强)的数据类型,即 id
和 ParentCommentId
列都定义为 BIGINT
。在第二个实验中,我们用 VARCHAR
类型定义了所有列。尽管两次实验的查询结果相同,但它们的运行时间却显著不同。以下结果表明,在 BIGINT
列上进行连接比在编码相同值的 VARCHAR
类型列上执行相同连接快约 1.8 倍。
连接列有效负载类型 | 连接列模式类型 | 示例值 | 查询时间 |
---|---|---|---|
BIGINT |
BIGINT |
70368755640078 |
1.2 秒 |
BIGINT |
VARCHAR |
'70368755640078' |
2.1 秒 |
最佳实践 避免将数值表示为字符串,特别是当您打算对其执行连接等操作时。
约束
DuckDB 允许定义约束,例如 UNIQUE
、PRIMARY KEY
和 FOREIGN KEY
。这些约束对于确保数据完整性有益,但它们会对加载性能产生负面影响,因为它们需要构建索引和执行检查。此外,它们极少提高查询性能,因为 DuckDB 不依赖这些索引进行连接和聚合操作(有关更多详细信息,请参阅索引)。
最佳实践 除非您的目标是确保数据完整性,否则不要定义约束。
微基准测试:主键的影响
我们使用比例因子为 300 的 LDBC Comment 表来说明使用主键的效果。该表包含约 5.54 亿条记录。在第一个实验中,我们不带主键创建模式,然后加载数据。在第二个实验中,我们带主键创建模式,然后加载数据。在第三种情况下,我们不带主键创建模式,加载数据,然后添加主键约束。在所有情况下,我们都从 .csv.gz
文件中获取数据,并测量执行加载所需的时间。
操作 | 执行时间 |
---|---|
带主键加载 | 461.6 秒 |
不带主键加载 | 121.0 秒 |
不带主键加载,然后添加主键 | 242.0 秒 |
对于此数据集,主键仅对高度选择性的查询(例如在单个标识符上进行过滤时)产生(小的)积极影响。定义主键(或索引)对连接和聚合操作符没有影响。
最佳实践 为了获得最佳的批量加载性能,请避免使用主键约束。如果它们是必需的,请在批量加载步骤之后定义它们。