LIMITを使用してmySQLでサンプリングされた行の総数をカウントする

一人の良い人がHabrに行きたいです。 彼の善意を確認するために、彼はあなたに届ける記事を書きました。



確かに多くの人がmySQLのFOUND_ROWS()関数の存在を知っています。 これは、指定された条件を満たすテーブル内の行の総数を計算するために最もよく使用されます。 通常、次のようにSQL_CALC_FOUND_ROWSディレクティブとともに使用されます。



Mysql> SELECT SQL_CALC_FOUND_ROWS * FROM table WHERE column > 1 LIMIT 0, 50;





Mysql> SELECT FOUND_ROWS();







2番目のクエリの結果は、条件 "column> 1"(最初のクエリで返されたものだけでなく)を満たすテーブル "table"の行の総数になります。

この関数のいくつかの機能は、SQL_CALC_FOUND_ROWSディレクティブなしで使用する場合に考慮する必要があります。mySQLのドキュメントは誠意をもって警告しています。この場合、処理された行の総数を返します(返されません!)。 例:



Mysql> SELECT * FROM table LIMIT 0, 50;





Mysql> SELECT FOUND_ROWS();







結果は、予想どおり、「50」になります。 ただし、次のクエリはあまり期待しない結果を返します。



Mysql> SELECT * FROM table WHERE column > 1 LIMIT 50, 50;





Mysql> SELECT FOUND_ROWS();







最初のクエリが50行を返すという事実にもかかわらず、結果は「100」になります。 mySQLは正確に100行を調べる必要がありました。



さらに、SQL_CALC_FOUND_ROWSでUNIONを使用する場合、いくつかの特定の要件も表示されます。



MySQLのドキュメントでは、この関数を使用して、クエリパラメータを満たすテーブル内の行の総数を計算することを明示的に推奨しています(たとえば、リストをページに分割する場合)。



Mysql> SELECT * FROM table WHERE column > 1 LIMIT 50, 50;





Mysql> SELECT COUNT(*) FROM table WHERE column > 1;







質問:FOUND_ROWS()関数を使用するオプションが本当に速い場合、なぜどこでも使用されないのですか? 私は両方のアプローチを比較することにしました。 このためにテーブルが作成されました:

CREATE TABLE `table_1` (

`id` int (10) unsigned NOT NULL auto_increment,

`column_1` varchar (32) default NULL ,

`column_2` varchar (32) default NULL ,

PRIMARY KEY (`id`),

KEY `column_2` (`column_2`)

) ENGINE=MyISAM AUTO_INCREMENT=1;



* This source code was highlighted with Source Code Highlighter .








テーブルには3つの列が含まれます。auto_incrementとPRIMARYキーを持つid数値フィールド。 インデックスのないテキスト列column_1とインデックスcolumn_2のあるテキスト列。 テーブルは、次のPHPスクリプトを使用して作成されました。



<?php

for ($i = 0; $i < 457128; $i ++) {

$db->insert ( 'table_1' , array ( 'column_1' , 'column_2' ), array ( md5(uniqid(rand(), true )), md5(uniqid(rand(), true )) ) );

}

?>



* This source code was highlighted with Source Code Highlighter .








$ dbは、mySQLを操作するためのPHP関数のラッパーです。 特に、データベースクエリの実行時間を測定します。

次に、次のタイプのサンプルが作成されました。





各クエリは異なるパラメータで10回実行されましたが、1回の反復で同じタイプの「標準」クエリとSQL_CALC_FOUND_ROWSクエリのパラメータは同じです。 すなわち:



for ($i = 0; $i < 10; $i ++) {

$id_limit = rand(15000, 20000);

$id_limit_2 = rand(15000, 20000);



$column_1_limit = rand(1, 9);

$column_2_limit = rand(1, 9);



$column_1_limit_2 = rand(10, 20);

$column_2_limit_2 = rand(10, 20);



$column_1_limit_3 = rand(20, 30);

$column_2_limit_3 = rand(20, 30);

// –

}



* This source code was highlighted with Source Code Highlighter .








テスト結果は図で見ることができます:



画像



mySQLのドキュメントにあるように、すべてが明確ではないことがわかりました。 インデックス付きの列によるサンプリングを使用する場合、「クラシック」スキームは間違いなく高速です。 インデックスのない列と混合クエリを使用する場合、FOUND_ROWS()関数は高速になりますが、そのゲインは非常に小さくなります。

したがって、アプローチの選択は、特定の各ケースの問題です。 パフォーマンスがほとんど重要でなく、コードの記述の利便性と速度が重要な場合、FOUND_ROWS()+ SQL_CALC_FOUND_ROWSを使用することは非常に可能です。 それ以外の場合、明らかに、2つのクエリの古典的なスキームを使用することが望ましいです。

テストは2台のマシンで実行されました。



このグラフは、Windows XPでの結果を示しています。 結果(サンプリング時間ではない)は、両方のシステムで同じです。



All Articles