2

私は単純なテーブルを持っています:

create table test (i int4 primary key);

ここで、i >= 1 および i <= 1000000 の 100 万行があります。

行の ~ 80% を削除delete from test where random() < 0.8したいので、次のようになりますが、値が小さいほど削除の可能性が高くなるようにしiます。

技術的には:delete from test where i < 800000そうですが、削除された行をランダムにして、「高pkey」行の一部を削除し、「低pkey」行の一部(はるかに少ない)を保持したいと考えています。

それを取得する方法について何か考えはありますか?

4

4 に答える 4

1

1 から始まる正規分布データでは、次のように動作します。

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

これにより、最小の ID が削除される確率は約 90%、最大の ID が削除される確率は 70% になります。

データが正常に分散されていない場合はrank() over (order by id)、代わりに in を使用して同じことを実行できますidが、これははるかに遅くなります。

于 2013-06-19T16:47:52.687 に答える
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 は、常にval=1 のレコードを削除するlog(1) を回避するための非常に失礼な方法です]

于 2013-06-19T16:08:47.917 に答える
1

AnSo に重みを割り当てる必要がありますi。1000000 行あることがわかっているので、これは簡単なはずです。

ランダム < .8 + ((500000 - i) / 10000000) のテストから削除

上記の例では、 iの最小値は削除される可能性が最大 85% ですが、最大値は最大 75% の可能性があります。もちろん、これで正確に 80% が得られるわけではありませんが、おおよその値が必要でした。目的に合わせて分母を調整し、もちろん、より高度な重み付けスキームを考え出すことができます。

于 2013-06-19T16:14:55.797 に答える
0

このような傾斜した確率を取得するための非常に単純で効果的な方法は、2 乗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;
于 2013-06-19T17:51:27.427 に答える