假设我必须非常简单的表格
CREATE TABLE a(id integer PRIMARY KEY,
t timestamp default now(),
sensor_readings real[]);
CREATE TABLE b(id integer PRIMARY KEY,
t timestamp default now(),
sensor_readings real[]);
有一些关于他们的数据
INSERT INTO a(id) SELECT generate_series( 1, 100);
INSERT INTO b(id) SELECT generate_series(10001, 10100);
实际上,表 a 可能有大约 100_000 行,而表 b 大约有 50_000 行。在实践中,id 序列也可能有间隙(大约 %)。因此,笛卡尔积 axb 具有数十亿的基数。
我想随机抽取 1_000 个排序对(a.id,b.id)。我可以使用类似以下查询的内容:
SELECT
*
FROM
(
SELECT
*
FROM
(
SELECT
a.id AS a_id, b.id AS b_id
FROM
a CROSS JOIN b
ORDER BY
random()
) AS s0
LIMIT
1000
) AS s1
ORDER BY
a_id, b_id ;
...但是一旦 a 或 b 上的行数增加(由于 CROSS JOIN 的增长),它就会变得非常低效。
有没有办法以最佳方式做与此等效的事情?也就是说,是否有一种实用的方法可以从a x b
关系中获取随机行样本,而无需实际实例化它。
注意:对于 a.id 或 b.id 可以重复的事实没有限制。虽然这对 (a.id, b.id) 不能。
如果我试图用命令式语言对此进行编程,我很可能会使用循环并执行类似以下伪代码的操作(然后,请统计学家对其进行检查,以确保我真的采取了样本其中所有对具有相同的被选中概率):
start with a result set equal to {} (empty set)
while size of result set < 1000
Pick the id value from a random row from table a -> rand_id_a
Pick the id value from a random row from table b -> rand_id_b
If (rand_id_a, rand_id_b) not in result set
append (rand_id_a, rand_id_b) to result set
end if
end while
sort result set and return it
有没有办法在不求助于循环的情况下获得等效的结果?如果没有,是否有使用 plpgSQL 的有效方法?(或任何其他语言)
最佳解决方案取决于您的设置的确切定义。对于示例设置,这很简单:
唯一有趣的问题:如何有效地折叠骗子。解决方案:让 Postgres 决定。只需使用
DISTINCT
.我们甚至根本不需要涉及表格。超级快。
请注意,
random()
生成(根据文档):因此,要准确
1 + trunc(random() * 100)::int
覆盖1到100之间的 ID 号。实际设置?
您需要更具体地了解您的实际设置。假设每个表中至少有一个有效负载列,而不仅仅是 ID 列。
询问:
真正随机、非常快且几乎与实际表大小无关。
a(a_id)
您所需要的只是和上的索引b(b_id)
。或者可能是多列索引以允许仅索引扫描。该解决方案也适用于跳过调用的一些间隙
nextval()
,只要间隙不比island多多少,生成足够的组合来弥补间隙损失仍然非常便宜。(比使用大表的笛卡尔积或对整个大表进行排序要便宜得多ORDER BY random()
。)只要确保生成足够的组合即可。如果有多个空白,请从 95% 的时间足够的多个组合开始,并添加一个递归步骤以添加更多行(如果您不满足的话)。相关答案中有此解决方案的配方(针对单个表)。还有更多解释和变化:
它始终取决于随机的含义,但如果您要定义所需的行数,那么您可能需要扩展
tsm_system_rows
tsm_system_rows
首先安装扩展
然后你的查询,
这里重要的是它始终提供 1000 ROWS,这比我们可以说的要多
random() <= 0.10
,或者 forTABLESAMPLE BERNOULLI
。如果那不是很好的nuff'
如果你真的需要随机并且不能接受聚类的缺点,那么我会使用
如果您需要修剪重复项
修剪重复项(如果
a.id
和b.id
不是UNIQUE
)并保持结果集随机的唯一明智的方法是事先进行。这可能会变得很糟糕,因为TABLESAMPLE
它还不能在虚拟表中工作,所以你必须创建一个临时表(它可能仍然存在于内存中)。害羞,你可以使用另一种既慢又丑的方法,但至少它不必编写