28

次のクエリを実行しています

SELECT COUNT(*)
FROM table
WHERE field1='value' AND (field2 >= 1000 OR field3 >= 2000)

field1 に 1 つのインデックスがあり、field2&field3 に合成された別のインデックスがあります。

MySQL は常に field1 インデックスを選択し、146.000 行を結合する必要があるため、他の 2 つのフィールドを使用して結合を行います。

これを改善する方法についての提案はありますか? ありがとう

(提案された解決策を試した後に編集)

提案されたソリューションに基づいて、これで遊んでいるときにMysqlでこれを見ました。

SELECT COUNT(*) FROM (SELECT * FROM table WHERE columnA = value1
UNION SELECT * FROM table WHERE columnB = value2) AS unionTable;

実行よりもはるかに遅いです:

SELECT COUNT(*)
FROM table
WHERE (columnA = value1 AND columnB = value2)
      OR (columnA = value1 AND columnC = value3)

2 つの複合インデックスを持つ:

index1 (columnA,columnB)
index2 (columnA,columnC)

非常に興味深いのは、Mysql に、両方のケースで常に index1 を使用し、index2 を使用しないクエリを「説明」するように依頼することです。

インデックスを次のように変更すると:

index1 (columnB,columnA)
index2 (columnC,columnA)

そして、クエリは次のとおりです。

SELECT COUNT(*)
FROM table
WHERE (columnB = value2 AND columnA = value1)
      OR (columnC = value3 AND columnA = value1)

次に、Mysql が動作することを発見した最速の方法です。

4

2 に答える 2

36

OR述語を分割する一般的な方法は、 を使用することUNIONです。

あなたの例はインデックスにうまく適合しないことに注意してください。field1述語を省略してもfield2 >= 1000 OR field3 >= 2000、インデックスを使用できない があります。(field1, field2)and(field1,field3)または orfield2またはfield3個別にインデックスがあれば、かなり高速なクエリが得られます。

SELECT COUNT(*) FROM
(SELECT * FROM table WHERE field1 = 'value' AND field2 >= 1000
UNION
SELECT * FROM table WHERE field1 = 'value' AND field3 >= 2000) T

派生テーブルにエイリアスを指定する必要があることに注意してください。これが、サブクエリが としてエイリアス化されている理由Tです。

実例です。列とテーブルの名前は匿名化されています!

mysql> SELECT COUNT(*) FROM table;
+----------+
| COUNT(*) |
+----------+
|  3059139 |
+----------+
1 row in set (0.00 sec)

mysql> SELECT COUNT(*) FROM table WHERE columnA = value1;
+----------+
| COUNT(*) |
+----------+
|     1068 |
+----------+
1 row in set (0.00 sec)

mysql> SELECT COUNT(*) FROM table WHERE columnB = value2;
+----------+
| COUNT(*) |
+----------+
|      947 |
+----------+
1 row in set (0.00 sec)

mysql> SELECT COUNT(*) FROM table WHERE columnA = value1 OR columnB = value2;
+----------+
| COUNT(*) |
+----------+
|     1616 |
+----------+
1 row in set (9.92 sec)

mysql> SELECT COUNT(*) FROM (SELECT * FROM table WHERE columnA = value1
UNION SELECT * FROM table WHERE columnB = value2) T;
+----------+
| COUNT(*) |
+----------+
|     1616 |
+----------+
1 row in set (0.17 sec)

mysql> SELECT COUNT(*) FROM (SELECT * FROM table WHERE columnA = value1
UNION ALL SELECT * FROM table WHERE columnB = value2) T;
+----------+
| COUNT(*) |
+----------+
|     2015 |
+----------+
1 row in set (0.12 sec)
于 2010-05-13T19:46:35.723 に答える
6

私はここに来たばかりなので、他の人の投稿にコメントすることはできませんが、これは David M. と soulmerge による投稿に関連しています。

一時テーブルは必要ありません。UNION David M. が提案した UNION は重複していません。UNION は重複しないことを意味します (つまり、行がユニオンの半分に存在する場合、もう一方の行は無視されます)。UNION ALL を使用すると、2 つのレコードが得られます。

UNION のデフォルトの動作では、重複する行が結果から削除されます。オプションの DISTINCT キーワードは、重複行の削除も指定するため、デフォルト以外の効果はありません。オプションの ALL キーワードを使用すると、重複行の削除は行われず、結果にはすべての SELECT ステートメントから一致するすべての行が含まれます。

http://dev.mysql.com/doc/refman/5.0/en/union.html

于 2010-05-13T20:13:12.890 に答える