重写查询以使用连接而不是子查询

时间:2011-09-12 13:04:27

标签: mysql optimization join count subquery

我有以下查询作为CRON作业的一部分运行,该作业执行得非常糟糕。我想重写它,也许将子查询转换为连接,但我不确定如何。

查询可归结为以下内容:

SELECT 
    u.id, gu.level
FROM game_users gu
INNER JOIN users u ON u.id = gu.id
WHERE
    gu.points >= 5 AND
    (
        SELECT COUNT(g.id)
        FROM groups g
        INNER JOIN group_members gm ON gm.group_id = g.id
        WHERE g.active = 1
        AND gm.user_id = gu.id
    ) = 0 AND
    gu.level BETWEEN 5 AND 6 AND
    gu.ai = 0 AND
    u.last_visit > '2011-08-12 14:56:00'
ORDER BY gu.points DESC
LIMIT 1

解释输出:

+----+--------------------+-------+--------+----------------------------------------+-----------------------------+---------+--------------------------+------+-------------------------------------------+
| id | select_type        | table | type   | possible_keys                          | key                         | key_len | ref                      | rows | Extra                                     |
+----+--------------------+-------+--------+----------------------------------------+-----------------------------+---------+--------------------------+------+-------------------------------------------+
|  1 | PRIMARY            | gu    | range  | PRIMARY,level_points_ai                | level_points_ai             | 10      | NULL                     | 2016 | Using where; Using index; Using temporary |
|  1 | PRIMARY            | u     | eq_ref | PRIMARY,last_visit                     | PRIMARY                     | 4       | joomla.gu.id             |    1 | Using where                               |
|  2 | DEPENDENT SUBQUERY | g     | ref    | PRIMARY,active                         | active                      | 5       | const                    |  549 | Using where; Using index                  |
|  2 | DEPENDENT SUBQUERY | gm    | eq_ref | group_id_user_id,party_member_to_party | group_id_user_id            | 8       | joomla.g.id,joomla.gu.id |    1 | Using index                               |
+----+--------------------+-------+--------+----------------------------------------+-----------------------------+---------+--------------------------+------+-------------------------------------------+

2 个答案:

答案 0 :(得分:0)

尝试这样的事情:

SELECT 
    u.id, gu.level
FROM game_users gu
INNER JOIN users u ON u.id = gu.id
inner join (
    SELECT gm.user_id, COUNT(g.id) as count
    FROM groups g
    INNER JOIN group_members gm ON gm.group_id = g.id
    WHERE g.active = 1
    group by gm.user_id
    having count(g.id) = 0
) a on gu.id = a.user_id
WHERE gu.level BETWEEN 5 AND 6 
    AND gu.ai = 0 
    AND u.last_visit > '2011-08-12 14:56:00'
ORDER BY gu.points DESC
LIMIT 1

答案 1 :(得分:0)

LEFT OUTER JOIN替换子选择应该返回相同的结果。

SQL语句

SELECT  u.id
        , gu.level
FROM    game_users gu
        INNER JOIN users u ON u.id = gu.id
        LEFT OUTER JOIN group_members gm ON gm.user_id = gu.id
        LEFT OUTER JOIN groups g ON g.id = gm.group_id AND g.active = 1
WHERE   gu.points >= 5 
        AND g.id IS NULL
        AND gu.level BETWEEN 5 AND 6 
        AND gu.ai = 0 
        AND u.last_visit > '2011-08-12 14:56:00'
ORDER BY 
        gu.points DESC
LIMIT 1