Phân vùng theo hiệu suất tăng dần và giảm dần


7

Tôi có một bảng và đối với một tập hợp các trường a, b và c đã cho, tôi cần lấy các hàng đầu tiên và cuối cùng được sắp xếp theo d và e, và đang sử dụng ROW_NUMBER để có được các hàng này. Phần có liên quan của tuyên bố là ...

ROW_NUMBER() OVER (PARTITION BY a,b,c ORDER BY d ASC, e ASC) AS row_number_start,
ROW_NUMBER() OVER (PARTITION BY a,b,c ORDER BY d DESC, e DESC) AS row_number_end

Kế hoạch thực hiện cho thấy hai hoạt động sắp xếp, một cho mỗi hoạt động. Các hoạt động sắp xếp này chiếm hơn 60% tổng chi phí của câu lệnh (chúng tôi đang nói về hàng chục triệu hàng ở đây, các phân vùng thường sẽ có 1-100 bản ghi trên mỗi phân vùng, chủ yếu dưới 10)

vì vậy sẽ tốt nếu tôi có thể thoát khỏi một trong số họ. Tôi đã cố gắng tạo một chỉ mục để sao chép sắp xếp; điều này đã loại bỏ một trong các hoạt động sắp xếp nhưng không phải là hoạt động sau. (Lưu ý rằng mọi chỉ mục được tạo sẽ chỉ được sử dụng cho quy trình này và sẽ được tạo lại hàng ngày như một phần của quy trình ETL.)

Từ việc kiểm tra kế hoạch thực hiện, tôi tin rằng vấn đề là khi thực hiện phân vùng bằng câu lệnh, SQL Server khăng khăng yêu cầu sắp xếp theo các cột phân vùng trên cơ sở tăng dần. Về mặt logic, không có vấn đề gì nếu bạn đặt hàng tăng dần hoặc giảm dần, và nếu trình tối ưu hóa hiểu điều này thì nó chỉ có thể đọc cùng một chỉ mục ngược để xử lý row_number_end.

Có cách nào tôi có thể làm cho trình tối ưu hóa có ý nghĩa ở đây không, hoặc ai đó có thể đề xuất một phương pháp thay thế để đạt được mục tiêu cuối cùng không?

Câu trả lời:


8

Bảng ví dụ và chỉ mục

CREATE TABLE dbo.Test
(
    a integer NOT NULL,
    b integer NOT NULL,
    c integer NOT NULL,
    d integer NOT NULL,
    e integer NOT NULL
);

CREATE INDEX i1 ON dbo.Test (a, b, c, d, e);

1. Áp dụng giải pháp

SELECT
    DT.a,
    DT.b,
    DT.c,
    FL.d,
    FL.e 
FROM 
(
    -- Could be an indexed view
    SELECT
        T.a,
        T.b,
        T.c
    FROM dbo.Test AS T
    GROUP BY
        T.a,
        T.b,
        T.c
) AS DT
CROSS APPLY 
(
    (
        -- First
        SELECT TOP (1)
            T2.d,
            T2.e
        FROM  dbo.Test AS T2
        WHERE
            T2.a = DT.a
            AND T2.b = DT.b
            AND T2.c = DT.c
        ORDER BY
            T2.d ASC,
            T2.e ASC
    )

    UNION ALL

    (
        -- Last
        SELECT TOP (1)
            T3.d,
            T3.e
        FROM  dbo.Test AS T3
        WHERE
            T3.a = DT.a
            AND T3.b = DT.b
            AND T3.c = DT.c
        ORDER BY
            T3.d DESC,
            T3.e DESC
    )
) AS FL;

Kế hoạch thực hiện:

Kế hoạch

2. Giải pháp đánh số hàng

Một cách khác, có thể tốt hơn nếu trung bình có một số lượng hàng nhỏ cho mỗi nhóm: (được cải thiện dựa trên đề xuất của Martin Smith)

Truy vấn

SELECT
    TF.a,
    TF.b,
    TF.c,
    TF.d,
    TF.e
FROM
(
    SELECT
        T.*,
        rn = ROW_NUMBER() OVER (
                PARTITION BY a,b,c 
                ORDER BY d ASC, e ASC)
    FROM dbo.Test AS T
) AS TF
WHERE
    TF.rn = 1

UNION ALL

SELECT
    TL2.a,
    TL2.b,
    TL2.c,
    TL2.d,
    TL2.e
FROM 
(
    -- TOP (max bigint) to allow an ORDER BY in this scope
    SELECT TOP (9223372036854775807)
        TL.a,
        TL.b,
        TL.c,
        TL.d,
        TL.e
    FROM 
    (
        SELECT
            T.*,
            rn = ROW_NUMBER() OVER (
                    PARTITION BY a,b,c 
                    ORDER BY d DESC, e DESC)
        FROM dbo.Test AS T
    ) AS TL
    WHERE
        TL.rn = 1
    ORDER BY
        -- To allow the optimizer to scan the index backward
        -- (Workaround for PARTITION BY being assumed ASC)
        TL.a DESC,
        TL.b DESC,
        TL.c DESC,
        TL.d DESC,
        TL.e DESC
) AS TL2;

Kế hoạch thực hiện:

Kế hoạch 2

3. Sử dụng HÀNG ĐẦU

Điều này dựa trên ý tưởng rằng hàng đầu tiên là hàng số 1 và hàng cuối cùng là hàng trước hàng số 1:

SELECT
    L.a,
    L.b,
    L.c,
    L.d,
    L.e
FROM 
(
    -- Add LEAD(1) on numbering
    SELECT 
        N.*,
        next_rn = LEAD(N.rn, 1, 1) OVER (
            PARTITION BY N.a, N.b, N.c
            ORDER BY N.d, N.e) 
    FROM 
    (
        -- Numbered
        SELECT
            T.*,
            rn = ROW_NUMBER() OVER (
                PARTITION BY T.a, T.b, T.c
                ORDER BY T.d, T.e) 
        FROM dbo.Test AS T
    ) AS N
) AS L
WHERE
    -- This row is first, or the next one is
    L.rn = 1
    OR L.next_rn = 1;

Kế hoạch thực hiện

Kế hoạch 3


Để tóm tắt các cuộc thảo luận bình luận:

  • Nếu bạn cần các cột bổ sung được trả về, chỉ cần thêm chúng vào các truy vấn ở những vị trí thích hợp và đảm bảo chúng được bao gồm trong chỉ mục.
  • Khi được tạo, các truy vấn có thể có khả năng hưởng lợi từ các chỉ mục nhiều lần. Với một sắp xếp rõ ràng trong kế hoạch thực hiện, sắp xếp xảy ra trên mỗi thực hiện . Ngoài ra, các loại tạo chỉ mục được phép tự động lấy thêm bộ nhớ khi cần thiết; đó không phải là trường hợp của các loại thông thường - họ có được phân bổ cố định dựa trên ước tính tối ưu hóa, và đó là điều đó. Vượt quá sự phân bổ và sắp xếp tràn vào đĩa.
  • Phương pháp áp dụng là tối ưu cho các nhóm tương đối lớn. Ước tính là 2 hàng trên mỗi lần lặp nhưng 'thực tế' là trên tất cả các lần lặp ( quyết định thiết kế SSMS ). Một số lượng rất lớn các lần lặp là không tốt cho việc áp dụng.
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.