このような傾斜した確率を取得するための非常に単純で効果的な方法は、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;