- 安装
- 文档
- 入门
- 连接
- 数据导入与导出
- 湖仓格式
- 客户端 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
- MERGE INTO
- PIVOT
- 性能分析
- SELECT
- SET / RESET
- SET VARIABLE
- SHOW 与 SHOW DATABASES
- 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
- Lance
- MySQL
- PostgreSQL
- 空间
- SQLite
- TPC-DS
- TPC-H
- UI
- Unity Catalog
- Vortex
- 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 的占用空间
- 安装 DuckDB
- 日志
- 保护 DuckDB 安全
- 非确定性行为
- 限制
- DuckDB Docker 容器
- 开发
- 内部结构
- 站点地图
- 在线演示
采样用于从数据集中随机选择一个子集。
示例
使用 reservoir(蓄水池)采样从 tbl 中选择恰好 5 行的样本
SELECT *
FROM tbl
USING SAMPLE 5;
使用 system(系统)采样从表中选择 大约 10% 的样本
SELECT *
FROM tbl
USING SAMPLE 10%;
警告 默认情况下,当你指定百分比时,每个 向量(vector) 都会以该概率包含在样本中。如果你的表包含少于约 1 万行数据,建议改用
bernoulli(伯努利)采样选项,它将概率独立应用于每一行。即便如此,有时你得到的行数也会多于或少于指定的百分比,但完全没有获取到任何行的情况会大大减少。若要获取(四舍五入后)精确 10% 的行,必须使用reservoir采样选项。
使用 bernoulli(伯努利)采样从表中选择 大约 10% 的样本
SELECT *
FROM tbl
USING SAMPLE 10 PERCENT (bernoulli);
使用 reservoir(蓄水池)采样从表中选择 精确 10%(四舍五入后)的样本
SELECT *
FROM tbl
USING SAMPLE 10 PERCENT (reservoir);
使用带有固定种子 (100) 的蓄水池采样从表中选择 精确 50 行的样本
SELECT *
FROM tbl
USING SAMPLE reservoir(50 ROWS)
REPEATABLE (100);
使用带有固定种子 (377) 的系统采样从表中选择 大约 20% 的样本
SELECT *
FROM tbl
USING SAMPLE 20% (system, 377);
在与 tbl2 连接之前,对 tbl 进行 大约 20% 的采样
SELECT *
FROM tbl TABLESAMPLE reservoir(20%), tbl2
WHERE tbl.i = tbl2.i;
在与 tbl2 连接之后,对 tbl 进行 大约 20% 的采样
SELECT *
FROM tbl, tbl2
WHERE tbl.i = tbl2.i
USING SAMPLE reservoir(20%);
语法
采样允许你随机提取数据集的子集。采样对于加速探索数据集非常有用,因为你往往不需要查询的精确答案,而只需要了解数据的概貌以及其中包含的内容。采样可以通过减少需要通过查询引擎处理的数据量,让你更快地获得查询的近似答案。
DuckDB 支持三种不同的采样方法:reservoir(蓄水池)、bernoulli(伯努利)和 system(系统)。默认情况下,当采样行数确定时,DuckDB 使用 reservoir 采样;当指定百分比时,使用 system 采样。下面详细介绍这些采样方法。
采样需要一个样本大小,它指示将从总总体中采样多少元素。采样既可以以百分比(10% 或 10 PERCENT)给出,也可以以固定的行数(10 或 10 ROWS)给出。所有三种采样方法都支持按百分比采样,但只有蓄水池采样支持固定行数的采样。
采样是概率性的,也就是说,除非明确指定了种子,否则每次运行的样本可能不同。指定种子仅在未启用多线程(即 SET threads = 1)时保证样本一致。在多线程处理样本的情况下,即使种子固定,样本也不一定保持一致。
采样方法
reservoir(蓄水池采样)
蓄水池采样是一种流式采样技术,它通过维护一个大小等于样本大小的蓄水池来选择随机样本,并随着后续元素的输入随机替换其中的元素。蓄水池采样允许我们精确指定最终样本中所需的元素数量(通过设置蓄水池大小)。因此,与系统采样和伯努利采样不同,蓄水池采样总是输出相同数量的元素。
蓄水池采样仅推荐用于较小的样本量,不建议与百分比采样一起使用。这是因为蓄水池采样需要物化整个样本,并在物化样本内随机替换元组。样本量越大,此过程带来的性能损失就越高。
当使用多处理时,蓄水池采样也会产生额外的性能损耗,因为必须在不同线程之间共享蓄水池以确保无偏采样。当蓄水池非常小时,这不是大问题,但当样本很大时,代价会变得昂贵。
最佳实践 如果可能,请避免在大样本量下使用蓄水池采样。蓄水池采样要求将整个样本物化到内存中。
bernoulli(伯努利采样)
伯努利采样仅在指定了采样百分比时才能使用。它非常简单:基础表中的每一行都以等于指定百分比的概率被包含在内。因此,即使指定了相同的百分比,伯努利采样也可能返回不同数量的元组。预期行数等于表中指定的百分比,但会有一定的方差。
由于伯努利采样是完全独立的(没有共享状态),因此在多线程环境下使用伯努利采样没有任何性能惩罚。
system(系统采样)
系统采样是伯努利采样的变体,有一个关键区别:每个向量(vector)以等于采样百分比的概率被包含在内。这是一种集群采样形式。系统采样比伯努利采样更高效,因为不需要执行逐个元组的选择。
预期行数仍然等于表中指定的百分比,但方差会高出 vectorSize 倍。因此,系统采样不适用于少于约 1 万行的数据集,因为在这种情况下,即使你要求 50 PERCENT,也可能出现所有行都被过滤掉或者所有数据都被包含的情况。
表采样
TABLESAMPLE 和 USING SAMPLE 子句在语法和效果上是相同的,但有一个重要区别:tablesample 直接对指定的表进行采样,而 sample 子句在整个 from 子句解析完成后进行采样。这在查询计划中存在连接(join)时非常重要。
TABLESAMPLE 子句本质上等同于创建一个带有 USING SAMPLE 子句的子查询,即以下两个查询是完全相同的:
连接之前对 tbl 进行 20% 采样
SELECT *
FROM
tbl TABLESAMPLE reservoir(20%),
tbl2
WHERE tbl.i = tbl2.i;
连接之前对 tbl 进行 20% 采样
SELECT *
FROM
(SELECT * FROM tbl USING SAMPLE reservoir(20%)) tbl,
tbl2
WHERE tbl.i = tbl2.i;
连接之后进行 20% 采样(即对连接结果进行 20% 采样)
SELECT *
FROM tbl, tbl2
WHERE tbl.i = tbl2.i
USING SAMPLE reservoir(20%);