我需要优化查询以获取永久性的排名(查询本身可行,但我知道它很糟糕,我只是尝试了大量记录并且它会超时)。
我将简要解释一下这个模型。我有3个桌子:球员,球队和球员队员。我有球员,可以属于球队。听起来很明显,玩家存储在玩家桌面和团队中。在我的应用程序中,每个玩家可以随时切换团队,并且必须保留日志。但是,在给定时间,玩家被认为仅属于一个团队。现在的球员队伍是他加入的最后一支球队。
我认为球员和球队的结构不相关。我每个都有一个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
答案 0 :(得分:2)
它正在杀死它的子查询 - 如果你在current
表上添加一个player_team
字段,如果它是最新的那么你给它值= 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
)或通过将历史数据移动到完全不同的表(根据您的情况,这可能是过度杀伤),能够看到哪一行是最新的更好
答案 1 :(得分: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)
上创建一个索引(按此顺序),以便快速工作。
答案 2 :(得分:1)
我有时会发现MySQL中更复杂的查询需要分成两部分。
第一部分将所需数据拉入临时表,第二部分是试图操纵创建的数据集的查询。这样做肯定会带来显着的性能提升。
答案 3 :(得分: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;
答案 4 :(得分: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行更新为current = 0.
您还可以添加一个列“last_team_id”并保留其当前团队,您可以获得上述查询的最快结果,但对其他查询可能没什么帮助。