Sql 根据顺序随机选择有机会的行?
我有一张简单的桌子:Sql 根据顺序随机选择有机会的行?,sql,postgresql,math,Sql,Postgresql,Math,我有一张简单的桌子: create table test (i int4 primary key); 其中有一百万行,i>=1,i是这样的 create table ztest (val int4 primary key); INSERT INTO ztest (val) SELECT gs FROM generate_series(1,1000) gs; DELETE FROM ztest WHERE (val >0 AND val <= 10 and random() &l
create table test (i int4 primary key);
其中有一百万行,i>=1,i是这样的
create table ztest (val int4 primary key);
INSERT INTO ztest (val) SELECT gs FROM generate_series(1,1000) gs;
DELETE FROM ztest
WHERE (val >0 AND val <= 10 and random() < 0.1)
OR (val >10 AND val <= 100 and random() < 0.5)
OR (val >100 AND val <= 1000 and random() < 0.9)
;
SELECT * FROM ztest;
创建表ztest(val int4主键);
插入ztest(val)中,从generate_系列(11000)gs中选择gs;
从ztest中删除
其中(val>0和val 10,val 100和valAnSo),您需要为i分配一个权重。因为您知道您有1000000行,这应该很容易
从测试中删除随机<.8+((500000-i)/10000000)
在上面的示例中,i的最低值有约85%的几率被删除,而最高值有约75%的几率被删除。当然,这不会产生精确的80%,但您只需要近似值。您可以调整分母以满足您的目的,当然还可以提出更高级的加权方案。对于正态分布数据,s从1开始,这是有效的:
delete from test where random() + 0.1 * (500000 - id) / 500000 > 0.2;
这应该有大约90%的几率删除最低的ID,70%的几率删除最高的ID
如果您的数据不是正态分布的,您可以使用rank()over(order by id)
代替id
来完成同样的事情,但这会慢得多。获得这种倾斜概率的一个非常简单有效的方法是平方random()
(或取random()^3
以获得更强大的效果
在此前提下,此函数将产生“完美结果”:
CREATE OR REPLACE FUNCTION f_del()
RETURNS void AS
$func$
DECLARE
_del_pct CONSTANT real := 0.8; -- percentage to delete
_min int; -- minimum i in table
_span int; -- diff. to maximum i
_ct int; -- helper var.
_del_target int; -- number rows to be deleted
BEGIN
SELECT INTO _min, _span, _del_target
min(i), max(i) - min(i), (count(*) * _del_pct)::int FROM tbl;
LOOP
DELETE FROM tbl t
USING (
SELECT DISTINCT i
FROM (
SELECT DISTINCT _min + (_span * random()^2)::int AS i -- square it
FROM generate_series (1, _del_target * 3) -- good estimate for 80%
) num -- generate approx. more than enough numbers
JOIN tbl USING (i)
LIMIT _del_target -- prohibit excess dynamically
) x
WHERE t.i = x.i;
GET DIAGNOSTICS _ct = ROW_COUNT;
_del_target := _del_target - _ct;
EXIT WHEN _del_target <= 0;
END LOOP;
END $func$ LANGUAGE plpgsql;
这应该能很好地工作
- 数字空间中是否有间隙
(将\u del_target
修改为使用count()
而不是\u span
,因此此方法也适用。)
- 具有任何最小和最大数量
- 行数不限
线路
JOIN tbl USING (i)
只有当您对generate_series()
的初始估计值有很多差距或不正确时,..才真正有用。可以将其删除以获得更高的速度(以及更精确的结果)
如果仔细选择generate_series()
的初始限制,函数将不会循环
我认为可以安全地假设,我不需要告诉您如何进一步推广它来处理动态表名或百分比
这有点类似于这个答案:
就在这种情况下,简单的SQL命令会运行得更快一些:
DELETE FROM tbl t
USING (
SELECT DISTINCT (1000000 * random()^2)::int AS i
FROM generate_series (1, 2130000)
) x
WHERE t.i = x.i;
我曾经考虑过这样的事情,但试图找到一种不需要条件逻辑的方法。此外,这将给较大的数字选择一个数字的最大机会,因此选择要保留的数字,而不是选择要删除的数字。我还建议创建一个临时表,其中包含要首先删除的索引o允许检查并检查数字是否与要删除的数字足够接近,以及随机排列是否符合您的喜好。如果没有丑陋和/或列表的非分层版本,以及预期的总传递率为80%,那就太好了。仍在尝试…按照规定运行后,它删除了约10%的行(日志(3+)方法。一个复杂的where-肯定能用,但我需要一些不那么乏味的东西来写。对不起,我测试了N=1000,删除了大约200行。你可能需要稍微调整一下。但对我来说,它似乎有点陡峭(太多的低值被击中,太少的高值。可能它需要一个额外的项或某个地方的系数…)顺便说一句:我倾向于使用多次运行来做一些事情:首先从所有运行中删除20%,然后从较低的100K中删除20%,从较低的10K中删除20%,等等。它几乎删除了所有最高id行。在表的其余部分,分布或多或少是线性的。可以尝试使用:create table test as select generate_series(1100000)作为id;从测试中删除random()<.8+((50000-id)/1000000);选择id/5000,计数(*)从测试组按1的顺序按1;
的想法是,在最后一次选择中,每行的计数应该越来越高。是的,我的测试结果是类似的线性分布。因此,80%的阈值不需要完全满足,只需要大致满足?是-正如我在示例delete…where random()中所示<0.8
不管怎样,我最终写了一些东西来获得准确的结果,几乎没有更多的成本。顺便说一句,这是一个很好的问题。请在100万行表上检查这一点,与Coray代码比较。并检查时间。@depesz:generate_series()的初始估计值
太低,导致了许多循环。根据调整后的估计,它不会循环并在10秒内完成,耗时100万行。现在在我的旧测试服务器上。Corey的代码只需1.5秒。但我的方法更精确、更通用。根据您的需要。仅供参考,您应该将500000行替换为(从测试中选择计数(*))对于答案的一般形式。
JOIN tbl USING (i)
DELETE FROM tbl t
USING (
SELECT DISTINCT (1000000 * random()^2)::int AS i
FROM generate_series (1, 2130000)
) x
WHERE t.i = x.i;