次の例では、グループ_からランダムに行を抽出するテーブルfoo
があります。
_CREATE TABLE foo (
line INT
);
INSERT INTO foo (line)
SELECT generate_series(0, 999, 1);
_
_line % 10
_でグループ化するとします。私はこれを行うことができます:
_SELECT DISTINCT ON (bin) bin, line
FROM (
SELECT line, line % 10 AS bin, random() x
FROM foo
ORDER BY x
) X
_
私がやりたいのは、各ビンから数回ランダムにピックを取得することです。 generate_series()
とLATERAL
でこれを行うことができると思っていました
_SELECT i, line, bin
FROM
(
SELECT generate_series(1,3) i
) m,
LATERAL
(SELECT DISTINCT ON (bin) bin, line
FROM (
SELECT line, line % 10 bin, random() x
FROM foo
ORDER BY x
) X
ORDER BY bin) Q
ORDER BY bin, i;
_
ただし、PostgreSQL 9.5でこれを行うと、指定したline
ごとに同じbin
が得られます。繰り返しごとにi
です。たとえば、
_i;line;bin
1;530;0
2;530;0
3;530;0
1;611;1
2;611;1
3;611;1
...
_
random()
を含むサブクエリは、generate_series()
の各行に対して異なる方法で実行されると思ったので、混乱しています。
編集:私はより多くの組み合わせを生成し、これらから選択することで同じ目的を達成できることに気付きました
_SELECT DISTINCT ON (bin, round) round, bin, line
FROM (
SELECT line, line % 10 as bin, round
FROM foo, generate_series(1,3) round
ORDER BY bin, random()
) X;
_
だから私の質問は、なぜ最初の方法がうまくいかなかったのですか?
編集:問題は、サブクエリが何らかの方法で相関している場合にLATERALがforループのようにしか機能しないことにあるようです(@ypercubeのコメントのおかげです)。したがって、私の元のアプローチは、次の小さな変更を追加することで修正できます
_SELECT i, line, bin
FROM
(
SELECT generate_series(1,3) i
) m,
LATERAL
(
SELECT DISTINCT ON (bin) bin, line
FROM (
SELECT line, line % 10 bin, m.i, random() x -- <NOTE m.i HERE
FROM foo
ORDER BY x
) X
ORDER BY bin
LIMIT 3
) Q
ORDER BY bin, i;
_
_DISTINCT ON
_の代わりにLIMIT (3)
を使用して、このようなクエリを記述します。
generate_series(0, 9)
は、すべての個別のビンを取得するために使用されます。 「ビン」が0から9までのすべての整数ではない場合は、代わりに_(SELECT DISTINCT line % 10 FROM foo) AS g (bin)
_を使用できます。
_SELECT
g.bin,
ROW_NUMBER() OVER (PARTITION BY g.bin ORDER BY d.x) AS i,
d.*
FROM
generate_series(0, 9) AS g (bin),
LATERAL
( SELECT f.*, random() x
FROM foo AS f
WHERE f.line % 10 = g.bin
ORDER BY x
LIMIT 3
) AS d
ORDER BY
bin, x ;
_
また、出力でrandom()
番号が必要ない場合は、サブクエリでORDER BY random()
を使用して、selectおよびorder by句からx
を削除できます-または_ORDER BY d.x
_を_ORDER BY d.line
_に置き換えます。
私がやりたいのは、各ビンから数回ランダムにピックを取得することです。
この問題を解決する方法はたくさんあります。それぞれがランダム性を導入し、時間がかかります。
TABLESAMPLE SYSTEM
および tsm_system_rows
TABLESAMPLE BERNOULLI
ほとんどの場合、TABLEAMPLE SYSTEM
およびtsm_system_rows
は、テーブルの「公正な」サンプリングを取得するのに十分です。テーブル全体を訪問する必要がないという追加の利点があります。
より等間隔のサンプルが必要な場合は、TABLESAMPLE BERNOULLI
はテーブル全体を訪問し、内部のすべてのページから選択します。
アドホックに進みたい場合は、これで十分でしょう。
SELECT *
FROM (
SELECT dense_rank() OVER (PARTITION BY bin ORDER BY random()), *
FROM (
SELECT line % 10 AS bin, line
FROM foo
) AS t
) AS t
WHERE dense_rank <= 3
ORDER BY line;