我有以下查询,所有相关列都已正确索引。 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 | | | | |
----+--------------------+-------+-------+-----------------------+---------+---------+------+------+-------------
为什么?
答案 0 :(得分: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
答案 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( )
这里的逻辑是:
答案 2 :(得分:0)
警告:我不是MySQL专家,但在不同的SQL风格(VFP)方面相当不错,但我相信如果以下情况你会节省一些时间:
你只计算一个字段,假设是memberid,而不是*。
您的比较NOT IN (1,2)
已替换为> 2
(前提是有效)。
我认为您的子选择中的ORDER BY
是不必要的。您是否想要获得最后完成的订阅?
< 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 )
调整您的子选择,以便尽快减少该组。第一个条件应该是最不可能发生的条件。
答案 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之前无法修复的错误。