【问题标题】:Ranking without Rank() [duplicate]没有Rank()的排名[重复]
【发布时间】:2019-10-23 06:53:15
【问题描述】:

这是表格:

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() 的情况下获得相同的结果?

【问题讨论】:

  • 但是你为什么不想使用 Rank() 呢?您是否面临任何性能问题?
  • 使用 Rank() 没有问题,我已将其作为一项任务。
  • 可能的解决方案:stackoverflow.com/questions/46856267/….
  • 可以使用其他窗口功能吗?
  • 我可以使用 Count()

标签: sql sql-server rank


【解决方案1】:

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

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;

一个测试dbfiddle可以找到here

但是RANK会更有效率。

【讨论】:

    【解决方案2】:

    换句话说,学生的排名就是分数高于hir加一的学生的数量。例如。 87分的学生排在他们前面的是89分和91分,所以他们排在第三位:

    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

    【讨论】:

      【解决方案3】:

      我正在使用标识键创建临时表以便对行进行排序。当对具有IDENTITY 列的表执行INSERT 时,SQL 引擎将遵循ORDER BY 子句。

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

      • 如果class发生变化,重新开始排名
      • 如果classmarks 相同 - 使用相同的排名(增加当前排名的计数器)
      • 如果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;
      

      注意,这是一个想法。您可以用IIF 替换CASE WHEN 语句,或者您可以用不同的方式编写CTE(不使用第二个表来存储数据)。

      祝你好运。

      【讨论】:

        【解决方案4】:

        以下内容仅适用于计数,尽管您最好使用排名:

        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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-11-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多