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?
quelle
Antworten:
Es scheint, dass es keine gute endgültige Antwort auf die Frage gibt, was im Optimierer vor sich geht, es sei denn, Sie sind dessen Entwickler und kennen dessen Interna.
Ich werde die Kommentare hier zusammenstellen.
Insgesamt scheint es zu hart zu sein, ihn als Fehler zu bezeichnen, da das Endergebnis der Abfrage korrekt ist. In einigen Fällen ist der Ausführungsplan einfach nicht optimal. ypercube Aaron , Martin Smith und Aaron Bertrand nennen es "verpasste Optimierung".
Es gibt einen etwas verwandten Artikel Absteigende Indizes. Indexreihenfolge, Parallelität und Rangfolgenberechnungen von Itzik Ben-Gan. Dort bespricht Itzik absteigende Indizes und gibt auch ein Beispiel, wie sich die Richtung der Indexdefinition auf Fensterfunktionen mit Partitionen auswirkt. Er zeigt Beispiele für Abfragen und generierte Pläne mit
ROW_NUMBER
einem zusätzlichen Sortieroperator, den das Optimierungsprogramm hätte vermeiden können.Für mich wäre das praktische Ergebnis, diese Besonderheit des Optimierers im Auge zu behalten.
PARTITION BY
Versuchen Sie bei der Verwendung von In-Window-Funktionen immer, die Reihenfolge, in der Sie die SpaltenPARTITION BY
auflisten, mit der Reihenfolge abzugleichen, in der sie im Index aufgeführt sind. Auch wenn es keine Rolle spielen sollte.Eine andere Seite dieser Vorsichtsmaßnahme ist, wenn Sie Ihre Indizes überprüfen und einige Spalten in der Indexdefinition vertauschen. Beachten Sie, dass Sie möglicherweise versehentlich einige vorhandene Abfragen betreffen, die anscheinend nicht betroffen sein sollten. So ist mir diese Besonderheit des Optimierers aufgefallen.
Andernfalls kann das Optimierungsprogramm den Index möglicherweise nicht vollständig nutzen. Selbst wenn das Optimierungsprogramm einen optimalen Plan auswählt, kann sich dieser Plan mit einer geringfügigen unschuldigen Änderung der Abfrage in einen weniger optimalen Plan ändern, z. B. durch Ändern der Spaltenreihenfolge in der
SELECT
Anweisung.quelle