MySQLでのカスタムレコードの取得

immediatelyせずにすぐに解決できる一見普通のタスクがありますが、そのようなソリューションを集中的に使用すると、小さなものではなく問題が発生します。 これらのタスクの1つについてお話したいと思います。





問題

ここでは、PHPとMySQLで小さくシンプルなコードを書くためにアウトソーサーを利用しました。 タスクの1つがありました-MySQLデータベースのテーブルから複数の任意のレコードを選択すること。 そして、この怠け者で愚かなアウトソーサーは何をしましたか? もちろん、私はこのようなナンセンスを書いた:
SELECT * FROM tTable ORDER BY RAND ( ) LIMIT 10 ;





一見、すべてが論理的であり、正しく機能します。 10個のランダムエントリが選択されます。 しかし、このリクエストを実現するための計画を見ると、なぜバカなアウトソーサーにたくさんのわいせつな呪いを加えたのかが明らかになるでしょう。

このクエリの実行中に、MySQLは一時的なテーブルに、RAND()関数の結果が書き込まれる新しいフィールドを1つ持つ、元のテーブルのすべての(!!!)行を書き込みます。 任意の値のセット。 次に、この一時テーブルは、任意の値を持つ追加フィールドによってfilesortでソートされ、最初の10レコードが選択されます。 完全なPPC。 次に、ソーステーブルに10,000件のレコードがある場合に何が起こるか想像してください。 しかし、1,000,000の場合はどうでしょうか? しかし、このサンプルを1秒間に10回実行する必要がある場合はどうでしょう。 はい。ここでは、超大型サーバーは長い間考えられます。



しかし、あなたが少し精通している場合(そして、アウトソーシング業者は考えたくない、仕事をあきらめ、お金を飲みに行く)、あなたはエレガントで迅速なオプションを思い付くことができます。その速度は表の行の数に依存しません。



アイデア

それでは、ゆっくり始めましょう。 まず、タスクを単純化します。10個ではなく、1つのレコードのみを選択する必要があるとします。

ここではすべてが非常に簡単です。 テーブル内のレコード数で操作する必要があるのは、 キーは任意(複合、非数値)にすることができ、レコードを削除した結果として「放電」することもできます。 まず、テーブル内のエントリの総数を調べます。
SELECT COUNT ( * ) FROM tTable ;





次に、0からこのテーブルのエントリ数までの任意の数を計算します
rand_row = round(rand() * row_count);





これで、問題なく、ランダムなレコードを選択できます。
SELECT * FROM tTable LIMIT rand_row, 1;







PHPソリューション

そこで、彼らは単純化されたタスクに対処しました。 今、あなたは元のセットを克服する必要があります、すなわち 10エントリを選択します。 ここでのロジックは簡単です。0からテーブル内のエントリの数までの10個の任意の数をカウントし、前のクエリのような10個のクエリを作成し、UNIONを使用して結合する必要があります。

これを行う方法には2つのオプションがあります。PHPコードの一部としてフォーマットするか、MySQLストアドプロシージャとして使用することができます。

PHPでは、すべてが非常に簡単です。
$row_count = mysql_result ( mysql_query ( 'SELECT COUNT(*) FROM tTable;' ) , 0 ) ;

$query = array ( ) ;

while ( count ( $query ) < 10 ) {

$query [ ] = '(SELECT * FROM tTable LIMIT ' . rand ( 0 , $row_count ) . ', 1)' ;

}

$query = implode ( ' UNION ' , $query ) ;

$res = mysql_query ( $query ) ;






すべてがシンプルで高速です。 1万件のレコードを含む元のテーブルでは、パフォーマンスの向上は最初の「遅延」バージョンの12倍以上になります。

ソーステーブルにそれほど多くのレコードがなく、選択範囲内の重複行の表示が受け入れられない場合、最初に非繰り返しの任意の値のリストを作成してから、それらに対してクエリを実行できます。



MySQLソリューション

または、これをストアドプロシージャとして実行することもできます。
CREATE PROCEDURE `spRandomSelect` ( IN aSchema VARCHAR ( 50 ) , IN aTable VARCHAR ( 50 ) , IN aNumRows INTEGER ( 11 ) )

NOT DETERMINISTIC

READS SQL D

BEGIN

DECLARE iQuery VARCHAR ( 10000 ) ;

DECLARE iNumRows INTEGER ( 11 ) ;



SET iNumRows = ( SELECT `TABLE _ ROWS` FROM `information _ schema` . `TABLES` t

WHERE t. `TABLE _ SCHEMA` = aSchema AND t. `TABLE _ NAME` = aTable ) ;

SET iQuery = '' ;

loop1: LOOP

SET iQuery = CONCAT ( iQuery , '(SELECT * FROM `' , aSchema , '`.`' , aTable ,

'` LIMIT ' , ROUND ( RAND ( UNIX_TIMESTAMP ( ) + aNumRows ) * iNumRows ) , ', 1)' ) ;

IF aNumRows > 1 THEN

SET iQuery = CONCAT ( iQuery , ' UNION ' ) ;

END IF ;

SET aNumRows = aNumRows - 1 ;

IF aNumRows > 0 THEN

ITERATE loop1 ;

END IF ;

LEAVE loop1 ;

END LOOP loop1 ;

SET @iQuery = iQuery ;

PREPARE iExecStmt FROM @iQuery ;

EXECUTE iExecStmt ;

DRP PREPARE iExecStmt ;

END ;






このソリューションのパフォーマンスは、PHPで複合クエリを準備する場合よりも低くなりますが、ポイントは、純粋なSQLでの実装の可能性を示すことです。



All Articles