データベースからレコードを取得するとき、ランダムな順番で表示したいシーンは意外と多いですよね。クイズゲームや抽選ロジック、サンプリングなど、ランダムなデータ取得の方法を覚えておくと便利です。
MySQL編 — RAND()でランダムソート
MySQL でデータをランダムにソートするには、ORDER BY RAND() を使うのが手軽です。
SELECT *
FROM テーブル名
ORDER BY RAND();
RAND() は 0 以上 1 未満の乱数を返す関数です。
結果がランダムになる一方、全行に対して乱数が評価されるため、大量のデータがある場合はパフォーマンスが低下しやすい点に注意が必要です。
PostgreSQL編 — RANDOM()でランダムソート
PostgreSQL の場合は、MySQL の RAND() と似たような役割をする RANDOM() 関数を使用します。
SELECT *
FROM テーブル名
ORDER BY RANDOM();
RANDOM() は 0 以上 1 未満の乱数を返す関数です。
大量データを扱う場合は、やはりパフォーマンスに注意しましょう。
SQLite編 — RANDOM()でランダムソート
SQLite でも、PostgreSQL と同様に RANDOM() を使ってランダムソートが可能です。
SELECT *
FROM テーブル名
ORDER BY RANDOM();
SQLite の RANDOM() 関数は、整数の乱数を生成しますが、ORDER BY での挙動としては問題なくランダム化が行えます。
大きいテーブルで使う際はパフォーマンスを考慮しましょう。
SQL Server編 — NEWID()でランダムソート
SQL Server には MySQL や PostgreSQL のように行ごとに異なる数値を返すランダム関数がありません。RAND() を直接 ORDER BY で利用すると、全行に対して同じ値を返してしまいます。そのため、代わりに NEWID() を使うのが一般的です。
SELECT *
FROM テーブル名
ORDER BY NEWID();
NEWID() はユニークなGUID(グローバルユニークID)を生成し、結果として行の並びがランダムになります。
ただし、こちらも大量のレコードを処理する場合はパフォーマンスに注意が必要です。
大量データに対するパフォーマンス上の注意
ORDER BY RAND(), ORDER BY RANDOM(), ORDER BY NEWID() などは、すべての行に対して乱数やGUIDを生成→それに基づいてソート という流れをとるため、テーブルに数百万件以上のデータがあるような大規模環境だと負荷が非常に高くなります。
サンプリングテーブルを利用する
もし定期的にランダムデータを扱う必要があるなら、メインのテーブルとは別に、あらかじめサンプリング用のテーブルを作成しておき、そこからランダムソートして取得する方法も検討できます。
IDの範囲を利用して乱数で抽出する
テーブルの主キーやIDが連番であれば、乱数でIDを生成し、そのIDが該当するレコードを取得する方式もあります。ランダムに取得したい件数分だけ ID を抽出し、それを WHERE id IN (…) で取得するなど、工夫すると効率よくランダムデータが取得できます。
アプリケーション側で処理する
SQL でランダムソートを行わず、アプリケーション側で取得したデータをシャッフルするのも1つの手です。サーバーリソースや開発環境によっては、こちらのほうがパフォーマンスの面で優れている場合もあります。
まとめ
ランダムソート自体はシンプルな一行のSQLで実装できますが、データ量が多い場合にはパフォーマンスに注意が必要です。ランダム取得の方法は下記のように覚えておきましょう。
- MySQL: ORDER BY RAND()
- PostgreSQL / SQLite: ORDER BY RANDOM()
- SQL Server: ORDER BY NEWID()
必要に応じて、サンプリングテーブルを用意したり、アプリケーション側で実装したりと工夫してみてください。