10

次のクエリを実行したかった:

-- Main Query    
SELECT COUNT(*) FROM table_name WHERE device_id IN 
     (SELECT DISTINCT device_id FROM table_name WHERE NAME = 'SOME_PARA')

この次のクエリ(メインクエリからのサブクエリ):

SELECT DISTINCT device_id FROM table_name WHERE NAME = 'SOME_PARA'

7秒で実行され、210万行のテーブルから2691行が得られます。

上記のメインクエリを実行しましたが、5分以上待機しても実行されています。

最後に、サブクエリを個別に実行し、結果から2691レコードを取得して、次のクエリを実行しました。

-- Main Query (improvised)    
SELECT COUNT(*) FROM table_name WHERE device_id IN 
     ("device_id_1", "device_id_2", ....., "device_id_2691")

驚いたことに、これは私に40秒以内に答えを与えました。

何が得られますか?MySQLが私が使用したのと同じ手法を使用して、すぐに答えを出さないのはなぜですか?私は何か間違ったことをしていますか?

4

4 に答える 4

5

残念ながら、MySQLはINを使用したサブクエリの最適化にはあまり適していません。これはMySQLのドキュメントからのものです:

INのサブクエリの最適化は、=演算子やIN(value_list)演算子ほど効果的ではありません。

INサブクエリのパフォーマンスが低下する一般的なケースは、サブクエリが少数の行を返すが、外部クエリがサブクエリの結果と比較するために多数の行を返す場合です。

問題は、INサブクエリを使用するステートメントの場合、オプティマイザがそれを相関サブクエリとして書き換えることです。無相関のサブクエリを使用する次のステートメントについて考えてみます。

SELECT ... FROM t1 WHERE t1.a IN(SELECT b FROM t2);

オプティマイザーは、ステートメントを相関サブクエリに書き換えます。

SELECT ... FROM t1 WHERE EXISTS(SELECT 1 FROM t2 WHERE t2.b = t1.a);

内部クエリと外部クエリがそれぞれM行とN行を返す場合、実行時間は、無相関のサブクエリの場合のようにO(M + N)ではなく、O(M×N)のオーダーになります。

つまり、INサブクエリは、サブクエリが返すのと同じ値をリストするIN(value_list)演算子を使用して記述されたクエリよりもはるかに遅くなる可能性があります。

代わりにJOINを使用してみてください。

MySQLは裏返しに機能するため、次のようにサブクエリをさらに別のサブクエリ内にラップすることでMySQLをだますことができる場合があります。

SELECT COUNT(*) FROM table_name WHERE device_id IN
     (SELECT * FROM (SELECT DISTINCT device_id FROM table_name WHERE NAME = 'SOME_PARA') tmp)

JOINソリューションは次のとおりです。

SELECT COUNT(DISTINCT t2.id) FROM table_name t1
  JOIN table_name t2
    ON t2.device_id = t1.device_id
  WHERE t1.NAME = 'SOME_PARA'

私は内側から始めて、外にも出ていることに注意してください。

于 2012-08-02T18:26:44.480 に答える
4

編集:この場合のMySQLの愚かさの理由はわかりません:)、このバグレポートはこの場合に関連しているようです。回避策は、JOINを使用することです

SELECT 
    COUNT(t1.device_id) 
FROM table_name t1 
JOIN (
    SELECT DISTINCT device_id FROM table_name WHERE NAME = 'SOME_PARA'
) as t2 ON t2.device_id = t1.device_id 
于 2012-08-02T18:02:32.353 に答える
2

クエリを次のように書き直すことができると思います。

 SELECT sum(NumOnDevice) 
 from (SELECT device_id, count(*) as NumOnDevice
       FROM table_name
       having sum(case when NAME = 'SOME_PARA' then 1 else 0 end) > 0
      ) t

私はこれがあなたの質問に答えないことを理解します、しかしそれはあなたを助けるかもしれません。

最適化に関しては、クエリに一連の定数を与えることと、クエリにサブクエリを与えること(結果が同じであっても)には違いがあります。最初のケースでは、クエリオプティマイザには、クエリプランを決定するためのより多くの情報があります。2つ目は、コンパイル時に情報を利用できないことです。

Mysql(ほとんどのデータベースよりも多い)は、クエリの表現方法に基づいてクエリプランを生成するようです。SQLは、手続き型言語ではなく、宣言型言語として設計されました。つまり、SQLクエリは目的の結果セットを記述し、クエリエンジンはその結果を達成するための最良の方法を決定することになっています。ただし、最良の結果を得るには、データベースエンジンを支援しなければならない場合が多くあります。

于 2012-08-02T18:43:26.077 に答える
1

MySQLに何を求めているかを確認し、table_nameのすべてのレコードを確認し、クエリを実行して取得したリストにdevice_idが含まれているかどうかを判断し、それをカウントに追加するかどうかを判断する必要があります。つまり、サブクエリを210万回実行しています。

これは、そのリストを手動で定義すると、リストをかなりすばやく切り詰めることができる理由でもあります。

于 2012-08-02T17:58:45.593 に答える