RANK () e DENSE_RANK () sono deterministici o non deterministici?


27

Secondo Microsoft BOL ufficiale DENSE_RANK non è deterministico ( RANK () ). Ma secondo le funzioni di classifica di Itzik Ben-Gan "... le funzioni RANK () e DENSE_RANK () sono sempre deterministiche". Chi ha ragione?

Quello che ho trovato finora: Definizione di Microsoft "Le funzioni deterministiche restituiscono sempre lo stesso risultato ogni volta che vengono chiamate con un set specifico di valori di input e dato lo stesso stato del database."

Quindi nelle tabelle della teoria degli insiemi Dipendenti

Employee            Salary
Sue Right            1.00
Robin Page           1.00
Phil Factor          1.00

e dipendenti2

Employee            Salary
Phil Factor          1.00
Sue Right            1.00
Robin Page           1.00

sono uguali. Ma le funzioni di classifica restituiscono valori diversi:

    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

Risposte:


23

Secondo Microsoft BOL ufficiale DENSE_RANK non è deterministico (RANK ()). Ma secondo le funzioni di classifica di Itzik Ben-Gan "... le funzioni RANK () e DENSE_RANK () sono sempre deterministiche". Chi ha ragione?

Hanno entrambi ragione, perché stanno usando sensi diversi della parola "deterministico".

Dal punto di vista dell'ottimizzatore di SQL Server, "deterministico" ha un significato molto preciso; un significato che esisteva prima che le funzioni di finestra e classificazione fossero aggiunte al prodotto. Per l'ottimizzatore, la proprietà "deterministica" definisce se una funzione può essere duplicata liberamente all'interno delle sue strutture ad albero interne durante l'ottimizzazione. Questo non è legale per una funzione non deterministica.

Deterministico qui significa: l'istanza esatta della funzione restituisce sempre lo stesso output per lo stesso input, indipendentemente da quante volte viene chiamata. Questo non è mai vero per le funzioni di windowing, per definizione, perché come una funzione scalare (a riga singola), non restituiscono lo stesso risultato all'interno di una riga o tra le righe. Per dirlo semplicemente, usando ROW_NUMBERcome esempio:

La ROW_NUMBERfunzione restituisce valori diversi per righe diverse (per definizione!), Quindi ai fini dell'ottimizzazione non è deterministica

Questo è il senso che BOL sta usando.

Itzik sta facendo un punto diverso sul determinismo del risultato nel suo insieme. Su un set di input ordinato (con opportuni tie-break) l'output è una sequenza "deterministica". Questa è un'osservazione valida, ma non è la qualità "deterministica" che è importante durante l'ottimizzazione delle query.


10

NTILE()è un caso interessante; sembra applicarsi dopo l'ordinamento (che, nel caso di un pareggio, viene lasciato ai dispositivi di SQL Server, e questo di solito è guidato dalla scelta più efficiente dell'indice ai fini dell'ordinamento). È possibile rendere questo deterministico non forzando SQL Server a fare una scelta arbitraria qui - aggiungere uno o più tie-breaker alla OVER()clausola:

OVER (ORDER BY Salary, Employee)

Fondamentalmente è necessario rendere unico l'ordinamento. Se hai dipendenti con lo stesso nome, potresti dover scegliere una colonna di tie-break diversa o continuare ad aggiungere colonne fino a quando non ci saranno davvero legami.

Per RANK()e DENSE_RANK(), i legami sono in realtà una ragione cruciale che non si può ottenere valori diversi. Cerca di non confondere il determinismo dell'output della funzione con il determinismo dell'ordine dei risultati. Se le tue domande non hanno ORDER BY, allora cosa non è deterministico al riguardo?

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()applicato gli stessi valori in entrambi i casi, SQL Server ti ha appena restituito i risultati in un ordine diverso. Ciò non ha nulla a che fare con l'attesa dello stesso output RANK()o DENSE_RANK()con lo stesso input: si tratta solo di assumere o aspettarsi un ordine deterministico quando si dice a SQL Server (omettendo una ORDER BYclausola) che non ti interessa l'ordine di i risultati. Vedi # 3 qui:


7

Sintassi:

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

Entrambe le funzioni RANK()e DENSE_RANK(), con le loro definizioni, sono garantite per produrre gli stessi risultati purché le espressioni nella OVERclausola siano esse stesse deterministiche. Ed è quello che intendeva Itzik Ben-Gun nel suo articolo. Questi elenchi sono spesso solo colonne delle tabelle coinvolte.

Quindi, sebbene le funzioni generali non siano deterministiche, la loro implementazione avrebbe potuto avere cura di distinguere i due casi e considerarli deterministici o meno, esaminando la partizione e gli elenchi di ordini.

La mia ipotesi selvaggia è che gli sviluppatori di SQL Server abbiano deciso che era più semplice implementarli come sempre "non deterministici", nonostante ciò contraddicesse in qualche modo la loro definizione di funzioni deterministiche. Quindi, sono dichiarati come non deterministici in MSDN perché nell'attuale implementazione, il motore li considera sempre non deterministici.

Un altro argomento è che le altre due funzioni della finestra ROW_NUMBER()e NTILE(), sono ancora più complicate perché per loro che hanno un output identico, l'espressione nella partizione e l'ordine in base agli elenchi devono non solo essere deterministici ma anche univoci. Quindi, implementare tutti questi dettagli è tutt'altro che banale.


Non commenterò l'ordine dei set di risultati, poiché questo non ha nulla a che fare con il determinismo, come ha spiegato chiaramente Aaron Bertrand nella sua risposta.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.