SQLで効率的な単純ランダムサンプルを取得するにはどうすればよいですか?問題のデータベースはMySQLを実行しています。私のテーブルは少なくとも200,000行であり、約10,000の単純なランダムサンプルが必要です。
「明白な」答えは次のとおりです。
SELECT * FROM table ORDER BY RAND() LIMIT 10000
大きなテーブルの場合、これは遅すぎます。RAND()
すべての行を呼び出し(すでにO(n)に配置されています)、それらを並べ替えて、せいぜいO(n lg n)にします。O(n)よりも速くこれを行う方法はありますか?
注:Andrew Maoがコメントで指摘しているように、SQL Serverでこのアプローチを使用している場合はNEWID()
、RAND()がすべての行に同じ値を返す可能性があるため、T-SQL関数を使用する必要があります。
編集:5年後
私はより大きなテーブルでこの問題に再び遭遇し、2つの調整を加えた@ignorantのソリューションのバージョンを使用することになりました:
- 行を希望のサンプルサイズの2〜5倍にサンプリングし、安価に
ORDER BY RAND()
RAND()
挿入/更新のたびに、の結果をインデックス付きの列に保存します。(データセットの更新がそれほど多くない場合は、この列を最新の状態に保つための別の方法を見つける必要がある場合があります。)
テーブルの1000アイテムのサンプルを取得するために、行をカウントし、frozen_rand列を使用して結果を平均10,000行までサンプリングします。
SELECT COUNT(*) FROM table; -- Use this to determine rand_low and rand_high
SELECT *
FROM table
WHERE frozen_rand BETWEEN %(rand_low)s AND %(rand_high)s
ORDER BY RAND() LIMIT 1000
(私の実際の実装では、アンダーサンプリングを行わないようにし、rand_highを手動でラップするためにさらに多くの作業が必要ですが、基本的な考え方は「Nをランダムに数千に減らす」ことです。)
これにはいくらかの犠牲が伴いますが、データベースがORDER BY RAND()
再び十分に小さくなるまで、インデックススキャンを使用してデータベースをサンプリングすることができます。
RAND()
後続の呼び出しごとに同じ値が返されるため、SQLサーバーでは機能しません。