Betrachten Sie diese beiden Funktionen:
ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C)
ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C)
Soweit ich weiß, liefern sie genau das gleiche Ergebnis. Mit anderen Worten, die Reihenfolge, in der Sie die Spalten in der PARTITION BY
Klausel auflisten, spielt keine Rolle.
Wenn es einen Index für gibt, habe (A,B,C)
ich erwartet, dass der Optimierer diesen Index in beiden Varianten verwendet.
Überraschenderweise entschied sich das Optimierungsprogramm für eine zusätzliche explizite Sortierung in der zweiten Variante.
Ich habe es auf SQL Server 2008 Standard und SQL Server 2014 Express gesehen.
Hier ist ein vollständiges Skript, mit dem ich es reproduziert habe.
Versucht auf Microsoft SQL Server 2014 - 12.0.2000.8 (X64) 20.02.2014 20:04:26 Copyright (c) Microsoft Corporation Express Edition (64-Bit) unter Windows NT 6.1 (Build 7601: Service Pack 1)
und Microsoft SQL Server 2014 (SP1-CU7) (KB3162659) - 12.0.4459.0 (X64) 27. Mai 2016, 15:33:17 Uhr Copyright (c) Microsoft Corporation Express Edition (64-Bit) unter Windows NT 6.1 (Build 7601: Service Pack 1)
mit altem und neuem Cardinality Estimator unter Verwendung von OPTION (QUERYTRACEON 9481)
und OPTION (QUERYTRACEON 2312)
.
Richten Sie Tabelle, Index und Beispieldaten ein
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);
Abfragen
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);
Ausführungspläne
TEILUNG DURCH A, B
TEILUNG DURCH B, A
Beide
Wie Sie sehen, hat der zweite Plan eine zusätzliche Sortierung. Es ordnet nach B, A, C. Das Optimierungsprogramm ist anscheinend nicht schlau genug, um zu erkennen, dass PARTITION BY B,A
es mit PARTITION BY A,B
den Daten identisch ist, und sortiert sie neu.
Interessanterweise enthält die dritte Abfrage beide Varianten von ROW_NUMBER
und es gibt keine zusätzliche Sortierung! Der Plan ist derselbe wie für die erste Abfrage. (Das Sequenzprojekt enthält in der Ausgabeliste einen zusätzlichen Ausdruck für die zusätzliche Spalte, jedoch keine zusätzliche Sortierung.) In diesem komplizierteren Fall schien das Optimierungsprogramm also klug genug zu sein, um zu erkennen, dass PARTITION BY B,A
es dasselbe ist wie PARTITION BY A,B
.
In der ersten und dritten Abfrage hat der Index-Scan-Operator die Eigenschaft Sortiert: Wahr, in der zweiten Abfrage ist sie Falsch.
Noch interessanter, wenn ich die dritte Abfrage so umschreibe (zwei Spalten vertauschen):
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);
dann erscheint die extra Sortierung wieder!
Könnte jemand Licht ins Dunkel bringen? Was ist hier im Optimierer los?