我们有一个游戏,玩家通过地图,并为其得分。我们需要找出哪些尝试在每张地图上都有高分,并将其isHighScore
标记设置为true。只有一次尝试可以获得每个地图的高分 - 如果两次尝试具有相同的分数,则只有按时间顺序排列的尝试才应设置isHighScore
标记。
我们目前的SQL看起来像这样:
UPDATE attempts AS A1
SET isHighScore = 1
WHERE A1.ID =
(
SELECT A2.ID
FROM (SELECT ID, score, mapID, `date` FROM attempts) AS A2
WHERE A2.mapID = A1.mapID
ORDER BY A2.score DESC, A2.date ASC
LIMIT 1
)
((SELECT ... FROM attempts)
子查询归于this issue)
上面的时间要比在超过75k条目的表上运行超时(并且是的,mapID, score, date
上有一个索引)。我假设这是因为最里面的查询将整个attempts
表复制到临时表,但将WHERE mapID = A1.mapID
条件移动到该查询会产生语法错误,所以我不知道该怎么做那。此外,内部查询为每一行运行一次 - 也许有办法解决这个问题?
有没有人知道在MySQL中编写此查询的更好方法?
答案 0 :(得分:1)
您可以尝试使用update-join。
UPDATE attempts
RIGHT JOIN
(
SELECT id FROM attempts a1
WHERE NOT EXISTS
(
SELECT 0 FROM attempts a2
WHERE a2.mapID = a1.mapID AND
(a2.score > a1.score OR (a2.score = a1.score AND a2.date < a1.date))
)
) tmp ON tmp.id = attempts.id
SET attempts.isHighestScore = 1;
如果您在mapID列和得分列上放置索引,这应该相当快。
答案 1 :(得分:0)
您可以尝试使用相关子查询仅匹配相同地图的行,其中没有其他行具有更高分数,或者如果存在具有相同分数的行,则没有其他行具有更早的日期:
UPDATE attempts
SET isHighScore = 1
WHERE ID IN (
SELECT ID FROM (
SELECT ID
FROM attempts a1
WHERE NOT EXISTS (
SELECT 0
FROM attempts a2
WHERE a2.mapID = a1.mapID
AND (a2.score > a1.score OR
(a2.score = a1.score AND a2.date < a1.date))
)
) a0
)
A SQLFiddle。
不要忘记将旧的高分设置为0。
答案 2 :(得分:0)
这有效,但我不确定性能,试一试。这是fiddle。
UPDATE attempts SET isHighScore = 1 WHERE ID IN
(
SELECT ID FROM
(
SELECT a5.* FROM attempts a5
INNER JOIN
(
SELECT mapID, max(score) as max_score, min(date) as min_date FROM
(
SELECT a1.id, a1.mapID, a1.score, a1.date
FROM attempts a1
INNER JOIN
(
SELECT mapID, max(score) as max_score
FROM attempts
GROUP BY mapID
) a2
ON
a1.mapID = a2.mapID and a1.score = a2.max_score
) a3
GROUP BY mapID
) a4
ON a5.mapID = a4.mapID and a5.score = a4.max_score and a5.date = a4.min_date
)a6
);
<强>解释强>:
1- inner most group by为每个mapID提供max_score和mapID
2-这与原始表连接以查找这些尝试的ID和日期,但是当max_score上存在平局时,每个mapID可能包含多行
3-再次按mapID分组以获得分钟(日期)
4-再次与原始表连接以获取更新的ID
答案 3 :(得分:-1)
对于这样的事情,我总是推荐一个Temp表。在这种情况下,临时表可能有列 - (id,highest_score,map) -
create temporary table temp1
select min(id) as id, highest_score, map
from attempts
where <<attempts of today>>
group by map;
现在直接使用临时表连接尝试并标记它们。
update temp1 as t inner join attempts as a on a.id = t.id
set a.isHighestScore = 1;
Drop temporary table temp1;
(如果启用了复制,请使用永久表或事务处于更安全的一面)