Làm cách nào để sử dụng Chế độ hàng loạt với UNPIVOT (nối vòng lặp)?


12

Tôi có một truy vấn của các hình thức sau đây:

SELECT ...
FROM ColumnstoreTable cs
CROSS APPLY (
    SELECT *
    FROM (VALUES
        ('A', cs.DataA)
        , ('B', cs.DataB)
        , ('C', cs.DataC)
    ) x(Col0, Col1)
) someValues

Điều này nhận mỗi hàng từ truy vấn con được hỗ trợ bởi Cột cửa hàng ( ColumnstoreTable) và nhân các hàng đó. Đây thực chất là một UNPIVOT. Các truy vấn thực sự lớn hơn này. Phần này của các truy vấn nguồn cấp dữ liệu vào xử lý khác.

Vấn đề ở đây là điều này CROSS APPLYđược thực hiện như một phép nối vòng, đây là một lựa chọn hợp lý. Thật không may, tham gia vòng lặp không hỗ trợ chế độ hàng loạt.

Phần truy vấn này rất quan trọng về hiệu năng và tôi nghi ngờ rằng việc chạy nó trong chế độ hàng loạt có thể rất có lợi cho hiệu suất.

Làm cách nào tôi có thể viết lại truy vấn này để không chuyển ra khỏi chế độ hàng loạt?

Tôi đã thử sử dụng một bảng tạm thời thay vì VALUES, nhưng điều đó không thay đổi thực tế là không có điều kiện tham gia bình đẳng để băm tham gia.

Câu trả lời:


12

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:

nhập mô tả hình ảnh ở đây

Nếu tôi thay thế các SELECTcâu lệnh bằng một SUMtrong những CASEtuyê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.

nhập mô tả hình ảnh ở đây

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 dummycộ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 Parallelismtoá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 Scanvà 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 Streamsvậ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 Streamsdẫ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 ALLtiế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.

nhập mô tả hình ảnh ở đây


Thủ thuật UNION ALL rất tốt để biết, nhưng thất bại trong trường hợp truy vấn nguồn không chỉ là một bảng mà là một truy vấn tốn kém.
boot4life
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.