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 BY
clausola 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
Partizione di B, A
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,B
e riordinare i dati.
È interessante notare che la terza query contiene entrambe le varianti ROW_NUMBER
e 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?