1

我需要优化一个查询,以获得永远需要的排名(查询本身有效,但我知道这很糟糕,我刚刚用大量记录尝试过它,它给出了超时)。

我将简要解释模型。我有 3 张桌子:player、team 和 player_team。我有球员,他们可以属于一个球队。听起来很明显,球员存储在球员表中,球队存储在球队中。在我的应用程序中,每个玩家都可以随时切换团队,并且必须保留日志。但是,在给定时间,一名球员被认为只属于一个球队。玩家的当前团队是他加入的最后一个团队。

我认为球员和球队的结构并不相关。我每个都有一个 id 列 PK。在 player_team 我有:

id          (PK)
player_id   (FK -> player.id)
team_id     (FK -> team.id)

现在,每支球队都会为每个加入的球员分配一个积分。所以,现在,我想得到前 N 支拥有最多球员的球队的排名。

我的第一个想法是首先从 player_team 获取当前玩家(即每个玩家的最高记录;该记录必须是玩家当前的团队)。我没有找到一个简单的方法来做到这一点(尝试 GROUP BY player_team.player_id HAVING player_team.id = MAX(player_team.id),但这并没有削减它。

我尝试了一些不起作用的查询,但设法让它工作。

SELECT 
    COUNT(*) AS total,
    pt.team_id,
    p.facebook_uid AS owner_uid, 
    t.color 
FROM 
    player_team pt 
JOIN player p ON (p.id = pt.player_id)  
JOIN team t ON (t.id = pt.team_id) 
WHERE 
    pt.id IN (
        SELECT max(J.id) 
        FROM player_team J 
        GROUP BY J.player_id
    )  

GROUP BY 
    pt.team_id 
ORDER BY 
    total DESC 
LIMIT 50            

正如我所说,它可以工作,但看起来很糟糕,性能更差,所以我确信一定有更好的方法。有人对优化这个有什么想法吗?

顺便说一句,我正在使用mysql。

提前致谢

添加解释。(抱歉,不知道如何正确格式化)

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY     t   ALL     PRIMARY     NULL    NULL    NULL    5000    Using temporary; Using filesort
1   PRIMARY     pt  ref     FKplayer_pt77082,FKplayer_pt265938,new_index    FKplayer_pt77082    4   t.id    30  Using where
1   PRIMARY     p   eq_ref  PRIMARY     PRIMARY     4   pt.player_id    1
2   DEPENDENT SUBQUERY  J   index   NULL    new_index   8   NULL    150000  Using index
4

5 回答 5

2

它是杀死它的子查询 - 如果你current在表上添加一个字段,player_team如果它是当前的,你给它 value = 1,如果它是旧的,你可以通过这样做来简化它:

SELECT 
    COUNT(*) AS total,
    pt.team_id,
    p.facebook_uid AS owner_uid, 
    t.color 
FROM 
    player_team pt 
JOIN player p ON (p.id = pt.player_id)  
JOIN team t ON (t.id = pt.team_id) 
WHERE 
    player_team.current = 1 
GROUP BY 
    pt.team_id 
ORDER BY 
    total DESC 
LIMIT 50  

player_team对于同一关系,表中有多个条目,其中区分哪一个是“当前”记录的唯一方法是比较两个(或更多)行,我认为这是不好的做法。我以前也遇到过这种情况,你必须采取的变通办法才能让它真正发挥作用。通过进行简单的查找(在本例中where current=1

于 2010-05-07T13:33:30.510 回答
2

试试这个:

SELECT  t.*, cnt
FROM    (
        SELECT  team_id, COUNT(*) AS cnt
        FROM    (
                SELECT  player_id, MAX(id) AS mid
                FROM    player_team
                GROUP BY
                        player_id
                ) q
        JOIN    player_team pt
        ON      pt.id = q.mid
        GROUP BY
                team_id
        ) q2
JOIN    team t
ON      t.id = q2.team_id
ORDER BY
        cnt DESC
LIMIT 50

player_team (player_id, id)为此(按此顺序)创建一个索引以使其快速工作。

于 2010-05-07T13:44:47.927 回答
1

我有时发现 MySQL 中更复杂的查询需要分成两部分。

第一部分会将所需的数据提取到临时表中,第二部分将是尝试操作创建的数据集的查询。这样做肯定会带来显着的性能提升。

于 2010-05-07T13:31:55.223 回答
0

这将获得按大小排序的颜色的当前团队:

  SELECT team_id, COUNT(player_id) c AS total, t.color 
    FROM player_team pt JOIN teams t ON t.team_id=pt.team_id  
    GROUP BY pt.team_id WHERE current=1
    ORDER BY pt.c DESC
    LIMIT 50;

但是你没有给出哪个球员应该被认为是球队的所有者的条件。由于分组,您当前的查询任意将一名玩家显示为 owner_id,而不是因为该玩家是实际所有者。如果您的 player_team 表包含“所有者”列,您可以将上述查询加入到所有者查询中。就像是:

SELECT o.facebook_uid, a.team_id, a.color, a.c
FROM player_teams pt1 
  JOIN players o ON (pt1.player_id=o.player_id AND o.owner=1)
  JOIN (...above query...) a
    ON a.team_id=pt1.team_id;
于 2010-05-07T13:49:19.590 回答
0

您可以在 player 表中添加一列“last_playteam_id”,并在每次玩家使用 player_team 表中的 pk 更改他的团队时更新它。

然后你可以这样做:

SELECT 
    COUNT(*) AS total,
    pt.team_id,
    p.facebook_uid AS owner_uid, 
    t.color 
FROM 
    player_team pt 
JOIN player p ON (p.id = pt.player_id)  and p.last_playteam_id = pt.id
JOIN team t ON (t.id = pt.team_id) 
GROUP BY 
    pt.team_id 
ORDER BY 
    total DESC 
LIMIT 50   

这可能是最快的,因为您不必将旧 player_team 行更新为 current=0。

您也可以添加一列“last_team_id”并将其保留为当前团队,您可以获得上述查询的最快结果,但它对其他查询的帮助可能较小。

于 2010-05-07T14:55:15.040 回答