web-dev-qa-db-ja.com

mysqlの大きなテーブルからランダムな行をすばやく選択

大きなmysqlテーブルからランダムな行を選択する高速な方法は何ですか?

私はphpで作業していますが、それが別の言語であっても、あらゆる解決策に興味があります。

45
lajos

すべてのIDを取得し、そこからランダムに1つを選択して、行全体を取得します。

IDがシーケンシャルでホールがないことがわかっている場合は、最大値を取得してランダムIDを計算できます。

あちこちに穴があり、ほとんどがシーケンシャルな値で、少しゆがんだランダム性を気にしない場合は、最大値を取得してIDを計算し、計算したID以上のIDを持つ最初の行を選択します。スキューイングの理由は、IDがそのようなホールをフォローしている場合、別のIDをフォローしているホールよりもピックされる可能性が高いためです。

ランダムに注文すると、ひどいテーブルスキャンが手に入ることになり、Word quickはそのようなソリューションには適用されません。

それを行わないでください。GUIDで注文する必要もありません。同じ問題があります。

単一のクエリで高速に実行する方法が必要であることはわかっていました。そしてここにあります:

外部コードの関与なしの高速な方法、称賛

http://jan.kneschke.de/projects/mysql/order-by-Rand/

SELECT name
  FROM random AS r1 JOIN
       (SELECT (Rand() *
                     (SELECT MAX(id)
                        FROM random)) AS id)
        AS r2
 WHERE r1.id >= r2.id
 ORDER BY r1.id ASC
 LIMIT 1;
37
Vinko Vrsalovic

MediaWikiは興味深いトリック(WikipediaのSpecial:Random機能用)を使用します。記事が含まれるテーブルには、ランダムな番号(記事の作成時に生成される)を持つ追加の列があります。ランダムな記事を取得するには、乱数を生成し、乱数列の値が次に大きいまたは小さい(覚えていない)記事を取得します。インデックスを使用すると、これは非常に高速になります。 (そしてMediaWikiはPHPで書かれており、MySQL用に開発されています。)

このアプローチは、結果の数値の分布が悪い場合に問題を引き起こす可能性があります。 IIRC、これはMediaWikiで修正されたので、この方法で行う場合は、コードを調べて、現在どのように行われているかを確認する必要があります(おそらく、定期的に乱数列が再生成されます)。

30
CesarB

これはかなり高速に実行されるソリューションであり、ID値が連続している、または1から始まることに依存せずに、より良いランダム分布を取得します。

SET @r := (SELECT ROUND(Rand() * (SELECT COUNT(*) FROM mytable)));
SET @sql := CONCAT('SELECT * FROM mytable LIMIT ', @r, ', 1');
PREPARE stmt1 FROM @sql;
EXECUTE stmt1;
12
Bill Karwin

多分あなたは次のようなことをすることができます:

SELECT * FROM table 
  WHERE id=
    (FLOOR(Rand() * 
           (SELECT COUNT(*) FROM table)
          )
    );

これは、ID番号がすべてギャップなく連続していることを前提としています。

3
davr

計算されたランダム値を含む列を各行に追加し、それを順序付け句で使用して、選択時に1つの結果に制限します。これは、ORDER BY RANDOM()が引き起こすテーブルスキャンよりも速く機能します。

pdate:検索時にSELECTステートメントを発行する前に、ランダム値を計算する必要があります。

SELECT * FROM `foo` WHERE `foo_Rand` >= {some random value} LIMIT 1
3
Rob

特定のテーブル(たとえば「単語」)から複数のランダムな行を選択するために、私たちのチームはこの美しさを考え出しました:

SELECT * FROM
`words` AS r1 JOIN 
(SELECT  MAX(`WordID`) as wid_c FROM `words`) as tmp1
WHERE r1.WordID >= (SELECT (Rand() * tmp1.wid_c) AS id) LIMIT n
1

