SQL Server 的 DENSE_RANK 函数是一个排名函数,它为分区中的每个记录分配连续的排名编号,而不跳过排名编号。如果函数在同一分区中遇到两个相等的值,它将为这两个值分配相同的排名编号。
SQL DENSE_RANK 函数在我们想要显示连续的排名而不跳过任何数字(无间隙)时非常有用。例如,按部门对员工进行薪资排名,按个人分数对学生进行排名,或按销售额对产品进行排名。
在本文中,我们将通过一些实际示例、常见错误及修复方法以及最佳实践来向您展示如何编写 SQL Server DENSE_RANK 函数。在实际示例之前,让我先介绍一下它的工作原理。
想象一个包含姓名、年龄、电子邮件和分数列的学生表。任务是根据分数对学生进行排名。如您所知,如果有多个学生获得相同的最高分数,他们都获得相同的排名,而下一个学生获得第二名(不跳过)。
SELECT Name, Age, Email, Marks,
DENSE_RANK() OVER(ORDER BY Marks DESC) AS Ranks
FROM students
SQL DENSE_RANK 函数语法
DENSE_RANK() 函数的语法,它返回 BIGINT 作为结果集:
SELECT DENSE_RANK() OVER (PARTITION_BY_Clause ORDER_BY_Clause) FROM [Source]
PARTITION BY 子句:这是一个可选参数,它将结果集中的记录或行划分为分区。
- 如果指定了 PARTITION BY 子句,SQL DENSE_RANK 函数将为每个分区分配排名编号。接下来,它会为下一组重置编号。
- 如果未指定,它将考虑所有记录为一个分区。
ORDER BY:使用此子句指定如何对列数据进行排序(升序或降序)。DENSE_RANK() 排名因子基于顺序工作。
对于此 DENSE_RANK 查询演示,我们使用具有十条记录的 Customers 表数据。
SQL DENSE_RANK 函数示例
在此示例中,我们将展示如何使用 DENSE_RANK 和 PARTITION BY 子句来分区表中的记录并对其进行排名。以下排名函数查询将按职业对客户数据进行分区,并使用年收入分配密集排名编号。
SELECT [ID], [FirstName], [LastName], [Education],
[Occupation], [Sales], [YearlyIncome]
,DENSE_RANK() OVER (
PARTITION BY [Occupation]
ORDER BY [YearlyIncome] DESC
) AS DRWP
FROM [Customer]
SQL Server 语句使用其职业将选定的数据划分为分区。从上一张图可以看出,我们有四个分区。
PARTITION BY [Occupation]
在下面的语句中,我们使用了带 PARTITION BY 子句的 SQL DENSE_RANK 函数。ORDER BY 子句将按收入降序对客户进行排序。因此,该函数根据客户的年收入为每个分区分配一个密集排名编号。当新分区开始时,DENSE_RANK 编号将重置为 1。
DENSE_RANK() OVER (
PARTITION BY [Occupation]
ORDER BY [YearlyIncome] DESC
) AS DRWP
由于第 3 条和第 4 条记录的年收入相同,因此它们获得了相同的排名。接下来,它为下一条记录分配了第 2 名(它不会跳过任何数字)。
提示:如果将 ORDER BY [YearlyIncome] DESC 改为 ASC,DENSE_RANK() 函数将分配排名编号 1 作为最低收入,n = 最高收入。
在 DENSE_RANK() OVER 子句中,您还可以在 ORDER BY 中使用多个列,以根据这两列分配不同的排名。这在您有一列中的相同值时非常有用,您可以使用另一列进一步划分它们以赋予不同的排名。
DENSE_RANK() OVER (
PARTITION BY [Occupation]
ORDER BY [YearlyIncome] DESC,[Sales] DESC
) AS DRWP2
注意:我们可以使用 WHERE 子句与上述语句结合使用,以根据 DENSE_RANK 值过滤记录。例如,如果我们将 WHERE DRWithPart < 2 作为最后一行。它会显示排名为 1 的客户。
SQL DENSE_RANK 无 PARTITION BY 子句
在此示例中,我们将展示如果在 DENSE_RANK 函数中遗漏 PARTITION BY 子句会发生什么。为了演示相同,我们将上面提到的SELECT 语句在没有 PARTITION BY 子句的情况下使用。这意味着 DENSE_RANK 函数将整个表视为一个分区或组。
SELECT *,DENSE_RANK()
OVER (ORDER BY [YearlyIncome] DESC
) AS DRWithoutP
FROM [Customer]
在此示例中,下面的语句按降序对年收入数据进行排序。请参考最后一张图查看输出。
ORDER BY [YearlyIncome] DESC
在下面的语句中,我们使用了不带 PARTITION BY 子句的 SQL Server DENSE_RANK 函数。因此,它会将整个表视为一个部分,并从头到尾分配排名编号。
DENSE_RANK() OVER (
ORDER BY [YearlyIncome] DESC
) AS DRWithoutP
由于第 2、3 和 4 条记录的年收入相同(80000),因此它们获得了相同的排名(2)。接下来,由于第 5 条和第 6 条记录的年收入相同,因此它们也获得了相同的编号,依此类推。
字符串列上的 DENSE_RANK
SQL Server DENSE_RANK 函数还允许您对字符串列进行排名。在此示例中,我们将使用此方法为 [First name] 分配排名编号。
SELECT *,
,DENSE_RANK() OVER (
PARTITION BY [Occupation]
ORDER BY [FirstName] DESC
) AS DRStr
FROM [Customer]
如果您观察下面的屏幕截图,此 DENSE_RANK 函数已为第 6 条和第 7 条记录分配了相同的排名,因为它们的名字是相同的。接下来,它为下一条记录分配了第 2 名。

DENSE_RANK 常见错误与最佳实践
在使用 SQL DENSE_RANK() 函数时,您必须注意一些事项以避免错误。以下最佳实践列表可帮助您完全避免这些错误或提供解决方案。
- 当您使用 DENSE_RANK() 函数时,必须放置 ORDER BY 子句。如果省略它,排名编号将不可预测,并且可能会产生错误的结果。
- ORDER BY 子句决定了排名因子。例如,产品销售表,ASC 表示排名从最低销量开始。如果是 DESC,则第一个排名属于最高销量。
- 如果您的目标是按组排名,则必须包含 PARTITION BY 子句。
- 在使用 PARTITION BY 时,请使用适当的列名进行分组。例如,Category、Country、Region 等。
- 在处理大型数据集时,可以使用 WHERE 子句过滤数据,或使用 CTE 获取表的所需部分。
- 尽可能在 ORDER BY 和 PARTITION 子句中使用索引列。
- 您可以将 SQL Server DENSE_RANK() 函数与其他排名函数结合使用,例如 RANK()、ROW_NUMBER 和 NTILE。
- 我们可以将 DENSE_RANK() 函数与聚合函数(如 SUM()、AVG()、MIN() 等)结合使用,以在分区内查找总计、平均值、最小值等。
- DENSE_RANK() 函数将所有 NULL 值视为相同,并将相同的排名编号分配给所有 NULL 值。
评论已关闭。