1

I have the following query, all relevant columns are indexed correctly. MySQL version 5.0.8. The query takes forever:

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 output:

----+--------------------+-------+-------+-----------------------+---------+---------+------+------+-------------
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   |         |         |      |      |            
----+--------------------+-------+-------+-----------------------+---------+---------+------+------+-------------

Why?

4

4 回答 4

2

您的查询很慢,因为在编写时您正在考虑 9,610 行,因此在 WHERE 子句中执行 9,610 SELECT 子查询。您确实应该首先将查询重写为 JOINmemberssubscriptions表,您的 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 回答
2

您的子选择引用父查询中的值。这被称为相关(依赖)子查询,并且这样的查询必须对父查询中的每一行执行一次,这通常会导致性能不佳。将查询重写为 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 回答
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 回答
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 回答