样本用于随机选择数据集的子集。
Examples
从tbl
中使用reservoir
抽样方法选择恰好5行的样本:
SELECT *
FROM tbl
USING SAMPLE 5;
使用system
采样方法选择表中大约10%的样本:
SELECT *
FROM tbl
USING SAMPLE 10%;
警告 默认情况下,当您指定一个百分比时,每个向量都会以该概率包含在样本中。如果您的表包含少于约10k行,则指定
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);
使用system
采样方法,选择一个大约20%的表格样本,并设置固定种子(377):
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%);
Syntax
样本允许您随机提取数据集的子集。样本对于更快地探索数据集非常有用,因为通常您可能对查询的确切答案不感兴趣,而只对数据的外观和数据中的内容有粗略的了解。样本允许您更快地获得查询的近似答案,因为它们减少了需要通过查询引擎的数据量。
DuckDB 支持三种不同的采样方法:reservoir
、bernoulli
和 system
。默认情况下,当采样确切的行数时,DuckDB 使用 reservoir
采样,而当指定百分比时,使用 system
采样。下面详细描述了这些采样方法。
样本需要一个样本大小,这表示将从总人口中抽取多少元素。样本可以以百分比形式给出(10%
或 10 PERCENT
),也可以以固定行数形式给出(10
或 10 ROWS
)。所有三种抽样方法都支持按百分比抽样,但只有水库抽样支持按固定行数抽样。
样本是概率性的,也就是说,除非明确指定种子,否则样本在运行之间可能会有所不同。指定种子仅保证在未启用多线程(即SET threads = 1
)的情况下样本是相同的。在多个线程对样本进行运行的情况下,即使有固定的种子,样本也不一定一致。
reservoir
水库采样是一种流采样技术,通过保持一个大小等于样本大小的水库,并在更多元素进入时随机替换元素来选择随机样本。水库采样允许我们精确指定我们希望在结果样本中有多少元素(通过选择水库的大小)。因此,水库采样总是输出相同数量的元素,与系统和伯努利采样不同。
水库采样仅推荐用于小样本量,不推荐用于百分比。这是因为水库采样需要物化整个样本,并在物化样本中随机替换元组。样本量越大,此过程带来的性能影响就越高。
当使用多处理时,水库采样还会带来额外的性能损失,因为水库需要在不同线程之间共享,以确保无偏采样。当水库非常小时,这不是一个大问题,但当样本很大时,这会变得非常昂贵。
最佳实践 尽量避免在大样本量时使用蓄水池抽样。 蓄水池抽样需要将整个样本物化在内存中。
bernoulli
伯努利采样只能在指定采样百分比时使用。它相当直接:基础表中的每一行都有等于指定百分比的机会被包含。因此,即使指定了相同的百分比,伯努利采样也可能返回不同数量的元组。预期的行数等于表的指定百分比,但会有一些方差。
因为伯努利采样是完全独立的(没有共享状态),所以与多线程一起使用伯努利采样不会有任何性能损失。
system
系统抽样是伯努利抽样的一种变体,有一个关键区别:每个向量被包含的概率等于抽样百分比。这是一种聚类抽样的形式。系统抽样比伯努利抽样更高效,因为不需要对每个元组进行选择。
预期的行数仍然等于表的指定百分比,但方差是vectorSize
倍高。因此,系统采样不适用于少于约10k行的数据集,在这种情况下,即使你要求50 PERCENT
,也可能发生所有行被过滤掉,或者所有数据都被包含的情况。
表格示例
TABLESAMPLE
和 USING SAMPLE
子句在语法和效果上是相同的,但有一个重要的区别:tablesamples 直接从指定的表中进行采样,而 sample 子句在整个 from 子句解析后进行采样。这在查询计划中存在连接时是相关的。
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%);