Перепишите запрос, чтобы использовать соединения вместо подзапроса - PullRequest
2 голосов
/ 12 сентября 2011

У меня есть следующий запрос, который выполняется как часть задания 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 голосов
/ 12 сентября 2011

Замена субвыбора на 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
0 голосов
/ 12 сентября 2011

Попробуйте что-то вроде этого:

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