このテーブルの行を削除しない場合、最も効率的な方法は次のとおりです。

(最小IDがわかっている場合は、スキップしてください)

SELECT MIN(id) AS minId, MAX(id) AS maxId FROM table WHERE 1

$randId=mt_Rand((int)$row['minId'], (int)$row['maxId']);

SELECT id,name,... FROM table WHERE id=$randId LIMIT 1
1
parm.95

テーブルからランダムな行を見つけるには、ORDER BY Rand()を使用しないでください。これにより、MySQLは完全なファイルソートを実行し、必要な制限行数を取得するようになります。この完全なファイルソートを回避するには、Rand()関数をwhere句でのみ使用します。必要な行数に達するとすぐに停止します。 http://www.rndblog.com/how-to-select-random-rows-in-mysql/ を参照してください

1
Sagi Bron

Rand()による順序なしで、クエリのみを使用してランダムな行を生成する別の方法があります。これには、ユーザー定義変数が含まれます。 テーブルからランダムな行を生成する方法を参照してください

1
Ilan Hazan

これを実行するための関数を作成して、最も可能性の高い最良の答えと最も速い答えをここに!

長所-ギャップがあっても非常に高速に動作します。

<?

$sqlConnect = mysqli_connect('localhost','username','password','database');

function rando($data,$find,$max = '0'){
   global $sqlConnect; // Set as mysqli connection variable, fetches variable outside of function set as GLOBAL
   if($data == 's1'){
     $query = mysqli_query($sqlConnect, "SELECT * FROM `yourtable` ORDER BY `id` DESC LIMIT {$find},1");

     $fetched_data = mysqli_fetch_assoc($query);
      if(mysqli_num_rows($fetched_data>0){
       return $fetch_$data;
      }else{
       rando('','',$max); // Start Over the results returned nothing
      }
   }else{
     if($max != '0'){
        $irand = Rand(0,$max); 
        rando('s1',$irand,$max); // Start rando with new random ID to fetch
     }else{

        $query = mysqli_query($sqlConnect, "SELECT `id` FROM `yourtable` ORDER BY `id` DESC LIMIT 0,1");
        $fetched_data = mysqli_fetch_assoc($query);
        $max = $fetched_data['id'];
        $irand = Rand(1,$max);
        rando('s1',$irand,$max); // Runs rando against the random ID we have selected if data exist will return
     }
   }
 }

 $your_data = rando(); // Returns listing data for a random entry as a ASSOC ARRAY
?>

このコードはテストされていないが、ギャップがあってもランダムなエントリを返すための実用的な概念であることを覚えておいてください。ギャップがロード時間の問題を引き起こすほど大きくない限り。

0
RandomGuest

従来の「SELECT id FROM table ORDER BY Rand()LIMIT 1」は実際には問題ありません。

MySQLマニュアルからの次の抜粋を参照してください。

LIMIT row_countをORDER BYと一緒に使用する場合、MySQLは結果全体をソートするのではなく、ソートされた結果の最初のrow_count行を見つけるとすぐにソートを終了します。

0
igelkott

私はこれを使用しており、ジョブは here からの参照で行われました

SELECT * FROM myTable WHERE Rand()<(SELECT ((30/COUNT(*))*10) FROM myTable) ORDER BY Rand() LIMIT 30;
0
Yousef Altaf

私の場合、私のテーブルにはidが主キーとしてあり、ギャップのない自動インクリメントなので、COUNT(*)またはMAX(id)を使用して行数を取得できます。

次のスクリプトを作成して、最速の操作をテストしました。

logTime();
query("SELECT COUNT(id) FROM tbl");
logTime();
query("SELECT MAX(id) FROM tbl");
logTime();
query("SELECT id FROM tbl ORDER BY id DESC LIMIT 1");
logTime();

結果は次のとおりです。

  • カウント: 36.8418693542479 ms
  • 最大:0.241041183472 ms
  • 注文0.216960906982 ms

注文方法で回答:

SELECT FLOOR(Rand() * (
    SELECT id FROM tbl ORDER BY id DESC LIMIT 1
)) n FROM tbl LIMIT 1

...
SELECT * FROM tbl WHERE id = $result;
0

Jan Kneschkeによる this link または this SO answer を見てください。どちらも同じ質問について話し合っています。SO回答はさまざまなオプションにも当てはまり、ニーズに応じていくつかの良い提案があります。Janはすべてのさまざまなオプションとそれぞれのパフォーマンス特性を検討します。実行するための最も最適化された方法については、次のようになります。これはMySQL選択内で:

SELECT name
  FROM random AS r1 JOIN
       (SELECT (Rand() *
                     (SELECT MAX(id)
                        FROM random)) AS id)
        AS r2
 WHERE r1.id >= r2.id
 ORDER BY r1.id ASC
 LIMIT 1;

HTH、

-Dipin

0
Dipin

IDが連続していないという問題に遭遇しました。私がこれを思いついたもの。

SELECT * FROM products WHERE Rand()<=(5/(SELECT COUNT(*) FROM products)) LIMIT 1

返される行は約5行ですが、1行に制限しています。

別のWHERE句を追加する場合は、少し面白くなります。割引価格で商品を検索したいとします。

SELECT * FROM products WHERE Rand()<=(100/(SELECT COUNT(*) FROM pt_products)) AND discount<.2 LIMIT 1

あなたがしなければならないことは、十分な結果を返すことを確認することです。これが100に設定されている理由です。サブクエリのWHERE discount <.2句を使用すると、10倍遅くなったため、より多くの結果と制限を返すことをお勧めします。

0
bikedorkseattle

私はここに多くの解決策を見ます。 1つまたは2つは問題ないようですが、他のソリューションにはいくつかの制約があります。しかし、次の解決策はすべての状況で機能します

select a.* from random_data a, (select max(id)*Rand() randid  from random_data) b
     where a.id >= b.randid limit 1;

ここでは、idは連続している必要はありません。任意の主キー/一意/自動インクリメント列にすることができます。以下を参照してください 大きなMySQLテーブルからランダムな行を選択する最速の方法

Zillurに感謝- www.techinfobest.com

0
Zillur

簡単だが遅い方法が(小さめのテーブルに適しています)

SELECT * from TABLE order by Rand() LIMIT 1
0
Vinko Vrsalovic

注文すると、フルスキャンテーブルが実行されます。 select count(*)を実行し、後で0と最後のレジストリの間でランダムなrow = rownumを取得する場合が最適です。

0
MazarD

疑似コード:

sql "select id from table"
store result in list
n = random(size of list)
sql "select * from table where id=" + list[n]

これは、idが一意の(主)キーであることを前提としています。

0
Anders Sandvig

以下のクエリを使用してランダムな行を取得します

SELECT user_firstname ,
COUNT(DISTINCT usr_fk_id) cnt
FROM userdetails 
GROUP BY usr_fk_id 
ORDER BY cnt ASC  
LIMIT 1
0
MANOJ

私はSQLに少し慣れていませんが、PHPで乱数を生成し、

_SELECT * FROM the_table WHERE primary_key >= $randNr
_

これは、テーブルの穴の問題を解決しません。

しかし、これがlassevksの提案のひねりです:

_SELECT primary_key FROM the_table
_

PHPでmysql_num_rows()を使用して、上記の結果に基づいて乱数を作成します。

_SELECT * FROM the_table WHERE primary_key = Rand_number
_

余談ですが、_SELECT * FROM the_table_の処理速度は次のとおりです。
mysql_num_rows()に基づいて乱数を作成し、データポインターをそのポイントに移動しますmysql_data_seek()。これは、たとえば100万行の大きなテーブルでどれほど遅くなりますか?

0
Adde