MySQL Order By Rand()効率分析


最近は需要のためにMYSQLのランダム抽出実現法を概ね検討した。例えば、テーブルからランダムに記録を抽出する場合、皆さんの一般的な書き方は、SELECT*FROM tableaname ORDER BY RAND()LIMIT 1です。しかし、その後MYSQLの公式マニュアルを調べてみましたが、RAND()に対するヒントとしては、ORDER BYの文の中でRAND()関数は使えません。このようにすると、データ列が何度もスキャンされます。しかし、MYSQL 3.23バージョンでは依然としてORDER BY RAND()によってランダムが実現され得る。でも、本当にテストしてみたら、このような効率はとても低いです。15万個余りの倉庫で、5つのデータを調べたら、8秒以上もかかります。公式マニュアルを見ても、ORDER BY子文に置くと何度も実行され、自然効率が低くなります。You cannot use a column with RAND()values in an ORDER BY clause、because ORDER BY would evaluate the column multimes.Googleを検索すると、基本的にはmax(id)*rand()を検索してランダムにデータを取得します。
 
SELECT *
FROM `table` AS t1 JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) FROM `table`)) AS id) AS t2
WHERE t1.id >= t2.id
ORDER BY t1.id ASC LIMIT 5;
ですが、このように5つの記録が連続して発生します。解決方法は毎回一つ調べて、5回調べます。それでも価値があります。15万本の表を調べたら0.01秒しかかかりません。以下の文はJOINを採用しています。mysqlのフォーラムで
 
SELECT *
FROM `table`
WHERE id >= (SELECT FLOOR( MAX(id) * RAND()) FROM `table` )
ORDER BY id LIMIT 1;
を使ってテストした人がいます。0.5秒かかります。速度もいいですが、上の文とはかなり違います。どこかおかしいと思います。そこで私は文を書き直しました。SELECT*FROM`table`WHERE id>=(SELECT fror*(SELECT MAX(id)FROM`table`))ORDER BY id LIMIT 1;これで効率が上がり、照会時間は0.01秒となります。最後に文を改善し、MIN(id)の判断を加えます。私は最初のテストの時、MIN(id)の判断を付けていないので、半分の時間はいつも表の前の何行かを調べます。完全な照会文は、
 
SELECT * FROM `table`
WHERE id >= (SELECT floor( RAND() * ((SELECT MAX(id) FROM `table`)-(SELECT MIN(id) FROM `table`)) + (SELECT MIN(id) FROM `table`)))
ORDER BY id LIMIT 1;
 
SELECT *
FROM `table` AS t1 JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM `table`)-(SELECT MIN(id) FROM `table`))+(SELECT MIN(id) FROM `table`)) AS id) AS t2
WHERE t1.id >= t2.id
ORDER BY t1.id LIMIT 1;
が最後にphpでこの2つの文をそれぞれ10回調べ、前者は0.47433秒、後者は0.015130秒という時間がかかり、JOINの文法は直接WHEREで使用するよりも効率が高いようです。