2012-05-11 13 views
2

私は約30kレコードでいっぱいのdbテーブルを持っています。データベースからランダムレコードを選択するこの方法の欠陥?

一度に1つずつレコードをランダムに選択し(ユーザーが要求した場合)、そのレコードをテーブルから削除し、別のテーブルに挿入したいとします。

私は、ORDER BY RAND()の処理が非常に遅いと聞いたことがあります。だから私はこのアルゴリズム(擬似コード)を使用しています:30Kレコードを、今

lowest = getLowestId(); //get lowest primary key id from table 
highest = getHighestId(); //get highest primary key id from table 

do 
{ 
    id = rand(lowest, highest); //get random number between a range of lowest id and highest id 
    idExists = checkIfRandomIdExists(id); 
} 
while (! idExists); 

row = getRow (id); 
process(row); 
delete(id); 

を、私は非常に迅速にランダムIDを取得するように見えます。しかし、テーブルのサイズが15k、10k、5k、100などに減少すると(数ヶ月になる可能性があります)、これが遅くなることが懸念されます。

この方法をより効果的にするために何かを行うことができますか、またはこの方法の代わりにORDER BY RAND()を実行する必要がある行のカウントがありますか? (例えば、5k行が残っている場合はORDER BY RAND()を実行しますか?)

+1

乱数を扱うときは、通常反復しないのが最善です。代わりに、すべての可能なIDでいっぱいの配列を取得し、ランダムに選択してみてください。 –

+0

[Linq Orderby random]の可能な複製(http://stackoverflow.com/questions/3339192/linq-orderby-random) –

答えて

2

I:リミットで

select floor(count(*) * rand()) from thetable; 

使ったレコード番号(例えば、chosenrec):それは、レコードの数を決定し、レコードで選択するかもしれない行うに

+0

ありがとう、非常にエレガントなソリューション。 –

+0

これはさらに多くのリソースを必要とします。それが永久的なテーブルならば、いつも同じようになります。それがメモリテーブルの場合、私はそれを行うことのポイントを見ていない(質問の所有者はサーバー側の言語で同じことを納得した)。とにかく私の意見では、dbロジックとアプリケーションロジックを混在させるのは良いことではありません。 dbmsが物事を処理するようにしてください。 –

+1

@CataCata何よりも多くのリソース?この場合、ランダムな順序が1回作成されると問題はないようです。このソリューションでは、実際に存在するレコードのみを注文し、他のテーブルの対応するレコードが削除されるとシャッフルレコードを削除します。一度に1つのランダムレコードを選択し、同じレコードを複数回選択できる問題があった場合、この解決策は不適切です。 – Andrew

3

このメソッドを使用してランダムIDを取得できますが、存在するかどうかを確認する代わりに試してみてください。

SELECT * FROM table WHERE id >= $randomId ORDER BY id LIMIT 0,1 

次に、失敗した場合は失敗します。

select * from thetable limit chosenrec, 1; 
+0

確かに、whoops。 – Dan

+0

これはIDがシーケンシャル*であることが保証されている場合にのみユニフォームになります(行が**削除**されていない限りはまれです)* –

+1

これは効率的ですが、ID (それがOPにとって重要であるならば)均一な方法で。 ID間のギャップが大きい場合、IDが大きいほど選択される可能性が高くなります。値1と100の表に2つのIDを持つ極端な例を見てください。この方法では、IDの100%を選択します(一様な選択方法では50%ではなく)。 –

3

一つの方法別の表の代わりにFisher-Yates Shuffleをお勧めします。外部キー制約を気にしないでください、特に

CREATE TABLE Shuffle 
(
    SequentialId INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    OtherTableId INT NOT NULL 
) 

:これを生成するには、のようなテーブルを作成します。たとえば、SQL Serverでは、ON DELETE CASCADEという外部キー制約を追加するとします。それがMySQLで実行可能なストレージエンジンを持っているなら、それを手に入れてください。今

、お好みの言語で:(@Truthはコメントで示唆したように)

  1. 他のテーブル内のすべてのIDの配列を取得します。
  2. Fisher-Yatesを使用してこれらのIDをシャッフルします(線形時間がかかります)。
  3. Shuffleテーブルに順に挿入します。

は今、あなたはランダムな順序を持っているので、あなただけのINNER JOINShuffleテーブルに、そしてORDER BY Shuffle.SequentialIdは、最初のレコードを検索することができます。 を実行する方法がない場合は、手動でShuffleからレコードを削除することができます。

+0

MySQLの 'limit'式でサブクエリを実行できますか? 'テーブルを選択してフロア(カウント(*)*ランドとフロア)を選択)、1;' –

+0

@ BlueRaja-DannyPhlughoeft:わからない。私は実際に解決策を投稿する前にそれを試しました。なぜなら、解決策をはるかに良くするからです。しかし、私は正しい構文(それが許されるならば)を考え出すことに失敗しました。 –

関連する問題