如何根据 SQL Server 中的最高点和最低时间计算排名

Posted

技术标签:

【中文标题】如何根据 SQL Server 中的最高点和最低时间计算排名【英文标题】:How can I calculate rank based on highest points and lowest time in SQL server 【发布时间】:2012-07-11 22:17:26 【问题描述】:

我已经搜索和搜索,但无法使其正常工作。如果有人可以帮助我,我将不胜感激。

我有一个存储所玩游戏信息的表格(小测验应用程序。) 我需要能够根据得分最高,其次是最低时间来确定排名前 5 的玩家。 (许多玩家有最高分)。 我还需要确定任何给定时间的用户排名。

这是游戏桌的示例:

user_id 学分 time_played 别名 -------- ------- ------------ -------------- 64 490 180 达普拉亚 93 690 187 超人 64 336 187 达普拉亚 75 196 192 薄片 93 182 197 超人 57 844 198 约翰·史密斯 75 350 198 薄片 64 858 384 达普拉亚 73 858 400 小家伙 57 858 412 约翰·史密斯 101 858 420 闪光灯 73 858 423 小家伙 73 858 434 小家伙 65 858 460 希娜 122 858 540 糖皇后 126 858 545 雷切尔 176 350 2417 火光 157 350 2442 大Q 161 350 2456 乔伊蓝

我正在使用的当前查询是这样的:

select top 5 alias,user_id, 
   rank() over (order by max(credits) desc,min(time_played)) as rank 
from games,users 
where games.user_id=users.id and users.id <> 20 and games.credits > 0 
group by user_id,alias

(从查询中可以看出,别名其实存放在users表中。)

我认为这个查询有效,因为它似乎显示了正确的结果,但是现在网站已经上线,并且我有成千上万的游戏,我可以看出它是错误的。

上面的查询基本上给了我 5 个可能得分最高的玩家,但它对他们的最低游戏时间进行了排名。 (所以分数较低的游戏速度更快)。

请告诉我如何调整/修改此查询或完全重写它。

(请注意,我可以使用带有循环的 php 来简单得多,但这不会很有效。)

如果需要,我很乐意提供更多信息。

提前致谢, 亚伦。

【问题讨论】:

我认为这个子句会为您解决问题 Order By Credits DESC, timePlayed ASC @StefanH - 同意 - OLAP 函数不需要聚合函数。除此之外,不要使用隐式连接语法(逗号分隔的FROM 子句,始终明确说明您的连接。这里还不错,但以后可能会变得很麻烦。另外,如果超过 5 个玩家拥有排名相同?您可能希望使用以下子句代替/补充:HAVING rank &lt; 6,它获得前 5 名玩家(最少 5 名玩家,没有最多)。 【参考方案1】:
with user_ranked_games as (
  select user_id,
         credits,
         time_played,
         row_number() over (  partition by user_id 
                              order by credits desc, time_played asc
                           ) as game_rank
    from games
)
select top 5
       rank() over (order by g.credits desc, g.time_played asc) as rank,
       g.user_id,
       u.alias,
       g.credits,
       g.time_played
  from user_ranked_games g
  join users u
    on g.user_id = u.user_id
 where g.game_rank=1
order by rank

我在SQL Fiddle 上成功测试了上述内容。我修改了数据值,使 littleguy 获得前 5 名中的 2 个。但我上面的查询正确地列出了 littleguy 在排名前 5 名的玩家中只有一次。

注意 - 我原来的 sql fiddle 有重复的用户行(我没有费心定义 PK),这会导致不同的输出,具体取决于连接的完成位置。我已经修复了数据并从 CTE 中删除了连接,并按照我最初的意图将它放在最外层的查询中。

根据评论更新

一个小的修改提供了一个查询,可以查找单个用户的用户排名和分数:

with user_ranked_games as (
  select user_id,
         credits,
         time_played,
         row_number() over (  partition by user_id 
                              order by credits desc, time_played asc
                           ) as game_rank
    from games
),
ranked_users as (
  select rank() over (order by g.credits desc, g.time_played asc) as rank,
         g.user_id,
         u.alias,
         g.credits,
         g.time_played
    from user_ranked_games g
    join users u
      on g.user_id = u.user_id
   where g.game_rank=1
)
select * from ranked_users where user_id=93

您可能需要考虑创建一个可以方便地用来回答这两个问题的视图:

create view ranked_users as
with user_ranked_games as (
  select user_id,
         credits,
         time_played,
         row_number() over (  partition by user_id
                              order by credits desc, time_played asc
                           ) as game_rank
    from games
),
select rank() over (order by g.credits desc, g.time_played asc) as rank,
       g.user_id,
       u.alias,
       g.credits,
       g.time_played
  from user_ranked_games g
  join users u
    on g.user_id = u.user_id
 where g.game_rank=1

然后第一个查询变成:

select top 5 * from ranked_users order by rank

第二个查询变成了

select * from ranked_users where user_id=93

【讨论】:

嘿dbenham,这个查询(稍加调整)效果很好,所以感谢您提供它。但是,我还需要确定单个用户的排名。 (基于该用户的最佳游戏得分和时间)。如果我使用相同的基本语句添加 where userid=xxx 那么我的排名为 1(显然因为它只查看该用户)。您是否认为我们也可以使用 sql 确定给定用户的排名,还是应该使用 php 循环对其进行解析? @user1519182 - 这听起来像是另一个问题 :) 但这只是一个很小的变化。如果答案符合您的需要,请不要忘记通过单击答案左上角附近的复选标记来接受答案。接受一个答案让其他人知道该问题已被回答,它奖励自己花时间接受的 2 分,并奖励答案的所有者 15 分。您只能接受一个答案。一旦您达到 15 名声望,您还可以对任何您认为有用的答案进行投票。您可以为多个答案投票 - 您不限于一个。 感谢 dbenham,这是完美的。我应该能够弄清楚那个轻微的修改,但我认为我看错了。无论如何,我真的很感谢你的帮助,现在一切都很好。【参考方案2】:

