⌘+k ctrl+k
1.4 (LTS)
搜索快捷键 cmd + k | ctrl + k
示例

采样用于从数据集中随机选择一个子集。

示例

使用 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)给出,也可以以固定的行数(1010 ROWS)给出。所有三种采样方法都支持按百分比采样,但只有蓄水池采样支持固定行数的采样。

采样是概率性的,也就是说,除非明确指定了种子,否则每次运行的样本可能不同。指定种子仅在未启用多线程(即 SET threads = 1)时保证样本一致。在多线程处理样本的情况下,即使种子固定,样本也不一定保持一致。

采样方法

reservoir(蓄水池采样)

蓄水池采样是一种流式采样技术,它通过维护一个大小等于样本大小的蓄水池来选择随机样本,并随着后续元素的输入随机替换其中的元素。蓄水池采样允许我们精确指定最终样本中所需的元素数量(通过设置蓄水池大小)。因此,与系统采样和伯努利采样不同,蓄水池采样总是输出相同数量的元素。

蓄水池采样仅推荐用于较小的样本量,不建议与百分比采样一起使用。这是因为蓄水池采样需要物化整个样本,并在物化样本内随机替换元组。样本量越大,此过程带来的性能损失就越高。

当使用多处理时,蓄水池采样也会产生额外的性能损耗,因为必须在不同线程之间共享蓄水池以确保无偏采样。当蓄水池非常小时,这不是大问题,但当样本很大时,代价会变得昂贵。

最佳实践 如果可能,请避免在大样本量下使用蓄水池采样。蓄水池采样要求将整个样本物化到内存中。

bernoulli(伯努利采样)

伯努利采样仅在指定了采样百分比时才能使用。它非常简单:基础表中的每一行都以等于指定百分比的概率被包含在内。因此,即使指定了相同的百分比,伯努利采样也可能返回不同数量的元组。预期行数等于表中指定的百分比,但会有一定的方差

由于伯努利采样是完全独立的(没有共享状态),因此在多线程环境下使用伯努利采样没有任何性能惩罚。

system(系统采样)

系统采样是伯努利采样的变体,有一个关键区别:每个向量(vector)以等于采样百分比的概率被包含在内。这是一种集群采样形式。系统采样比伯努利采样更高效,因为不需要执行逐个元组的选择。

预期行数仍然等于表中指定的百分比,但方差会高出 vectorSize 倍。因此,系统采样不适用于少于约 1 万行的数据集,因为在这种情况下,即使你要求 50 PERCENT,也可能出现所有行都被过滤掉或者所有数据都被包含的情况。

表采样

TABLESAMPLEUSING 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%);
© 2025 DuckDB 基金会,阿姆斯特丹,荷兰
行为准则 商标使用指南