2010-05-07 44 views
1

我需要优化一个查询的排名是永久的(查询本身的作品,但我知道这是可怕的,我刚刚试了很多记录,它会给超时) 。优化慢速排名查询​​

我会简单介绍一下这个模型。我有3个表格:球员,球队和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),但这并没有削减它

我试了一些querys didn'将不起作用,但设法得到这个工作。

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

你永久留下曾经在player_team发生的每一个球员的团队组合?你是不是以任何方式标记这一点(一个历史关系为0的列,当前一个很好的情况下为1)? – marr75 2010-05-07 13:25:51

+0

是的,我要离开组合,因为我必须保留一个日志。我想过有一面旗帜,如果没有更好的选择,可能会这样。但我也许有更好的办法。 (我是一个sql noob!)感谢您的建议,但。 – 2010-05-07 13:31:53

+0

请发表您的解释。 – 2010-05-07 13:36:25

回答

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)这个工作速度快的指数(按照这个顺序)。

+0

感谢Quassnoi。我认为你的意思是在条件下pt.id = q.mid;改变这一点,并工作。我尝试过,结果非常快。没有检查结果是否正确,但会尽快完成。再次感谢! – 2010-05-07 13:56:50

+1

+1记忆索引 – 2010-05-07 14:03:10

+0

对不起,我的意思是这个第二个ON条件,它应该是“t.id = q2.team_id”而不是“t.team_id = q2.team_id” – 2010-05-07 15:01:11

1

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

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

+0

谢谢。这是我想到的第一个想法之一(但是有一个实际的表格)。我正在考虑的另一个选项是有一个标志,将player_team关系标记为当前/活动。 – 2010-05-07 13:34:23

2

它的子查询正在杀死它 - 如果您在player_team表中添加current字段,那么您给它的值为1(如果它是最新的),如果它是旧的,则为0您可以通过执行以下操作来简化此操作:

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)或通过将历史数据移动到完全不同的表格(取决于您的情况,这可能是矫枉过正),能够查看哪一行是最新的。

+0

谢谢。我正在考虑添加该列。只是想看看是否有其他选择。 – 2010-05-07 13:36:53

+0

随着当前标志,你可以添加两列,activate_datetime和inactivate_datetime这样你就会知道,当实际的转变是发生了。 – 2010-05-07 13:49:48

+0

@Nitin Midha。感谢您的建议。实际上我有一个“已创建”列来存储插入行的时间戳(这是玩家加入团队的时间)。我只是试图在文章中留下不那么重要的东西,而不是添加太多混乱。 – 2010-05-07 14:05:40

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; 
0

你可以列“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行更新到当前= 0。

您还可以添加,而不是列“last_team_id”,并保持它的当前团队那里,你得到最快的结果对于上面的查询,但它可能是与其他查询帮助较小。