如果你想要得分最高的玩家的排名,然后是最低的时间,那么使用这个:

select 
    topalias,
    user_id, 
    rank() OVER (ORDER BY Credits DESC, timePlayed ASC) AS Rank
from 
    games
    INNER JOIN users 
        on games.user_id = users.id 
 Order By 
     Credits DESC, timePlayed ASC

【讨论】:

我可能没有提到我只希望给定用户列出一次,因此前 5 名的排行榜位置将始终由不同的人担任。这个查询给了我重复的用户。不过感谢您的快速回复。【参考方案3】:

我承认对您的 SELECT 子句有些困惑,但是...我想我只是在 GROUP BY 子句之后加上“ORDER BY credits DESC,time_played ASC”。并且可能只是 SELECT *。

如果您想要一个保证 5 个不同名称的解决方案,请更改 FROM 子句以获取包含该表中不同名称的表。

【讨论】:

我尝试使用右连接(选择不同的 user_id)...但我仍然为每个用​​户创建了多行。【参考方案4】:

复杂的因素是(如果我理解正确的话)你只想向每个玩家展示一次,即使他或她有(例如)五个最好的分数。所以你必须首先对所有游戏进行排名,然后为每个玩家获取最好的游戏,然后再获取其中前五名的详细信息。

CREATE TABLE #Games
    (
    GameID  INT NOT NULL PRIMARY KEY,
    UserID  INT NOT NULL,
    Rank    INT NOT NULL,
    )
INSERT INTO #Games (GameID, UserID, Rank)
    SELECT
        game_id, user_id, ROW_NUMBER() OVER (ORDER BY credits DESC, time_played) AS Rank
    FROM
        games

SELECT TOP 5
    U2.alias, G2.credits, G2.time_played
FROM
    (
    SELECT UserID, MIN(Rank) AS BestRank
    FROM #Games
    GROUP BY UserID
    ) AS U1
    INNER JOIN users AS U2 ON U1.UserID = U2.user_id
    INNER JOIN #Games AS G1 ON U.BestRank = G1.Rank
    INNER JOIN games AS G2 ON G1.GameID = G2.game_id
ORDER BY
    U1.BestRank

【讨论】:

我尝试使用此查询(修改一些字段名称以匹配)但我最终得到错误,即字段未在聚合函数或 group by 子句中使用。来自 dbenham 的查询确实有效。感谢您的帮助。 啊,在U1 子查询中。三个字,补充。

以上是关于如何根据 SQL Server 中的最高点和最低时间计算排名的主要内容,如果未能解决你的问题,请参考以下文章

sql 如何查询每个班级中的最高分

SQL 查询为其他列中的每个唯一条目返回最高和最低

数据查询 sql sever 2005

在 SQL 中,BETWEEN 语句是不是必须采用 BETWEEN 最低和最高形式?

如何从表格列中获取最低和最高日期?

SQL Server 2005 中的数据聚合