RANK()和DENSE_RANK()是确定性的还是不确定性的?


27

根据官方Microsoft BOL DENSE_RANK是不确定的(RANK())。但是根据Itzik Ben-Gan的排名函数, “ ... RANK()和DENSE_RANK()函数始终是确定性的”。谁是对的?

到目前为止,我发现的是: Microsoft的定义 “确定性函数在每次使用一组特定的输入值并被赋予相同的数据库状态时,总是返回相同的结果。”

所以在理论表中,员工

Employee            Salary
Sue Right            1.00
Robin Page           1.00
Phil Factor          1.00

和员工2

Employee            Salary
Phil Factor          1.00
Sue Right            1.00
Robin Page           1.00

是相同的。但是排名函数返回不同的值:

    CREATE TABLE [dbo].[Employees](
    --[ID] [int] IDENTITY(1,1) NOT NULL,
    [Employee] [varchar](150) NOT NULL,
    [Salary] [smallmoney] NULL,
) ON [PRIMARY]

GO
CREATE TABLE [dbo].[Employees2](
    --[ID] [int] IDENTITY(1,1) NOT NULL,
    [Employee] [varchar](150) NOT NULL,
    [Salary] [smallmoney] NULL,
) ON [PRIMARY]

INSERT INTO [dbo].[Employees]
([Employee] ,[Salary])
VALUES
('Sue Right', 1)
, ('Robin Page', 1)
,('Phil Factor', 1 )
GO
INSERT INTO [dbo].[Employees2]
([Employee] ,[Salary])
VALUES
('Phil Factor', 1 )
,('Sue Right', 1)
,('Robin Page', 1)
GO
SELECT RANK() OVER ( ORDER BY Salary) AS [Rank]
, DENSE_RANK() OVER (ORDER BY Salary ) AS [Dense_rank]
, [Employee]
FROM
dbo.Employees

SELECT RANK() OVER ( ORDER BY Salary) AS [Rank]
, DENSE_RANK() OVER (ORDER BY Salary ) AS [Dense_rank]
, [Employee]
FROM
dbo.Employees2

SELECT NTILE(3) OVER ( ORDER BY SALARY )
, [Employee]
FROM
dbo.Employees

SELECT NTILE(3) OVER ( ORDER BY SALARY )
, [Employee]
FROM
dbo.Employees2

Answers:


23

根据官方Microsoft BOL DENSE_RANK是不确定的(RANK())。但是根据Itzik Ben-Gan的排名函数,“ ... RANK()和DENSE_RANK()函数始终是确定性的”。谁是对的?

他们俩都是对的,因为他们对“确定性”一词使用了不同的含义。

从SQL Server优化器的角度来看,“确定性”具有非常精确的含义。在向产品添加窗口和排名功能之前存在的含义。对于优化器,“确定性”属性定义函数在优化过程中是否可以在其内部树结构中自由复制。对于不确定功能,这是不合法的。

确定性在这里意味着:函数的确切实例总是为相同的输入返回相同的输出,无论调用多少次。从定义上讲,这对于开窗函数永远都不是正确的,因为作为(单行)标量函数,它们在行内或行之间不会返回相同的结果。为了简单地说明,ROW_NUMBER以示例为例:

ROW_NUMBER函数针对不同的行返回不同的值(根据定义!),因此出于优化目的,它是不确定的

这就是BOL使用的感觉。

Itzik对整个结果的确定性提出了不同的观点。在有序输入集(具有适当的平局决胜)上,输出是“确定性”序列。这是一个有效的观察结果,但在查询优化过程中重要的不是“确定性”质量。


10

NTILE()是一个有趣的案例;它似乎在排序后适用(如果是平局,则留给SQL Server自己的设备使用,这通常是由用于排序目的的最有效索引选择驱动的)。您可以通过不强制SQL Server在此处做出任意选择来确定性-在OVER()子句中添加一个或多个“平局” :

OVER (ORDER BY Salary, Employee)

本质上,您需要使排序唯一。如果您有同名员工,则可能必须选择其他平局列或继续添加列,直到确实没有平局为止。

对于RANK()DENSE_RANK(),关系实际上是无法获得不同值的关键原因。尽量不要将函数输出的确定性与结果顺序的确定性混淆。如果您的查询没有ORDER BY,那么不确定的是什么?

1   1   Sue Right
1   1   Robin Page
1   1   Phil Factor

1   1   Phil Factor
1   1   Sue Right
1   1   Robin Page

RANK()并且DENSE_RANK()在两种情况下都应用了相同的值,SQL Server只是以不同的顺序将结果返回给您。这与期望来自相同输出RANK()DENSE_RANK()给定相同输入没有关系-这只是假设或期望某些确定性顺序(当您告诉SQL Server(通过省略ORDER BY子句)您不关心顺序)时。结果。请参阅此处的#3:


7

句法:

WindowFunction() OVER (PARTITION BY <some expressions>        -- partition list
                       ORDER BY <some other expressions>)     -- order list

只要子句中的表达式本身是确定性的,就可以保证这两个函数RANK()和和都会DENSE_RANK()产生相同的结果OVER。这就是Itzik Ben-Gun在他的文章中的意思。这些列表通常只是所涉及表的列。

因此,尽管功能不是通用的,但在检查分区和顺序列表时,它们的实现可以小心区分两种情况,并考虑是否具有确定性。

我的疯狂猜测是,尽管这在某种程度上与确定性函数的定义相矛盾,但是SQL-Server开发人员认为将它们始终实现为“非确定性”更为容易。因此,它们在MSDN中被声明为不确定的,因为在当前实现中,引擎始终将它们视为不确定的。

还有一个论点是,另外两个窗口函数ROW_NUMBER()和和NTILE()更复杂,因为对于它们具有相同的输出,分区和按列表排序的表达式不仅必须是确定性的,而且还必须是唯一的。因此,实现所有这些细节绝非易事。


我不会评论结果集的顺序,因为这与确定性无关,正如亚伦·伯特兰德(Aaron Bertrand)在回答中清楚说明的那样。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.