MySQL更新表只与另一个表的值最高

时间:2015-02-10 14:53:04

标签: mysql sql

对于游戏网站。
如果玩家的分数高于他的旧分数,则记录所有游戏

所有玩家的表(超过10,000名玩家)

CREATE TABLE games (
    PlayerID INT UNSIGNED,
    Date TIMESTAMP(12),
    Score BIGINT UNSIGNED DEFAULT 0,
    #...other data
);

每月一次,我会对记录表best进行更新。擦除所有games后。

最佳球员表(前50名)

CREATE TABLE best (
    #...same as games, without final other data
    PlayerID INT UNSIGNED,
    Date TIMESTAMP(12),
    Score BIGINT UNSIGNED DEFAULT 0
);

所以我将表games中的50位最佳玩家添加到表best中:

INSERT INTO best (PlayerID, Date, Score)
   SELECT PlayerID, Date, Score FROM games ORDER BY Score DESC LIMIT 50;

之后(这就是我遇到问题的地方)我试着保留best只有最好的50个。此时best包含100行。

我必须做什么:

  • 不要多次存储同一个玩家PlayerID
  • 删除此播放器的最差Score
  • 最后,只留下前50名。

- >

+----------+---------+
| PlayerID | Score   |
+----------+---------+
| 25       | 20000   | New
| 25       | 25000   | Old best
| 40       | 10000   | Old best
| 57       | 80000   | New best
| 57       | 45000   | Old
| 80       | 35000   | New best
+----------+---------+

我最终只能保留50行(在我的例子中“最好”的行) 我尝试了很多东西,但是我没有成功实现预期的结果。

我正在使用PHP,所以如果可以通过数组中的中间存储来实现它,那也没关系。 速度不是优先级,因为它是一个每月只进行一次的操作。

2 个答案:

答案 0 :(得分:1)

以下SQL返回前50个分数:

SELECT `PlayerId`, max(`Score`) MaxScore
FROM (
    SELECT `PlayerId`, `Date`, `Score` FROM games    
    UNION
    SELECT `PlayerId`, `Date`, `Score` FROM best    
) t
GROUP BY `PlayerId`
ORDER BY `MaxScore` DESC
LIMIT 50

您可以使用结果覆盖表格best。为此,您还需要相应的Date字段,这是迄今为止缺少的字段。下一个SQL也将返回一个maxDate字段,该字段对应于高分。

SELECT t2.`PlayerId`, max(t2.`Date`) maxDate, top.`MaxScore`  
FROM
    (   
        SELECT `PlayerId`, max(`Score`) MaxScore
        FROM (
            SELECT `PlayerId`, `Date`, `Score` FROM games    
            UNION
            SELECT `PlayerId`, `Date`, `Score` FROM best    
        ) t1
        GROUP BY `PlayerId`
        ORDER BY `MaxScore` DESC
        LIMIT 50
    ) top
    LEFT JOIN (
        SELECT `PlayerId`, `Date`, `Score` FROM games    
        UNION
        SELECT `PlayerId`, `Date`, `Score` FROM best    
    ) t2 ON t2.`PlayerId` = top.`PlayerId` AND t2.`Score` = top.`MaxScore`
GROUP BY t2.`PlayerId`
ORDER BY top.`MaxScore` DESC

要将新的前50个高分转移到best表,您可以使用tmp_best之类的临时表。将最高分数插入空表tmp_best中(您必须从上面插入您的选择查询):

INSERT INTO tmp_best (`PlayerId`, `Date`, `Score`) 
    SELECT ...

在此之后,best表可以清空,然后您可以将tmp_best中的行复制到best


这是一个替代解决方案,它简化了SQL。区别 上面的解决方案是在统一数据的开头使用临时表tmp_all。在使用以下SQL之前,您必须创建tmp_all,它可以是gamesbest结构的副本。

DELETE FROM tmp_all;

INSERT INTO tmp_all
    SELECT `PlayerId`, `Date`, `Score` FROM games    
    UNION
    SELECT `PlayerId`, `Date`, `Score` FROM best    
;

DELETE FROM best;

INSERT INTO best (`PlayerId`, `Date`, `Score`) 
    SELECT t2.`PlayerId`, max(t2.`Date`) maxDate, top.`MaxScore`  
    FROM
        (   
            SELECT `PlayerId`, max(`Score`) MaxScore
            FROM tmp_all t1
            GROUP BY `PlayerId`
            ORDER BY `MaxScore` DESC
            LIMIT 50
        ) top
        LEFT JOIN tmp_all t2 ON t2.`PlayerId` = top.`PlayerId` AND t2.`Score` = top.`MaxScore`
    GROUP BY t2.`PlayerId`
    ORDER BY top.`MaxScore` DESC
;

答案 1 :(得分:0)

SELECT PlayerID, Date, Score FROM games ORDER BY Score DESC LIMIT 50
UNION
SELECT PlayerID, Date, Score FROM best

在这里,你将获得有史以来最好的50名玩家。然后,根据@ethrbunny的建议,删除最佳表并使用上述查询再次填充它。您可以使用TEMPORARY TABLE

UNION保证您不会获得重复播放器