根据订单随机选择行?

时间:2013-06-19 16:02:36

标签: sql postgresql math

我有一张简单的表格:

create table test (i int4 primary key);

其中有百万行,其中i> = 1且i <= 1000000。

我想删除大约80%的行 - 所以类似于:delete from test where random() < 0.8,但我希望删除更高的i值的删除机会。

技术上:delete from test where i < 800000做到了,但我希望删除的行是随机的,并且仍然希望删除某些的“high-pkey”行,以及一些(只是很多)少保持“低调”。

关于如何获得它的任何想法?

4 个答案:

答案 0 :(得分:1)

这样的东西?

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;

更新:(但很难调整......)

DELETE FROM ztest
WHERE ( log(3+val) * random() < .5)
        ;

[+3是一种非常粗鲁的方式来避免log(1),这将总是删除val = 1的记录]

答案 1 :(得分:1)

AnSo您需要为 i 分配权重。既然你知道你有1000000行,这应该很容易。

从测试中删除随机&lt; .8 +((500000 - i)/ 10000000)

在上面的示例中, i 的最低值有~85%被删除的可能性,而最高有~75%的几率。当然,这不会产生80%,但你只是想要近似值。您可以调整分母以适合您的目的,当然还可以提出更高级的加权方案。

答案 2 :(得分:1)

对于正态分布的数据,从1开始,这有效:

delete from test where random() + 0.1 * (500000 - id) / 500000 > 0.2;

这应该有大约90%的机会删除最低的ID,并有70%的机会删除最高的ID。

如果您的数据未正常分发,则可以使用rank() over (order by id)代替id来完成相同的操作,但这会慢得多。

答案 3 :(得分:0)

获得这种倾斜概率的一种非常简单有效的方法是将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;

呼叫:

SELECT f_del();

->SQLfiddle

这应该可以完美地运作

  • 在数字空间中有或没有间隙
    (改编_del_target使用count()代替_span,因此也适用。)
  • 包含任何最小和最大数量
  • 包含任意行数

该行

JOIN   tbl USING (i)

..只有在generate_series()存在大量差距或初始估算错误时才真正有用。可以移除手头的情况以获得更快的速度(并且仍然可以得到精确的结果)。

如果您仔细选择generate_series()的初始限制,则该功能根本不会循环。

我认为可以安全地假设我不需要告诉你如何进一步概括这一点以使用动态表名或百分比。

这与这个答案有点相似:
Best way to select random rows PostgreSQL


对于这种情况,简单的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;