根据官方 Microsoft BOL DENSE_RANK 是不确定的(RANK())。但根据Itzik Ben-Gan 的排名函数 “...... RANK() 和 DENSE_RANK() 函数总是确定性的”。谁是对的?
到目前为止我发现: 微软的定义“确定性函数在任何时候使用一组特定的输入值调用它们时总是返回相同的结果,并给出相同的数据库状态。”
所以在集合理论表雇员
Employee Salary
Sue Right 1.00
Robin Page 1.00
Phil Factor 1.00
Run Code Online (Sandbox Code Playgroud)
和员工2
Employee Salary
Phil Factor 1.00
Sue Right 1.00
Robin Page 1.00
Run Code Online (Sandbox Code Playgroud)
是相同的。但是排名函数返回不同的值:
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
Run Code Online (Sandbox Code Playgroud)
Pau*_*ite 23
根据官方 Microsoft BOL DENSE_RANK 是不确定的 (RANK())。但是根据 Itzik Ben-Gan 的 Ranking Functions “...... RANK() 和 DENSE_RANK() 函数总是确定性的”。谁是对的?
他们都是对的,因为他们使用了“确定性”这个词的不同含义。
从 SQL Server 优化器的角度来看,“确定性”具有非常精确的含义;在将窗口和排名功能添加到产品之前存在的含义。对于优化器而言,“确定性”属性定义了在优化期间是否可以在其内部树结构中自由复制函数。这对于非确定性函数是不合法的。
这里的确定性意味着:函数的确切实例总是为相同的输入返回相同的输出,无论它被调用多少次。根据定义,这对于加窗函数来说永远不是真的,因为作为(单行)标量函数,它们不会在行内或跨行返回相同的结果。简单说一下,ROW_NUMBER举个例子:
该
ROW_NUMBER函数为不同的行返回不同的值(根据定义!),因此出于优化目的,它是不确定的
这就是 BOL 正在使用的意义。
Itzik 对整个结果的决定性提出了不同的观点。在有序的输入集(具有适当的打破平局)上,输出是一个“确定性”序列。这是一个有效的观察,但在查询优化期间重要的不是“确定性”质量。
Aar*_*and 11
NTILE()是一个有趣的案例;它似乎在排序之后应用(在平局的情况下,这留给 SQL Server 自己的设备,这通常是由最有效的索引选择用于排序目的)。您可以通过不强制 SQL Server 在此处做出任意选择来确定这一点 - 在OVER()子句中添加一个或多个决胜局:
OVER (ORDER BY Salary, Employee)
Run Code Online (Sandbox Code Playgroud)
本质上,您需要使排序独一无二。如果您有同名的员工,您可能必须选择不同的决胜列或继续添加列,直到真正没有关系为止。
对于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
Run Code Online (Sandbox Code Playgroud)
RANK()并且DENSE_RANK()在这两种情况下应用了相同的值,SQL Server 只是以不同的顺序向您返回结果。这与期待相同的输出RANK()或DENSE_RANK()给出相同的输入无关- 这只是假设或期待某些确定性顺序,当您告诉 SQL Server(通过省略ORDER BY子句)您不关心顺序时结果。请参阅此处的 #3:
句法:
WindowFunction() OVER (PARTITION BY <some expressions> -- partition list
ORDER BY <some other expressions>) -- order list
Run Code Online (Sandbox Code Playgroud)
只要子句中的表达式本身是确定性的,这两个函数RANK()和DENSE_RANK(),根据它们的定义,都可以保证产生相同的结果OVER。这就是 Itzik Ben-Gun 在他的文章中的意思。这些列表通常只是所涉及表格的列。
因此,虽然函数是通用的不是确定性的,但它们的实现可以注意区分这两种情况,并在检查分区和顺序列表时考虑它们是否具有确定性。
我的疯狂猜测是,SQL-Server 开发人员认为将它们实现为始终“非确定性”更容易,尽管这在某种程度上与他们对确定性函数的定义相矛盾。因此,它们在 MSDN 中被声明为非确定性的,因为在当前的实现中,引擎始终认为它们是非确定性的。
另一个论点是,其他两个窗口函数ROW_NUMBER()和NTILE()甚至更复杂,因为对于它们具有相同的输出,分区和排序列表中的表达式不仅必须是确定性的,而且也是唯一的。因此,实现所有这些细节绝非易事。
我不会评论结果集的顺序,因为这与决定论无关,正如 Aaron Bertrand 在他的回答中明确解释的那样。