Một cách tiếp cận có thể là sử dụng bảng #temp cho các giá trị và cũng giới thiệu một cột Equijoin giả để cho phép tham gia băm. Ví dụ:
-- 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
Hiệu suất và kế hoạch truy vấn
Cách tiếp cận này mang lại một kế hoạch truy vấn như sau và khớp băm được thực hiện trong chế độ hàng loạt:
Nếu tôi thay thế các SELECT
câu lệnh bằng một SUM
trong những CASE
tuyên bố nhằm tránh phải truyền tất cả những hàng đến giao diện điều khiển và sau đó chạy các truy vấn trên một bảng hàng columnstore 100 triệu thực tôi đã nằm xung quanh, tôi thấy hiệu suất khá tốt để tạo ra 300MM điều kiện tiên quyết hàng:
CPU time = 33803 ms, elapsed time = 4363 ms.
Và kế hoạch thực tế cho thấy sự song song tốt của phép nối băm.
Lưu ý về song song nối băm khi tất cả các hàng có cùng giá trị
Hiệu năng của truy vấn này phụ thuộc rất nhiều vào từng luồng ở phía đầu dò của phép nối có quyền truy cập vào bảng băm đầy đủ (trái ngược với phiên bản phân vùng băm, sẽ ánh xạ tất cả các hàng thành một luồng duy nhất với điều kiện chỉ có một giá trị riêng biệt cho dummy
cột).
May mắn thay, điều này đúng trong trường hợp này (như chúng ta có thể thấy do thiếu Parallelism
toán tử ở phía đầu dò) và đáng tin là đúng vì chế độ hàng loạt xây dựng một bảng băm duy nhất được chia sẻ trên các luồng. Do đó, mỗi luồng có thể lấy các hàng của chúng từ Columnstore Index Scan
và khớp chúng với bảng băm được chia sẻ đó. Trong SQL Server 2012, chức năng này ít được dự đoán hơn vì sự cố tràn khiến nhà điều hành khởi động lại ở chế độ Hàng, vừa mất lợi ích của chế độ hàng loạt, vừa yêu cầu người Repartition Streams
vận hành ở phía đầu dò của phép nối sẽ gây ra sai lệch luồng trong trường hợp này . Cho phép sự cố tràn vẫn ở chế độ hàng loạt là một cải tiến lớn trong SQL Server 2014.
Theo hiểu biết của tôi, chế độ hàng không có khả năng bảng băm được chia sẻ này. Tuy nhiên, trong một số trường hợp, thông thường với ước tính có ít hơn 100 hàng ở phía xây dựng, SQL Server sẽ tạo một bản sao riêng của bảng băm cho mỗi luồng (có thể được xác định bằng cách Distribute Streams
dẫn vào tham gia băm). Điều này có thể rất mạnh, nhưng kém tin cậy hơn nhiều so với chế độ hàng loạt vì nó phụ thuộc vào ước tính số lượng thẻ của bạn và SQL Server đang cố gắng đánh giá lợi ích so với chi phí xây dựng bản sao đầy đủ của bảng băm cho mỗi luồng.
UNION ALL: một sự thay thế đơn giản hơn
Paul White đã chỉ ra rằng một tùy chọn khác, và có khả năng đơn giản hơn, sẽ được sử dụng UNION ALL
để kết hợp các hàng cho mỗi giá trị. Đây có thể là sự đánh cược tốt nhất của bạn khi cho rằng bạn dễ dàng xây dựng SQL này một cách linh hoạt. Ví dụ:
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
Điều này cũng mang lại một kế hoạch có thể sử dụng chế độ hàng loạt và cung cấp hiệu suất thậm chí tốt hơn so với câu trả lời ban đầu. (Mặc dù trong cả hai trường hợp, hiệu suất đủ nhanh để bất kỳ việc chọn hoặc ghi dữ liệu nào vào bảng sẽ nhanh chóng trở thành nút thắt.) Cách UNION ALL
tiếp cận cũng tránh chơi các trò chơi như nhân với 0. Đôi khi, tốt nhất bạn nên nghĩ đơn giản!
CPU time = 8673 ms, elapsed time = 4270 ms.