mysqlの大きなテーブルからランダムな行をすばやく選択する
質問
大きなmysqlテーブルからランダムな行を選択する高速な方法は何ですか?
私はphpで作業していますが、別の言語であっても解決策に興味があります。
解決
すべてのIDを取得し、そこからランダムなIDを選択して、行全体を取得します。
IDに穴がなく連続していることがわかっている場合は、最大値を取得してランダムなIDを計算できます。
あちこちに穴があり、ほとんど連続した値があり、わずかに歪んだランダム性を気にしない場合は、最大値を取得し、IDを計算し、IDが1以上の最初の行を選択しますあなたが計算した。スキューが発生する理由は、そのようなホールに続くIDは、別のIDに続くものよりも選択される可能性が高いためです。
ランダムに注文した場合、手にひどいテーブルスキャンが行われますが、クイックという言葉はそのようなソリューションには適用されません。
それをしないでください。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;
MediaWikiは興味深いトリックを使用します(WikipediaのSpecial:Random機能の場合):記事のあるテーブルには、ランダムな番号(記事の作成時に生成される)を含む追加の列があります。ランダムな記事を取得するには、乱数を生成し、乱数列の値が次に大きいまたは小さい(覚えていない)記事を取得します。インデックスを使用すると、これは非常に高速になります。 (およびMediaWikiはPHPで記述され、MySQL用に開発されています。)
この方法では、結果の数値の分布が不適切な場合に問題が発生する可能性があります。 IIRC、これはMediaWikiで修正されているため、この方法で行う場合は、コードを見て、現在の実行方法を確認する必要があります(おそらく、定期的に乱数列を再生成します)。
これはかなり迅速に実行されるソリューションであり、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;
おそらく次のようなことができます:
SELECT * FROM table
WHERE id=
(FLOOR(RAND() *
(SELECT COUNT(*) FROM table)
)
);
これは、ID番号がすべて連続しており、隙間がないことを前提としています。
計算されたランダム値を含む列を各行に追加し、それを順序句で使用して、選択時の結果を1つに制限します。これは、ORDER BY RANDOM()
が原因でテーブルをスキャンするよりも速く動作します。
更新:もちろん、取得時にSELECT
ステートメントを発行する前に、ランダムな値を計算する必要があります。たとえば、
SELECT * FROM `foo` WHERE `foo_rand` >= {some random value} LIMIT 1
簡単だが遅い方法は(小さなテーブルに適しています)
SELECT * from TABLE order by RAND() LIMIT 1
擬似コード内:
sql "select id from table"
store result in list
n = random(size of list)
sql "select * from table where id=" + list[n]
これは、id
が一意の(プライマリ)キーであることを前提としています。
クエリのみを使用し、rand()による順序なしでランダムな行を生成する別の方法があります。 ユーザー定義変数が含まれます。 テーブルからランダムな行を生成する方法
テーブルからランダムな行を見つけるには、don <!>#8217; tを使用してORDER BY RAND()を使用します。これにより、MySQLが完全なファイルソートを実行し、必要な制限行数のみを取得するようになります。この完全なファイルソートを回避するには、where句でのみRAND()関数を使用します。必要な行数に達するとすぐに停止します。 見る http://www.rndblog.com/how-to- select-random-rows-in-mysql /
このテーブルの行を削除しない場合、最も効率的な方法は次のとおりです。
(ミニナム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
特定のテーブル(「単語」など)から複数のランダムな行を選択するために、私たちのチームはこの美しさを思いつきました:
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
古典的な<!> quot; SELECT id FROM table ORDER BY RAND()LIMIT 1 <!> quot;実際にOKです。
MySQLマニュアルの次の抜粋を参照してください。
ORDER BYでLIMIT row_countを使用する場合、MySQLは結果全体をソートするのではなく、ソートされた結果の最初のrow_count行を見つけるとすぐにソートを終了します。
注文すると、全スキャンテーブルが実行されます。 select count(*)を実行し、後で0から最後のレジストリの間のランダムなrow = rownumを取得する場合に最適です
Jan Kneschkeのこのリンクをご覧ください。 このSOの回答 as彼らは両方とも同じ質問について議論します。 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
私は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()
に移動します。百万行などの大きなテーブルでこれはどのくらい遅くなりますか?
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 <!より多くの結果と制限。
ここには多くの解決策があります。 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テーブルからランダムな行を選択する最も速い方法
ありがとう ジル - www.techinfobest.com
以下のクエリを使用して、ランダムな行を取得します
SELECT user_firstname ,
COUNT(DISTINCT usr_fk_id) cnt
FROM userdetails
GROUP BY usr_fk_id
ORDER BY cnt ASC
LIMIT 1
私の場合、テーブルには主キーとして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;
これを使用し、仕事が完了しました こちら
SELECT * FROM myTable WHERE RAND()<(SELECT ((30/COUNT(*))*10) FROM myTable) ORDER BY RAND() LIMIT 30;
これを行うための関数を作成し、ここで最も可能性の高いベストアンサーと最も速いアンサーを作成してください!
長所-ギャップがあっても非常に高速で動作します。
<?
$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
?>
このコードはテストされていませんが、ギャップがあってもランダムなエントリを返すための実用的な概念であることに留意してください。ギャップがロード時間の問題を引き起こすほど大きくない限り。
高速でダーティなメソッド:
SET @COUNTER=SELECT COUNT(*) FROM your_table;
SELECT PrimaryKey
FROM your_table
LIMIT 1 OFFSET (RAND() * @COUNTER);
MyISAMテーブルの最初のクエリの複雑さはO(1)です。
2番目のクエリは、テーブルの完全スキャンを伴います。複雑さ= O(n)
汚れた手っ取り早い方法:
この目的のためだけに別のテーブルを保持してください。元のテーブルに挿入するときは常に、このテーブルにも同じ行を挿入する必要があります。仮定:削除なし。
CREATE TABLE Aux(
MyPK INT AUTO_INCREMENT,
PrimaryKey INT
);
SET @MaxPK = (SELECT MAX(MyPK) FROM Aux);
SET @RandPK = CAST(RANDOM() * @MaxPK, INT)
SET @PrimaryKey = (SELECT PrimaryKey FROM Aux WHERE MyPK = @RandPK);
DELETEが許可されている場合、
SET @delta = CAST(@RandPK/10, INT);
SET @PrimaryKey = (SELECT PrimaryKey
FROM Aux
WHERE MyPK BETWEEN @RandPK - @delta AND @RandPK + @delta
LIMIT 1);
全体的な複雑さはO(1)です。
SELECT DISTINCT * FROM yourTable WHERE 4 = 4 LIMIT 1;