RANK () e DENSE_RANK () são determinísticos ou não determinísticos?

27

De acordo com o oficial Microsoft BOL DENSE_RANK não é determinístico ( RANK () ). Mas de acordo com as funções de classificação de Itzik Ben-Gan "... as funções RANK () e DENSE_RANK () são sempre determinísticas". Quem está certo?

O que encontrei até agora: Definição da Microsoft "As funções determinísticas sempre retornam o mesmo resultado sempre que são chamadas com um conjunto específico de valores de entrada e com o mesmo estado do banco de dados".

Assim, nas tabelas da teoria dos conjuntos

Employee            Salary
Sue Right            1.00
Robin Page           1.00
Phil Factor          1.00

e Funcionários2

Employee            Salary
Phil Factor          1.00
Sue Right            1.00
Robin Page           1.00

são os mesmos. Mas as funções de classificação retornam valores diferentes:

    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
Pavel Nefyodov
fonte

Respostas:

23

De acordo com o oficial Microsoft BOL DENSE_RANK não é determinístico (RANK ()). Mas de acordo com as funções de classificação de Itzik Ben-Gan "... as funções RANK () e DENSE_RANK () são sempre determinísticas". Quem está certo?

Ambos estão certos, porque estão usando diferentes sentidos da palavra "determinista".

Do ponto de vista do otimizador do SQL Server, "determinístico" tem um significado muito preciso; um significado que existia antes das funções de janela e classificação serem adicionadas ao produto. Para o otimizador, a propriedade "determinística" define se uma função pode ser duplicada livremente dentro de suas estruturas internas da árvore durante a otimização. Isso não é legal para uma função não determinística.

Determinista aqui significa: a instância exata da função sempre retorna a mesma saída para a mesma entrada, não importa quantas vezes ela seja chamada. Isso nunca é verdade para funções de janelas, por definição, porque como uma função escalar (linha única), elas não retornam o mesmo resultado em uma linha ou entre linhas. Para simplificar, use ROW_NUMBERcomo exemplo:

A ROW_NUMBERfunção retorna valores diferentes para linhas diferentes (por definição!), Portanto, para fins de otimização , é não-determinístico

Este é o sentido que o BOL está usando.

Itzik está fazendo uma observação diferente sobre o determinismo do resultado como um todo. Sobre um conjunto de entrada ordenado (com desempate adequado), a saída é uma sequência "determinística". Essa é uma observação válida, mas não é a qualidade "determinística" que é importante durante a otimização da consulta.

Paul White diz que a GoFundMonica
fonte
10

NTILE()é um caso interessante; parece se aplicar após a classificação (que, no caso de um empate, é deixada para os próprios dispositivos do SQL Server, e isso geralmente é motivado pela escolha mais eficiente do índice para fins de classificação). Você pode fazer isso determinístico não forçando o SQL Server a fazer uma escolha arbitrária aqui - adicione um ou mais desempatadores à OVER()cláusula:

OVER (ORDER BY Salary, Employee)

Essencialmente, você precisa tornar a classificação única. Se você possui funcionários com o mesmo nome, pode ser necessário escolher uma coluna diferente do desempatador ou continuar adicionando colunas até que realmente não haja vínculos.

Para RANK()e DENSE_RANK(), os laços são, na verdade, uma razão crucial pela qual você não pode obter valores diferentes. Tente não confundir determinismo da saída da função com determinismo da ordem dos resultados. Se suas consultas não tiverem ORDER BY, o que não é determinístico sobre isso?

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()e DENSE_RANK()aplicou os mesmos valores nos dois casos, o SQL Server acabou de retornar os resultados para você em uma ordem diferente. Isso não tem nada a ver com esperar a mesma saída RANK()ou DENSE_RANK()com a mesma entrada - trata-se apenas de assumir ou esperar alguma ordem determinística quando você diz ao SQL Server (omitindo uma ORDER BYcláusula) que não se importa com a ordem de os resultados. Veja # 3 aqui:

Aaron Bertrand
fonte
7

Sintaxe:

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

Ambas as funções RANK()e DENSE_RANK(), por suas definições, garantem a produção dos mesmos resultados, desde que as expressões na OVERcláusula sejam determinísticas. E foi isso que Itzik Ben-Gun quis dizer em seu artigo. Essas listas geralmente são apenas colunas das tabelas envolvidas.

Portanto, embora as funções sejam gerais não sejam determinísticas, sua implementação pode ter tido o cuidado de distinguir os dois casos e considerá-los determinísticos ou não, ao examinar as listas de partições e pedidos.

Meu palpite é que os desenvolvedores do SQL Server decidiram que era mais fácil implementá-los como sempre "não determinísticos", apesar de isso contradizer de certa forma a definição de funções determinísticas. Portanto, eles são declarados como não determinísticos no MSDN porque, na implementação atual, o mecanismo os considera sempre como não determinísticos.

Mais um argumento é que as outras duas funções da janela ROW_NUMBER()e NTILE()são ainda mais complicadas porque, para elas terem saída idêntica, a expressão na partição e na ordem pelas listas não só precisa ser determinística, mas também única. Portanto, implementar todos esses detalhes está longe de ser trivial.


Não comentarei a ordem dos conjuntos de resultados, pois isso não tem nada a ver com o determinismo, como Aaron Bertrand explicou claramente em sua resposta.

ypercubeᵀᴹ
fonte