Gợi ý cardinality của SQL Server


14

Có cách nào để 'đưa' ước tính cardinality vào trình tối ưu hóa Máy chủ SQL (bất kỳ phiên bản nào) không?

tức là một cái gì đó tương tự như gợi ý về cardinality của Oracle.

Động lực của tôi được thúc đẩy bởi bài viết, Tối ưu hóa truy vấn tốt như thế nào? [1] , trong đó họ kiểm tra ảnh hưởng của công cụ ước tính cardinality đối với việc lựa chọn một kế hoạch xấu. Do đó, sẽ là đủ nếu tôi có thể buộc Máy chủ SQL 'ước tính' chính xác các thông số cho các truy vấn phức tạp.


[1] Leis, Viktor, et al. "Làm thế nào tốt là tối ưu hóa truy vấn, thực sự?"
Thủ tục tố tụng của VLDB 9.3 (2015): 204-215.

Câu trả lời:


10

Bạn có thể nhận được một cái gì đó tương tự như CARDINALITYgợi ý của Oracle bằng cách sử dụng chiến lược TOPvà một chức năng MANY() do người dùng xác định được gọi là được phát triển bởi Adam Machanic . Hãy làm việc qua một vài ví dụ. Tôi đang sử dụng cơ sở dữ liệu AdventureWorks có sẵn miễn phí. Giả sử rằng tôi thực sự cần kiểm soát số lượng hàng được trả về bởi thbảng dẫn xuất trong truy vấn sau:

SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
) th ON p.ProductID = th.ProductID;

Như vậy, tôi nhận được ước tính 113443 hàng:

bắt đầu truy vấn

Nếu tôi cần hạ ước tính từ thtôi có thể sử dụng TOPcùng với OPTIMIZE FORgợi ý truy vấn để đặt mục tiêu hàng. Đây là một cách để làm điều đó:

DECLARE @row_goal BIGINT = 9223372036854775807;
SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT TOP (@row_goal) ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
) th ON p.ProductID = th.ProductID
OPTION (OPTIMIZE FOR (@row_goal = 1));

Chúng ta có thể thấy rằng ước tính chỉ là 1 hàng:

Ước tính 1 hàng

Tôi đặt giá trị @row_goallớn nhất có thể BIGINTđể tránh thay đổi kết quả. Các OPTIMIZE FORgợi ý truy vấn chỉ thị ưu để tối ưu hóa các truy vấn như thể@row_goal bằng 1. Tôi sẽ nhận được kết quả tương tự nhưng truy vấn sẽ được tối ưu hóa khác nhau.

Tăng một ước tính cardinality là khó khăn hơn. Chúng ta không thể tăng giá trị TOPvì trình tối ưu hóa sẽ nhận ra rằng nó sẽ không trả về đủ số hàng. Tuy nhiên, chúng ta có thể sử dụng MANY()hàm để thêm hàng vào ước tính. Lưu ý rằng MANY()hàm sẽ luôn trả về 0 hàng nhưng ước tính hàng từ nó thay đổi theo tham số đầu vào. Giả sử rằng bạn cần tăng ước tính hàng từ bảng dẫn xuất thêm 10 lần. Một cách để thực hiện điều đó:

SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT TOP (9223372036854775807) ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
    LEFT OUTER JOIN dbo.Many(10) AS m ON 1=1
) th ON p.ProductID = th.ProductID;

Chúng ta có thể thấy rằng ước tính là 10 lần bảng cơ sở:

Truy vấn 10X

Không cần thiết TOPđã được thêm vào để ngăn chặn trình tối ưu hóa di chuyển các bảng xung quanh. Không có nó, MANY()chức năng có thể được áp dụng cho vị trí sai trong kế hoạch.

Có thể kết hợp hai kỹ thuật nếu bạn muốn đánh giá quá cao chính xác thay vì chỉ nhân số lượng hàng với một yếu tố. Ví dụ: giả sử rằng bạn thực sự cần ước tính của bảng dẫn xuất chính xác là 1000000 hàng. Một cách để thực hiện điều đó:

DECLARE @row_goal BIGINT = 9223372036854775807;

SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT TOP (@row_goal) ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
    LEFT OUTER JOIN dbo.Many(10) AS m ON
        1=1
) th ON p.ProductID = th.ProductID
OPTION (OPTIMIZE FOR (@row_goal = 1000000));

Chúng ta có thể thấy rằng ước tính là 1000000 hàng:

Hàng 1 M

Tôi cần cảnh báo bạn rằng đây là những kỹ thuật nâng cao thường không cần thiết cho tối ưu hóa truy vấn. Nếu bạn muốn tìm hiểu thêm, tôi khuyên bạn nên xem Clash of the Row Goals do Adam Machanic trình bày.


Chức năng dbo.Many

-- By Adam Machanic, reproduced with permission
IF EXISTS (SELECT * FROM sys.objects WHERE name = 'Many' AND OBJECT_SCHEMA_NAME(object_id) = 'dbo')
    DROP FUNCTION dbo.Many
