Ich frage mich, warum SQL Server in einem so einfachen Fall falsche Schätzungen vornimmt. Es gibt ein Szenario.
CREATE PARTITION FUNCTION PF_Test (int) AS RANGE RIGHT
FOR VALUES (20140801, 20140802, 20140803)
CREATE PARTITION SCHEME PS_Test AS PARTITION PF_Test ALL TO ([Primary])
CREATE TABLE A
(
DateKey int not null,
Type int not null,
constraint PK_A primary key (DateKey, Type) on PS_Test(DateKey)
)
INSERT INTO A (DateKey, Type)
SELECT
DateKey = N1.n + 20140801,
Type = N2.n + 1
FROM dbo.Numbers N1
cross join dbo.Numbers N2
WHERE N1.n BETWEEN 0 AND 2
and N2.n BETWEEN 0 AND 10000 - 1
UPDATE STATISTICS A (PK_A) WITH FULLSCAN, INCREMENTAL = ON
CREATE TABLE B
(
DateKey int not null,
SubType int not null,
Type int not null,
constraint PK_B primary key (DateKey, SubType) on PS_Test(DateKey)
)
INSERT INTO B (DateKey, SubType, Type)
SELECT
DateKey,
SubType = Type * 10000 + N.n,
Type
FROM A
cross join dbo.Numbers N
WHERE N.n BETWEEN 1 AND 10
UPDATE STATISTICS B (PK_B) WITH FULLSCAN, INCREMENTAL = ON
Die Einrichtung ist also ziemlich einfach, Statistiken sind vorhanden und SQL Server kann korrekte Schätzungen erstellen, wenn wir eine Tabelle abfragen.
select COUNT(*) from A where DateKey = 20140802
--10000
select COUNT(*) from B where DateKey = 20140802
--100000
Aber in dieser einfachen Auswahl sind Schätzungen weit entfernt, und ich sehe keine Erklärung dafür.
SELECT a.DateKey, a.Type
FROM A
JOIN B
ON b.DateKey = a.DateKey
AND b.Type = a.Type
WHERE a.DateKey = 20140802
Unmittelbar nach der Clustered Index-Suche liegt die Schätzung bei 57% vom tatsächlichen Wert! Die reale Abfrage ist noch schlimmer, die Schätzung liegt bei 2% vom tatsächlichen Wert.
PS-Nummerntabelle zur Reproduktion des Setups
DECLARE @UpperBound INT = 1000000;
;WITH cteN(Number) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY s1.[object_id]) - 1
FROM sys.all_columns AS s1
CROSS JOIN sys.all_columns AS s2
)
SELECT n = [Number] INTO dbo.Numbers
FROM cteN WHERE [Number] <= @UpperBound;
CREATE UNIQUE CLUSTERED INDEX CIX_Number ON dbo.Numbers(n)
WITH
(
FILLFACTOR = 100, -- in the event server default has been changed
DATA_COMPRESSION = ROW -- if Enterprise & table large enough to matter
);
PPS Das gleiche Szenario, jedoch nicht partitioniert, funktioniert einwandfrei.
Antworten:
Die Schätzungen (mit dem neuen Kardinalitätsschätzer) sind für einen normalen Join in Ordnung, jedoch weniger genau, wenn der Optimierer die Option eines kolokalisierten Joins in Betracht zieht .
Ein Colocated Join (auch als Partitionsverknüpfung bezeichnet) ist verfügbar, wenn zwei Tabellen verknüpft werden, die auf dieselbe Weise partitioniert sind. Die Idee ist, jeweils eine Partition zu verbinden, wobei verschachtelte Schleifen verwendet werden, die von Partitions-IDs gesteuert werden, die durch einen konstanten Scan (speicherinterne Wertetabelle) bereitgestellt werden.
Regelmäßiger Beitritt
Da für den kolokalisierten Join verschachtelte Schleifen gelten, können Sie den Optimierer zwingen, dies zu vermeiden, indem Sie
OPTION (HASH JOIN)
beispielsweise Folgendes angeben :Die beiden Ziele in diesem Plan sind:
Das Optimierungsprogramm hat in beiden Fällen die statische Partitionseliminierung angewendet und genaue Schätzungen für beide Suchvorgänge und den folgenden Join angegeben.
Colocated Join
Wenn der Optimierer einen kolokalisierten Join berücksichtigt (wie in der Frage gezeigt), lauten die Suchvorgänge:
... wo
[Expr1006]
ist der vom Operator Constant Scan zurückgegebene Wert.Der Kardinalitätsschätzer kann jetzt nicht erkennen, dass der
DateKey
Wert und die Partitions-ID voneinander abhängig sind, wie dies bei Verwendung von Literalkonstanten der Fall sein könnte. Mit anderen Worten, es ist für den Schätzer nicht ersichtlich, dass der Wert darin[Expr1006]
dieselbe Partition wie angibtDateKey = 20140802
.Infolgedessen wählt das CE (standardmäßig) die Schätzung der Selektivität der beiden (scheinbar unabhängigen) Prädikate unter Verwendung der normalen exponentiellen Backoff-Methode .
Dies erklärt die reduzierten Kardinalitätsschätzungen, die den Join speisen. Die geringeren offensichtlichen Kosten dieser Option (aufgrund der falschen Schätzung) bedeuten, dass der Optimierer einen kolokalisierten Join anstelle eines regulären Joins wählt, obwohl es (für Menschen) offensichtlich ist, dass er keinen Wert bietet.
Es gibt verschiedene Möglichkeiten, um diese Lücke in der Logik zu umgehen, einschließlich der Verwendung des Abfragehinweises
USE HINT ('ASSUME_MIN_SELECTIVITY_FOR_FILTER_ESTIMATES')
. Dies wirkt sich jedoch auf die gesamte Abfrage aus, nicht nur auf die problematische Colocated Join-Alternative. Wie Erik in seiner Antwort bemerkt, könnten Sie auch auf die Verwendung des Legacy-CE hinweisen.Weitere Informationen zu Colocated Joins finden Sie in meinem Artikel Verbessern der Leistung partitionierter Tabellenverknüpfungen
quelle
Dies scheint auf den neuen Kardinalitätsschätzer zurückzuführen zu sein, der in SQL Server 2014 eingeführt wurde.
Wenn Sie die Abfrage anweisen, die alte zu verwenden, erhalten Sie einen anderen Plan und korrekte Schätzungen.
Weitere Informationen finden Sie unter diesen Links:
quelle