1

次のクエリがあります。関連するすべての列に正しくインデックスが付けられています。MySQL バージョン 5.0.8。クエリには永遠に時間がかかります:

SELECT COUNT(*) FROM `members` `t` WHERE t.member_type NOT IN (1,2)
AND ( SELECT end_date FROM subscriptions s
WHERE s.sub_auth_id = t.member_auth_id AND s.sub_status = 'Completed'
AND s.sub_pkg_id > 0 ORDER BY s.id DESC LIMIT 1 ) < curdate( )

EXPLAIN出力:

----+--------------------+-------+-------+-----------------------+---------+---------+------+------+-------------
id  | select_type        | table | type  | possible_keys         | key     | key_len | ref  | rows | Extra
----+--------------------+-------+-------+-----------------------+---------+---------+------+------+-------------
1   | PRIMARY            | t     | ALL   | membership_type       | NULL    | NULL    | NULL | 9610 | Using where
----+--------------------+-------+-------+-----------------------+---------+---------+------+------+-------------
2   | DEPENDENT SUBQUERY | s     | index | subscription_auth_id, | PRIMARY | 4       | NULL |    1 | Using where
    |                    |       |       | subscription_pkg_id,  |         |         |      |      |            
    |                    |       |       | subscription_status   |         |         |      |      |            
----+--------------------+-------+-------+-----------------------+---------+---------+------+------+-------------

なんで?

4

4 に答える 4

2

副選択は、親クエリの値を参照します。これは相関 (依存) サブクエリと呼ばれ、このようなクエリは親クエリのすべての行に対して 1 回実行する必要があり、パフォーマンスの低下につながることがよくあります。多くの場合、クエリを JOIN として書き直す方が高速です。たとえば、次のようになります。

(注:テスト用のサンプル スキーマがないと、これがより高速で正しいかどうかを事前に判断することはできません。少し調整する必要があるかもしれません):

SELECT COUNT(*) FROM members t 
LEFT JOIN (
 SELECT sub_auth_id as member_id, max(id) as sid FROM subscriptions
 WHERE sub_status = 'Completed'
 AND sub_pkg_id > 0
 GROUP BY sub_auth_id
 LEFT JOIN (
  SELECT id AS subid, end_date FROM subscriptions
  WHERE sub_status = 'Completed'
  AND sub_pkg_id > 0
 ) sdate ON sid = subid
) sub ON sub.member_id = t.member_auth_id
WHERE t.member_type NOT IN (1,2)
AND sub.end_date < curdate( )

ここでのロジックは次のとおりです。

  1. メンバーごとに、最新のサブスクリプションを見つけます。
  2. 最新のサブスクリプションごとに、その終了日を見つけます。
  3. これらの member-latest_sub_date ペアをメンバー リストに追加します。
  4. リストをフィルタリングします。
于 2012-09-16T08:18:00.750 に答える
2

書かれているように、9,610行を考慮しているため、WHERE句で9,610のSELECTサブクエリを実行しているため、クエリが遅くなります。members最初におよびテーブルを結合するようにクエリを書き直す必要がsubscriptionsあります。これには、WHERE 条件がまだ適用される可能性があります。

編集:これを試してください。

SELECT COUNT(*)
FROM `members` `t`
JOIN subscriptions s ON (s.sub_auth_id = t.member_auth_id)
WHERE t.member_type NOT IN (1,2)
AND s.sub_status = 'Completed'
AND s.sub_pkg_id > 0
AND end_date < curdate()
ORDER BY s.id DESC LIMIT 1
于 2012-09-16T08:07:55.137 に答える
0

私は最終的に次のようにしました:

select count(*) from members t 
JOIN subscriptions s ON s.sub_auth_id = t.member_auth_id
WHERE t.membership_type > 2 AND s.sub_status = 'Completed' AND s.sub_pkg_id > 0 
AND  s.sub_end_date < curdate( ) 
AND s.id = (SELECT MAX(ss.id) FROM subscriptions ss WHERE ss.sub_auth_id =   t.member_auth_id)

この問題は、MySQL 6 まで修正されないバグによるものだと思います。

于 2012-09-18T05:01:05.880 に答える
0

警告: 私は MySQL の専門家ではありませんが、別の SQL フレーバー (VFP) にはかなり精通していますが、次の場合に時間を節約できると思います。

  1. * の代わりに、たとえば memberid としましょう。

  2. 比較は(有効な場合)NOT IN (1,2)に置き換えられます。> 2

  3. サブセレクトのORDER BYは不要だと思います。最後に完了したサブスクリプションを取得しようとしていますか?

  4. < curdate()、サブセレクトの 内にある必要がありますWHERE

    (SELECT end_date FROM subscriptions s
    WHERE s.end_date < curdate() and s.sub_auth_id = t.member_auth_id AND 
    s.sub_status =  'Completed' AND s.sub_pkg_id > 0 ORDER BY s.id DESC LIMIT 1 )
    
  5. サブセレクトを調整して、できるだけ早くセットを縮小してください。最初の条件は、発生する可能性が最も低いものにする必要があります。

于 2012-09-16T08:02:58.087 に答える