Un approccio potrebbe essere quello di utilizzare una tabella #temp per i valori e introdurre anche una colonna fittizia equijoin per consentire un hash join. Per esempio:
-- 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
Piano di prestazioni e query
Questo approccio produce un piano di query come il seguente e la corrispondenza hash viene eseguita in modalità batch:
Se si sostituisce la SELECT
dichiarazione con una SUM
della CASE
dichiarazione al fine di evitare di dover trasmettere tutte quelle file alla console e quindi eseguire la query su un vero e proprio 100MM tavolo fila columnstore ho in giro, vedo abbastanza buone prestazioni per generare il 300MM requisito righe:
CPU time = 33803 ms, elapsed time = 4363 ms.
E il piano reale mostra una buona parallelizzazione del join hash.
Note sulla parallelizzazione dei join hash quando tutte le righe hanno lo stesso valore
Le prestazioni di questa query dipendono fortemente da ogni thread sul lato sonda del join che ha accesso alla tabella hash completa (al contrario di una versione partizionata hash, che associ tutte le righe a un singolo thread dato che esiste un solo valore distinto per la dummy
colonna).
Fortunatamente, questo è vero in questo caso (come possiamo vedere dalla mancanza di un Parallelism
operatore sul lato della sonda) e dovrebbe essere vero in modo affidabile perché la modalità batch crea una singola tabella hash condivisa tra i thread. Pertanto, ogni thread può prendere le loro righe da Columnstore Index Scan
e abbinarle a quella tabella hash condivisa. In SQL Server 2012, questa funzionalità era molto meno prevedibile perché una fuoriuscita causava il riavvio dell'operatore in modalità Row, perdendo entrambi i vantaggi della modalità batch e richiedendo anche un Repartition Streams
operatore sul lato sonda del join che in questo caso causerebbe l'inclinazione del thread . Consentire agli sversamenti di rimanere in modalità batch è un notevole miglioramento in SQL Server 2014.
Per quanto ne so, la modalità riga non ha questa capacità di tabella hash condivisa. Tuttavia, in alcuni casi, in genere con una stima di meno di 100 righe sul lato build, SQL Server creerà una copia separata della tabella hash per ciascun thread (identificabile dal Distribute Streams
lead nel join hash). Questo può essere molto potente, ma è molto meno affidabile della modalità batch poiché dipende dalle stime della cardinalità e SQL Server sta cercando di valutare i vantaggi rispetto al costo di creazione di una copia completa della tabella hash per ogni thread.
UNION ALL: un'alternativa più semplice
Paul White ha sottolineato che un'altra opzione, e potenzialmente più semplice, sarebbe quella di utilizzare UNION ALL
per combinare le righe per ciascun valore. Questa è probabilmente la tua scommessa migliore supponendo che sia facile per te creare questo SQL in modo dinamico. Per esempio:
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
Ciò produce anche un piano che è in grado di utilizzare la modalità batch e fornisce prestazioni ancora migliori rispetto alla risposta originale. (Anche se in entrambi i casi le prestazioni sono abbastanza veloci che qualsiasi selezione o scrittura dei dati su un tavolo diventa rapidamente il collo di bottiglia.) L' UNION ALL
approccio evita anche di giocare come moltiplicare per 0. A volte è meglio pensare semplice!
CPU time = 8673 ms, elapsed time = 4270 ms.