为什么这个MySQL查询速度慢?

时间:2012-09-16 07:38:40

标签: mysql

我有以下查询,所有相关列都已正确索引。 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 个答案:

答案 0 :(得分:2)

您的查询速度很慢,因为正如您所写,您正在考虑9,610行,因此在WHERE子句中执行9,610个SELECT子查询。你真的应该重写你的查询,首先加入memberssubscriptions表,你的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

答案 1 :(得分:2)

您的子选择引用父查询中的值。这称为correlated (dependent) subquery,并且必须对父查询中的每一行执行一次这样的查询,这通常会导致性能不佳。将查询重写为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. 过滤清单。

答案 2 :(得分: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. 调整您的子选择,以便尽快减少该组。第一个条件应该是最不可能发生的条件。

答案 3 :(得分: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之前无法修复的错误。