2

我们有一个游戏,玩家在地图上奔跑,并为此得分。我们需要找出哪些尝试在每张地图上得分最高,并将其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)子查询是由于这个问题

在大约 75k 条目的表上运行上述操作所需的时间比超时时间要长(是的,在 上有一个索引mapID, score, date。我认为这是因为最里面的查询将整个attempts表复制到一个临时表,但是将WHERE mapID = A1.mapID条件移动到该查询中会产生语法错误,所以我不知道该怎么做。此外,内部查询为每一行运行一次 - 也许有办法解决这个问题?

有谁知道在 MySQL 中编写此查询的更好方法?

4

4 回答 4

1

您可以尝试使用更新连接。

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 列和 score 列上放置索引,这应该相当快。

于 2013-03-13T18:58:06.287 回答
0

这可行,但我不确定性能,试一试。这是小提琴

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

于 2013-03-13T19:59:39.480 回答
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
       )

一个SQLFiddle

不要忘记将旧的高分设置为 0。

于 2013-03-13T18:52:12.663 回答
-1

对于这样的事情,我总是推荐一个临时表。在这种情况下,临时表可能有列 - (id, high_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;

(如果启用了复制,请使用永久表或事务以更安全)

于 2013-03-13T19:47:40.120 回答