没有排名的排名

时间:2019-10-23 06:53:15

标签: sql sql-server rank

这是桌子:

StuId   Name    Class   Marks
-----------------------------
2003    aman    X-A     91
2005    ankita  X-A     89
2010    Aakash  X-A     87
2011    Cyril   X-A     87
2012    Bala    X-B     87
2013    Sara    X-C     89
2014    Katlyn  X-C     89
2015    Casy    X-C     87
2016    Katie   X-B     93

我需要输出表为:

StuId   Name    Class   Marks Rank
-----------------------------------
2003    aman    X-A     91    1
2005    ankita  X-A     89    2
2010    Aakash  X-A     87    3
2011    Cyril   X-A     87    3
2016    Katie   X-B     93    1
2012    Bala    X-B     87    2
2013    Sara    X-C     89    1
2014    Katlyn  X-C     89    1
2015    Casy    X-C     87    3

我为其执行了以下查询:

SELECT *,
    RANK() OVER (PARTITION BY Class ORDER BY Marks DESC) AS Rank
FROM StudentTable;

但是在不使用Rank()的情况下如何获得相同的结果?

4 个答案:

答案 0 :(得分:3)

您可以尝试使用相关的子查询,该子查询使用相同的“类”中的较高或相等的标记来计数。

SELECT *, 
(
 SELECT COUNT(DISTINCT s2.Marks) 
 FROM StudentTable s2 
 WHERE s2.Class = s.Class 
   AND s2.Marks >= s.Marks
) AS Rank
FROM StudentTable s
ORDER BY Class, Marks DESC;

可以找到测试 db <>小提琴here

但是RANK会更有效。

答案 1 :(得分:1)

换句话说,学生的排名将是分数高于hir 加1 的学生人数。例如。得分为87分的学生排在前面89位和91位,因此他们排名第3:

SELECT t.*, (
    SELECT COUNT(*)
    FROM StudentTable AS x
    WHERE x.Class = t.Class
    AND x.Marks > t.Marks
) + 1 AS Rank
FROM StudentTable AS t
ORDER BY t.Class, Rank

SQL Fiddle

答案 2 :(得分:0)

我正在使用身份密钥创建临时表以便对行进行排序。当对具有INSERT列的表执行IDENTITY时,SQL引擎会遵守ORDER BY子句。

然后,我使用递归CTE为每一行创建一个RANK列。这个想法很简单:

  • 如果更改了class,请重新排名
  • 如果class相同且marks相同-使用相同的等级(增加当前等级的计数器)
  • 如果class相同而marks不相同,则用1增加等级并重置等级计数器

我们正在使用这样的计数器来实现RANK的行为,对于DENSE_RANK,我们不需要这样的计数器。

所以,代码是这样的:

DECLARE @DataSource TABLE
(
    [StudID] INT
   ,[Name] VARCHAR(12)
   ,[Class] VARCHAR(12)
   ,[Marks] TINYINT
);

INSERT INTO @DataSource ([StudID], [Name], [Class], [Marks])
VALUES ('2003', 'aman', 'X-A', '91')
      ,('2005', 'ankita', 'X-A', '89')
      ,('2010', 'Aakash', 'X-A', '87')
      ,('2011', 'Cyril', 'X-A', '87')
      ,('2012', 'Bala', 'X-B', '87')
      ,('2013', 'Sara', 'X-C', '89')
      ,('2014', 'Katlyn', 'X-C', '89')
      ,('2015', 'Casy', 'X-C', '87')
      ,('2016', 'Katie', 'X-B', '93');

CREATE TABLE #DataSource
(
    [StudID] INT
   ,[Name] VARCHAR(12)
   ,[Class] VARCHAR(12)
   ,[Marks] TINYINT
   ,[RowID] INT IDENTITY(1,1)
)

INSERT INTO #DataSource ([StudID], [Name], [Class], [Marks])
SELECT [StudID], [Name], [Class], [Marks]
FROM @DataSource
ORDER BY [Class] ASC, [Marks] DESC;

WITH DataSource AS
(
    SELECT *
          ,1 AS [Rank]
          ,0 AS [RanksCount]
    FROM #DataSource
    WHERE [RowID] = 1
    UNION ALL
    SELECT DS1.*
          ,CASE WHEN DS1.[Class] = DS2.[Class] 
                THEN CASE WHEN DS1.[Marks] = DS2.[Marks] THEN DS2.[Rank] ELSE DS2.[Rank] + DS2.[RanksCount] + 1 END
                ELSE 1
           END
          ,CASE WHEN DS1.[Class] = DS2.[Class] 
                THEN CASE WHEN DS1.[Marks] = DS2.[Marks] THEN DS2.[RanksCount] + 1 ELSE 0 END
                ELSE 0
           END
    FROM #DataSource DS1
    INNER JOIN DataSource DS2
        ON DS1.[RowID] = DS2.[RowID] + 1

)
SELECT *
FROM DataSource
ORDER BY [RowID];


DROP TABLE #DataSource;

enter image description here

注意,这是一个主意。您可以将CASE WHEN语句替换为IIF,也可以用其他方式编写CTE(无需使用第二个表来存储数据)。

祝你好运。

答案 3 :(得分:0)

以下内容仅适用于count,尽管您最好使用rank:

COUNT(*) OVER (PARTITION BY Class ORDER BY Marks ASC RANGE BETWEEN UNBOUNDED PRECEDING  AND CURRENT ROW)
    - COUNT(*) OVER (PARTITION BY Class,Marks) + 1

或者,您可以使用相关子查询,该查询仅使用纯计数,但速度较慢:

(SELECT COUNT(*) FROM StudentTable AS CountMe WHERE StudentTable.Class = CountMe.Class AND StudentTable.Marks > CountMe.Marks) + 1 AS Rank