Одним із підходів може бути використання таблиці #temp для значень, а також введення стовпчика фіктивного еджійона, щоб дозволити хеш-з'єднання. Наприклад:
-- 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
План продуктивності та запиту
Цей підхід дає план запитів на зразок наступного, і хеш-відповідність виконується в пакетному режимі:
Якщо я заміню SELECT
оператор SUM
на CASE
оператор, щоб уникнути необхідності передавати всі ці рядки до консолі, а потім запустити запит у реальну таблицю стовпців стовпців 100 ММ, у якій я лежу, я бачу досить хороші показники для створення необхідних 300 мм рядки:
CPU time = 33803 ms, elapsed time = 4363 ms.
І власне план показує хорошу паралелізацію хеш-з'єднання.
Примітки про паралелізацію хеш-з'єднання, коли всі рядки мають однакове значення
Виконання цього запиту сильно залежить від кожного потоку на стороні зонда з'єднання, що має доступ до повної хеш-таблиці (на відміну від хеш-розділеної версії, яка відображатиме всі рядки в один потік, враховуючи, що існує лише одне чітке значення для dummy
стовпчика).
На щастя, це вірно в цьому випадку (як ми бачимо за відсутністю Parallelism
оператора на стороні зонда) і надійно повинно бути правдою, оскільки пакетний режим створює єдину хеш-таблицю, яка поділяється по потоках. Таким чином, кожен потік може взяти свої рядки з Columnstore Index Scan
та зіставити їх із загальною таблицею хешу. У SQL Server 2012 ця функціональність була набагато менш передбачуваною, оскільки розлив призвів до того, що оператор перезапустився в рядковому режимі, втрачаючи при цьому перевагу пакетного режиму, а також вимагаючи від Repartition Streams
оператора з боку зонда з'єднання, що може спричинити перекос потоку в цьому випадку . Дозвіл розливів залишається в пакетному режимі - це головне вдосконалення для SQL Server 2014.
Наскільки мені відомо, рядовий режим не має такої можливості спільної хеш-таблиці. Однак у деяких випадках, як правило, з оцінкою менше 100 рядків на стороні збірки, SQL Server створить окрему копію хеш-таблиці для кожного потоку (ідентифікується Distribute Streams
ведучим у хеш-з'єднанні). Це може бути дуже потужним, але набагато менш надійним, ніж пакетний режим, оскільки це залежить від ваших оцінок кардинальності, і SQL Server намагається оцінити переваги порівняно з витратами на створення повної копії хеш-таблиці для кожного потоку.
СОЮЗ ВСІ: простіша альтернатива
Пол Вайт зазначив, що ще одним, і потенційно простішим, варіантом буде використання UNION ALL
для об'єднання рядків для кожного значення. Це, швидше за все, найкраща ставка, припускаючи, що вам легко створити цей SQL динамічно. Наприклад:
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
Це також дає план, який здатний використовувати пакетний режим і забезпечує навіть кращу продуктивність, ніж вихідна відповідь. (Хоча в обох випадках продуктивність досить швидка, що будь-який вибір або запис даних у таблицю швидко перетворюється на вузьке місце.) UNION ALL
Підхід також дозволяє уникнути гри в такі ігри, як множення на 0. Іноді найкраще думати просто!
CPU time = 8673 ms, elapsed time = 4270 ms.