2

我有一个简单的表:

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”行,并保留一些(少得多)“low-pkey”。

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

4

4 回答 4

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 的记录]

于 2013-06-19T16:08:47.917 回答
1

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

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

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

如果您的数据没有正常分布,您可以通过使用rank() over (order by id)代替来完成相同的事情,id但这会慢得多。

于 2013-06-19T16:47:52.687 回答
1

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

从随机 < .8 + ((500000 - i) / 10000000) 的测试中删除

在上面的示例中, i的最小值有大约 85% 的机会被删除,而最高的有大约 75% 的机会。当然,这不会产生准确的 80%,但您只需要近似值。您可以调整分母以适合您的目的,当然还可以提出更高级的加权方案。

于 2013-06-19T16:14:55.797 回答
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_targetcount()代替_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;
于 2013-06-19T17:51:27.427 回答