Ein Ansatz könnte darin bestehen, eine #temp-Tabelle für die Werte zu verwenden und auch eine Dummy-Equijoin-Spalte einzuführen, um einen Hash-Join zu ermöglichen. Beispielsweise:
-- Create a #temp table with a dummy column to match the hash join
-- and the actual column you want
CREATE TABLE #values (dummy INT NOT NULL, Col0 CHAR(1) NOT NULL)
INSERT INTO #values (dummy, Col0)
VALUES (0, 'A'),
(0, 'B'),
(0, 'C')
GO
-- A similar query, but with a dummy equijoin condition to allow for a hash join
SELECT v.Col0,
CASE v.Col0
WHEN 'A' THEN cs.DataA
WHEN 'B' THEN cs.DataB
WHEN 'C' THEN cs.DataC
END AS Col1
FROM ColumnstoreTable cs
JOIN #values v
-- Join your dummy column to any numeric column on the columnstore,
-- multiplying that column by 0 to ensure a match to all #values
ON v.dummy = cs.DataA * 0
Leistungs- und Abfrageplan
Dieser Ansatz ergibt einen Abfrageplan wie den folgenden, und die Hash-Übereinstimmung wird im Stapelmodus ausgeführt:
Wenn ich die SELECT
Anweisung durch eine SUM
der CASE
Anweisung ersetze, um zu vermeiden, dass alle diese Zeilen an die Konsole übertragen werden müssen, und dann die Abfrage für eine echte 100-mm-Zeilenspeichertabelle ausführe, die ich herumliegen habe, sehe ich eine ziemlich gute Leistung beim Generieren der erforderlichen 300-mm-Tabelle Reihen:
CPU time = 33803 ms, elapsed time = 4363 ms.
Und der aktuelle Plan zeigt eine gute Parallelisierung des Hash-Joins.
Hinweise zur Parallelisierung von Hash-Joins, wenn alle Zeilen denselben Wert haben
Die Leistung dieser Abfrage hängt stark davon ab, ob jeder Thread auf der Testseite des Joins Zugriff auf die vollständige Hash-Tabelle hat (im Gegensatz zu einer Version mit Hash-Partition, bei der alle Zeilen einem einzelnen Thread zugeordnet werden, vorausgesetzt, es gibt nur einen eindeutigen Wert) für die dummy
Spalte).
Glücklicherweise ist dies in diesem Fall der Fall (wie wir am Fehlen eines Parallelism
Operators auf der Testseite sehen können) und sollte zuverlässig der Fall sein, da im Stapelmodus eine einzelne Hash-Tabelle erstellt wird, die von allen Threads gemeinsam genutzt wird. Daher kann jeder Thread seine Zeilen aus der Columnstore Index Scan
Tabelle entnehmen und sie mit der gemeinsam genutzten Hash-Tabelle abgleichen. In SQL Server 2012 war diese Funktionalität viel weniger vorhersehbar, da der Operator aufgrund eines Überlaufs im Zeilenmodus neu gestartet wurde, wodurch sowohl der Batch-Modus als auch ein Repartition Streams
Operator auf der Testseite des Joins verloren gingen, der in diesem Fall zu einem Thread-Versatz führen würde . Das Zulassen, dass Verschüttungen im Stapelverarbeitungsmodus verbleiben, ist eine wesentliche Verbesserung in SQL Server 2014.
Meines Wissens verfügt der Zeilenmodus nicht über diese Funktion für gemeinsam genutzte Hashtabellen. In einigen Fällen jedoch, normalerweise mit einer Schätzung von weniger als 100 Zeilen auf der Build-Seite, erstellt SQL Server für jeden Thread eine separate Kopie der Hash-Tabelle (erkennbar an dem Distribute Streams
in den Hash-Join führenden Thread ). Dies kann sehr leistungsfähig sein, ist jedoch weitaus weniger zuverlässig als der Batch-Modus, da dies von Ihren Kardinalitätsschätzungen abhängt und SQL Server versucht, die Vorteile im Vergleich zu den Kosten für die Erstellung einer vollständigen Kopie der Hash-Tabelle für jeden Thread zu bewerten.
UNION ALL: eine einfachere Alternative
Paul White wies darauf hin, dass eine andere und möglicherweise einfachere Option UNION ALL
darin besteht, die Zeilen für jeden Wert zu kombinieren. Dies ist wahrscheinlich die beste Wahl, vorausgesetzt, Sie können diese SQL problemlos dynamisch aufbauen. Beispielsweise:
SELECT 'A' AS Col0, c.DataA AS Col1
FROM ColumnstoreTable c
UNION ALL
SELECT 'B' AS Col0, c.DataB AS Col1
FROM ColumnstoreTable c
UNION ALL
SELECT 'C' AS Col0, c.DataC AS Col1
FROM ColumnstoreTable c
Dies ergibt auch einen Plan, der den Batch-Modus verwenden kann und eine noch bessere Leistung als die ursprüngliche Antwort bietet. (Obwohl in beiden Fällen die Leistung schnell genug ist, dass das Auswählen oder Schreiben der Daten in eine Tabelle schnell zum Engpass wird.) Bei diesem UNION ALL
Ansatz wird auch vermieden, dass Spiele wie das Multiplizieren mit 0 gespielt werden. Manchmal ist es am besten, einfach zu denken!
CPU time = 8673 ms, elapsed time = 4270 ms.