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

发布于 2024-12-04 01:53:06 字数 2207 浏览 3 评论 0原文

我有以下查询作为 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                               |
+----+--------------------+-------+--------+----------------------------------------+-----------------------------+---------+--------------------------+------+-------------------------------------------+

I have the following query that runs as part of a CRON job, which performs very badly. I want to rewrite it, maybe converting the subquery into a join, but I'm not sure how.

The query boils down to something like this:

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

Explain output:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

阿楠 2024-12-11 01:53:06

用 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

Replacing the sub select with a LEFT OUTER JOIN should return equivalent results.

SQL Statement

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
栀梦 2024-12-11 01:53:06

尝试这样的事情:

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

Try something like this:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文