Warum Ausführungspläne unterschiedlich sind
Erste Abfrage
select sum(PrinBal)
from fpc
inner join gl on gl.SnapshotKey = fpc.SnapshotKey
where fpc.SnapshotKey = 201703
Der Optimierer weiß:
gl.SnapshotKey = fpc.SnapshotKey
;; und
fpc.SnapshotKey = 201703
so kann es schließen:
Als ob Sie geschrieben hätten:
select sum(PrinBal)
from fpc
inner join gl on gl.SnapshotKey = fpc.SnapshotKey
where fpc.SnapshotKey = 201703
and gl.SnapshotKey = 201703
Der Literalwert 201703 kann auch vom Optimierer verwendet werden, um die Partitions-ID zu bestimmen. Bei beiden SnapshotKey
Prädikaten (eines angegeben, eines abgeleitet) bedeutet dies, dass der Optimierer die Partitions-ID für beide Tabellen kennt.
Mit einem Literalwert (201703) SnapshotKey
, der derzeit in beiden Tabellen verfügbar ist, wird das Join-Prädikat weiter ausgeführt:
gl.SnapshotKey = fpc.SnapshotKey
vereinfacht zu:
201703 = 201703
;; oder einfach
true
Das heißt, es gibt überhaupt kein Join-Prädikat. Das Ergebnis ist eine logische Querverbindung. Wenn Sie den endgültigen Ausführungsplan mit der nächstgelegenen verfügbaren T-SQL-Syntax ausdrücken, ist es so, als hätten Sie geschrieben:
SELECT
CASE
WHEN SUM(Q1.c) = 0 THEN NULL
ELSE SUM(Q1.s)
END
FROM
(
SELECT c = COUNT_BIG(*), s = SUM(GL.PrinBal)
FROM dbo.gl AS GL
WHERE GL.SnapshotKey = 201703
AND $PARTITION.PF(GL.SnapshotKey) = $PARTITION.PF(201703)
) AS Q1
CROSS JOIN
(
SELECT Dummy = 1
FROM dbo.fpc AS FPC
WHERE FPC.SnapshotKey = 201703
AND $PARTITION.PF(FPC.SnapshotKey) = $PARTITION.PF(201703)
) AS Q2;
Zweite Abfrage
select sum(PrinBal)
from fpc
inner join gl on $Partition.PF(gl.SnapshotKey) = $Partition.PF(fpc.SnapshotKey)
where fpc.SnapshotKey = 201703
Das Optimierungsprogramm kann nichts mehr ableiten gl.SnapshotKey
, sodass die für die erste Abfrage vorgenommenen Vereinfachungen und Transformationen nicht mehr möglich sind.
Wenn nicht wahr ist, dass jede Partition nur eine einzige enthält SnapshotKey
, kann beim Umschreiben nicht garantiert werden, dass dieselben Ergebnisse erzielt werden.
Drücken Sie erneut den Ausführungsplan aus, der mit der nächstgelegenen verfügbaren T-SQL-Syntax erstellt wurde:
SELECT
CASE
WHEN SUM(Q2.c) = 0 THEN NULL
ELSE SUM(Q2.s)
END
FROM
(
SELECT
Q1.PtnID,
c = COUNT_BIG(*),
s = SUM(Q1.PrinBal)
FROM
(
SELECT GL.PrinBal, PtnID = $PARTITION.PF(GL.SnapshotKey)
FROM dbo.gl AS GL
) AS Q1
GROUP BY
Q1.PtnID
) AS Q2
CROSS APPLY
(
SELECT Dummy = 1
FROM dbo.fpc AS FPC
WHERE
$PARTITION.PF(FPC.SnapshotKey) = Q2.PtnID
AND FPC.SnapshotKey = 201703
) AS Q3;
Diesmal gibt es keine logische Querverbindung. Stattdessen gibt es einen korrelierten Join (ein Apply) für die Partitions-ID.
warum die zweite Abfrage schneller ist.
Dies ist anhand der gegebenen Informationen schwer zu beurteilen. Bei Verwendung von Scheindaten und Tabellen basierend auf den bereitgestellten Abfragen und dem Planbild stellte ich fest, dass die erste Abfrage in jedem Fall die zweite übertraf.
Dieselbe Abfrage, die mit unterschiedlicher Syntax ausgedrückt wird, kann häufig zu einem anderen Ausführungsplan führen, einfach weil das Optimierungsprogramm an einem anderen Punkt gestartet und Optionen in einer anderen Reihenfolge untersucht hat, bevor es einen geeigneten Ausführungsplan gefunden hat. Die Plansuche ist nicht erschöpfend und nicht jede mögliche logische Transformation ist verfügbar. Daher ist das Endergebnis wahrscheinlich unterschiedlich. Wie oben erwähnt, drücken die beiden Abfragen ohnehin nicht unbedingt dieselbe Anforderung aus (zumindest angesichts der Informationen, die dem Optimierer zur Verfügung stehen).
Beachten Sie in einem separaten Hinweis, dass die anfängliche Implementierung des Spaltenspeichers in SQL Server 2012 (und in etwas geringerem Umfang 2014) viele Einschränkungen aufweist, nicht zuletzt auf der Optimierungsseite. Sie werden wahrscheinlich bessere und konsistentere Ergebnisse erzielen, wenn Sie auf eine neuere Version (im Idealfall die neueste) aktualisieren. Dies gilt insbesondere dann, wenn Sie die Partitionierung verwenden möchten.
Ich würde Ihnen sicherlich nicht empfehlen, sich daran zu gewöhnen, Joins mit neu zu schreiben $PARTITION
, außer als allerletzter Ausweg und mit einem sehr tiefen Verständnis dafür, was Sie tun.
Das ist ungefähr alles, was ich sagen kann, ohne das Schema oder die Planungsdetails sehen zu können.