ROW_NUMBER () OVER (PARTITION BY B, A ORDER BY C) non utilizza l'indice su (A, B, C)


12

Considera queste due funzioni:

ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C)

ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C)

Per quanto ho capito, producono esattamente lo stesso risultato. In altre parole, l'ordine in cui elenchi le colonne nella PARTITION BYclausola non ha importanza.

Se c'è un indice su, (A,B,C)mi aspettavo che l'ottimizzatore usasse questo indice in entrambe le varianti.

Ma, sorprendentemente, l'ottimizzatore ha deciso di fare un ordinamento esplicito in più nella seconda variante.

L'ho visto su SQL Server 2008 Standard e SQL Server 2014 Express.

Ecco uno script completo che ho usato per riprodurlo.

Provato su Microsoft SQL Server 2014 - 12.0.2000.8 (X64) 20 febbraio 2014 20:04:26 Copyright (c) Microsoft Corporation Express Edition (64-bit) su Windows NT 6.1 (Build 7601: Service Pack 1)

e Microsoft SQL Server 2014 (SP1-CU7) (KB3162659) - 12.0.4459.0 (X64) 27 maggio 2016 15:33:17 Copyright (c) Microsoft Corporation Express Edition (64-bit) su Windows NT 6.1 (Build 7601: Service Pacco 1)

con il vecchio e nuovo stimatore della cardinalità usando OPTION (QUERYTRACEON 9481)e OPTION (QUERYTRACEON 2312).

Imposta tabella, indice, dati di esempio

CREATE TABLE [dbo].[T](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [A] [int] NOT NULL,
    [B] [int] NOT NULL,
    [C] [int] NOT NULL,
    CONSTRAINT [PK_T] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, 
STATISTICS_NORECOMPUTE = OFF, 
IGNORE_DUP_KEY = OFF, 
ALLOW_ROW_LOCKS = ON, 
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

CREATE NONCLUSTERED INDEX [IX_ABC] ON [dbo].[T]
(
    [A] ASC,
    [B] ASC,
    [C] ASC
)WITH (PAD_INDEX = OFF, 
STATISTICS_NORECOMPUTE = OFF, 
SORT_IN_TEMPDB = OFF, 
DROP_EXISTING = OFF, 
ONLINE = OFF, 
ALLOW_ROW_LOCKS = ON, 
ALLOW_PAGE_LOCKS = ON)
GO

INSERT INTO [dbo].[T] ([A],[B],[C]) VALUES
(10, 20, 30),
(10, 21, 31),
(10, 21, 32),
(10, 21, 33),
(11, 20, 34),
(11, 21, 35),
(11, 21, 36),
(12, 20, 37),
(12, 21, 38),
(13, 21, 39);

Interrogazioni

SELECT -- AB
    ID,A,B,C
    ,ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C) AS rnAB
FROM T
ORDER BY C
OPTION(RECOMPILE);

SELECT -- BA
    ID,A,B,C
    ,ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) AS rnBA
FROM T
ORDER BY C
OPTION(RECOMPILE);

SELECT -- both
    ID,A,B,C
    ,ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C) AS rnAB
    ,ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) AS rnBA
FROM T
ORDER BY C
OPTION(RECOMPILE);

Piani di esecuzione

Partizione di A, B

AB

Partizione di B, A

BA

Tutti e due

tutti e due

Come puoi vedere, il secondo piano ha un ulteriore ordinamento. Ordina per B, A, C. L'ottimizzatore, a quanto pare, non è abbastanza intelligente da rendersi conto che PARTITION BY B,Aè lo stesso PARTITION BY A,Be riordinare i dati.

È interessante notare che la terza query contiene entrambe le varianti ROW_NUMBERe non è disponibile alcun ordinamento aggiuntivo! Il piano è lo stesso della prima query. (Il progetto di sequenza ha un'espressione extra nell'elenco di output per la colonna aggiuntiva, ma nessun ordinamento aggiuntivo). Quindi, in questo caso più complicato, l'ottimizzatore sembrava essere abbastanza intelligente da rendersi conto che PARTITION BY B,Aè lo stesso PARTITION BY A,B.

Nella prima e terza query l'operatore Index Scan ha la proprietà Ordinata: True, nella seconda query è False.

Ancora più interessante, se riscrivo la terza query in questo modo (cambio due colonne):

SELECT -- both
    ID,A,B,C
    ,ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) AS rnBA
    ,ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C) AS rnAB
FROM T
ORDER BY C
OPTION(RECOMPILE);

quindi viene visualizzato di nuovo l'ordinamento aggiuntivo!

Qualcuno potrebbe far luce? Cosa sta succedendo nell'ottimizzatore qui?


Commenti archiviati .
Paul White 9

Risposte:


2

Sembra che non ci sia una buona "risposta" definitiva alla domanda "cosa sta succedendo nell'ottimizzatore", a meno che tu non sia il suo sviluppatore e non conosca i suoi interni.

Metterò insieme i commenti qui.

Nel complesso, sembra che sarebbe troppo duro definirlo un bug, perché il risultato finale della query è corretto. In alcuni casi il piano di esecuzione non è semplicemente ottimale. ypercubeᵀᴹ , Martin Smith e Aaron Bertrand lo chiamano "mancata ottimizzazione".

  • Sembra GROUP BY a,be GROUP BY b,aproduce piani identici ma PARTITION BYnon possono utilizzare la stessa trasformazione

  • Vi sono anche altre ottimizzazioni mancanti in cui le funzioni della finestra con la stessa specifica della finestra possono avere un'operazione di ordinamento aggiuntiva se separate nell'elenco di selezione da una con una specifica diversa.

  • Sì, questa sembra un'altra mancata ottimizzazione, e ce ne sono molti. L'ottimizzatore è scritto dagli umani e non è perfetto


C'è un articolo in qualche modo correlato Indici discendenti. Ordinamento dell'indice, parallelismo e calcoli di classifica di Itzik Ben-Gan. Qui Itzik discute gli indici discendenti e fornisce anche un esempio di come la direzione della definizione dell'indice influenzi le funzioni della finestra con le partizioni. Mostra esempi di query e piani generati con ROW_NUMBERun operatore di ordinamento extra che l'ottimizzatore avrebbe potuto evitare.


Per me il risultato pratico sarebbe quello di tenere presente questa peculiarità dell'ottimizzatore. Quando si utilizzano le PARTITION BYfunzioni nella finestra, cercare sempre di far corrispondere l'ordine in cui si elencano le colonne PARTITION BYcon l'ordine in cui sono elencate nell'indice. Anche se non dovrebbe importare.

Un altro aspetto di questa precauzione è quando rivedi gli indici e decidi di scambiare alcune colonne nella definizione dell'indice. Tieni presente che potresti inavvertitamente influire su alcune query esistenti che apparentemente non dovrebbero essere interessate. In realtà è così che ho notato questa peculiarità dell'ottimizzatore.

In caso contrario, l'ottimizzatore potrebbe non essere in grado di utilizzare l'indice al massimo delle sue potenzialità. Anche se l'ottimizzatore sceglie un piano ottimale, tale piano può cambiare in meno ottimale con una minima modifica innocente alla query, come cambiare l'ordine delle colonne SELECTnell'istruzione.

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.