Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 根据顺序随机选择有机会的行?_Sql_Postgresql_Math - Fatal编程技术网

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;