GO
CREATE FUNCTION dbo.Many(@n INT)
RETURNS TABLE AS
RETURN
(
    WITH
    a(x) AS
    (
        SELECT
            *
        FROM
        (
            VALUES
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)
        ) AS x0(x)
    )
    SELECT TOP(@n)
        1 AS x
    FROM
        a AS a1,
        a AS a2
    WHERE
        a1.x % 2 = 0
)
GO

9

Không có cách nào để đưa ước lượng cardinality trực tiếp vào trình tối ưu hóa nhưng tùy thuộc vào những gì bạn muốn đạt được, có một vài lựa chọn.

Bạn có thể sử dụng một OPTION (FAST N)gợi ý truy vấn để giới thiệu các mục tiêu hàng và có thể viết lại truy vấn của mình bằng cách sử dụng CTE hoặc truy vấn con để TOP...ORDER BYthực hiện các mục tiêu hàng dựa trên các phần khác nhau trong kế hoạch thực hiện của bạn, nhưng tôi không chắc truy vấn kết quả của bạn sẽ hiệu quả như thế nào khi bạn bắt đầu chơi xung quanh với các công trình phức tạp hơn.

Xem Bên trong Trình tối ưu hóa: Mục tiêu hàng sâu trong độ sâu để được giải thích kỹ hơn.

Nếu bạn muốn tác động đến các toán tử mà trình tối ưu hóa chọn, bạn không cần phải cố gắng đưa ra các ước tính về số lượng thẻ nhưng bạn có thể sử dụng những thứ như OPTION (MERGE JOIN)hoặcOPTION (HASH JOIN) ví dụ để buộc các toán tử nối vật lý.

Bài viết này đi sâu vào chi tiết hơn về cách ảnh hưởng đến một kế hoạch bằng cách sử dụng các gợi ý: Kiểm soát các kế hoạch thực hiện bằng các gợi ý

Nếu bạn muốn sửa chữa một kế hoạch, bạn cũng có thể sử dụng một hướng dẫn kế hoạch.

Một lần nữa, không rõ trường hợp sử dụng thực tế của bạn là gì và số dặm của bạn có thể thay đổi khi sử dụng các kỹ thuật này. Trong nhiều trường hợp, tốt hơn hết là cứ để trình tối ưu hóa quyết định và đảm bảo bạn có số liệu thống kê cập nhật để trình tối ưu hóa có thể đưa ra quyết định sáng suốt.


Đề xuất Microsoft Connect có liên quan: Cho phép chỉ định gợi ý chọn lọc bộ lọc trong các truy vấn theo xor88. Microsoft đã trả lời:

Cảm ơn vì bạn đã phản hồi. Tôi có thể thấy lợi ích tiềm năng của việc này. Nói chung, chúng tôi cố gắng hết sức để thực hiện hành vi tự động của mình tốt nhất có thể và tránh sự cần thiết của loại gợi ý này, nhưng tất nhiên chúng tôi có nhiều gợi ý khác. Chúng tôi sẽ xem xét điều này để phát hành trong tương lai nhưng nó sẽ vượt ra ngoài phiên bản Denali (11.0).

Trân trọng,
Eric Hanson
Trình quản lý chương trình
SQL Server Xử lý truy vấn


3

Bạn có thể sử dụng OPTIMIZE FORgợi ý truy vấn SQL Server để ép buộc ước tính cardinality dựa trên các giá trị được gợi ý thay vì sử dụng giá trị thực (tham số) hoặc giá trị không xác định (biến) trong quá trình biên dịch. Xem chủ đề Gợi ý truy vấn trong tài liệu SQL Server để biết chi tiết đầy đủ.

Ví dụ: truy vấn bên dưới sẽ ước tính số lượng hàng dựa trên biểu đồ thống kê từ các giá trị được gợi ý thay vì tổng số trung bình chung như với các biến cục bộ.

DECLARE 
      @StartDate datetime = '20150101'
    , @EndDate datetime = '20150102';
SELECT *
FROM dbo.Example
WHERE
    DateColumn BETWEEN  @StartDate AND @EndDate
OPTION(OPTIMIZE FOR(@StartDate = '20100101', @EndDate='20100101'));

Tương tự, gợi ý có thể được sử dụng cho các tham số để ước tính dựa trên biểu đồ thống kê từ các giá trị được gợi ý thay vì giá trị tham số thực tế trong quá trình biên dịch.

DECLARE 
      @StartDate datetime = '20150101'
    , @EndDate datetime = '20150102';
EXECUTE sp_executesql N'SELECT *
        FROM dbo.Example
        WHERE
            DateColumn BETWEEN  @StartDate AND @EndDate
        OPTION(OPTIMIZE FOR(@StartDate = ''20100101'', @EndDate=''20100101''));'
    , N'@StartDate datetime, @EndDate datetime'
    , @StartDate = @StartDate
    , @EndDate = @EndDate;

Các UNKNOWNtừ khóa có thể được xác định thay vì một chữ trong gợi ý để sử dụng cardinality tổng thể trung bình thay vì ước tính dựa trên giá trị tham số và số liệu thống kê biểu đồ thực tế.

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.