Petunjuk kardinalitas SQL Server

14

Apakah ada cara bagaimana 'menyuntikkan' estimasi kardinalitas ke pengoptimal SQL Server (versi apa pun)?

yaitu sesuatu yang mirip dengan petunjuk kardinalitas Oracle.

Motivasi saya didorong oleh artikel, Seberapa Baik Pengoptimal Kueri, Benarkah? [1] , di mana mereka menguji pengaruh penaksir kardinalitas pada pemilihan rencana yang buruk. Oleh karena itu, akan cukup jika saya bisa memaksa SQL Server untuk 'memperkirakan' kardinalitas dengan tepat untuk permintaan kompleks.


[1] Leis, Viktor, dkk. "Seberapa bagus pengoptimal permintaan?"
Prosiding Endowment VLDB 9.3 (2015): 204-215.

Radim Bača
sumber

Jawaban:

10

Anda bisa mendapatkan sesuatu yang mirip dengan CARDINALITYpetunjuk Oracle dengan menggunakan secara strategis TOPdan fungsi yang ditetapkan pengguna yang disebut MANY() dikembangkan oleh Adam Machanic . Mari kita bekerja melalui beberapa contoh. Saya menggunakan database AdventureWorks yang tersedia secara bebas. Misalkan saya benar-benar perlu mengontrol jumlah baris yang dikembalikan oleh thtabel turunan dalam kueri berikut:

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;

Seperti, saya mendapatkan perkiraan 113443 baris:

mulai kueri

Jika saya perlu menurunkan taksiran mulai thsaya bisa gunakan TOPbersama dengan OPTIMIZE FORpetunjuk kueri untuk menetapkan tujuan baris. Inilah satu cara untuk melakukannya:

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));

Kita dapat melihat bahwa perkiraannya hanya 1 baris:

Perkiraan 1 baris

Saya menetapkan @row_goalke nilai terbesar yang mungkin BIGINTuntuk menghindari mengubah hasil. The OPTIMIZE FORpermintaan petunjuk menginstruksikan optimizer untuk query optimizer seakan @row_goalsama dengan 1. Saya akan mendapatkan hasil yang sama tetapi permintaan akan dioptimalkan berbeda.

Meningkatkan perkiraan kardinalitas lebih sulit. Kami tidak dapat hanya meningkatkan nilai TOPkarena pengoptimal akan menyadari bahwa itu tidak akan menghasilkan cukup baris. Namun, kita bisa menggunakan MANY()fungsi untuk menambahkan baris ke taksiran. Perhatikan bahwa MANY()fungsi akan selalu mengembalikan 0 baris tetapi estimasi baris darinya berubah dengan parameter input. Misalkan Anda perlu menambah taksiran baris dari tabel turunan sebesar 10X. Salah satu cara untuk mencapai itu:

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;

Kita dapat melihat bahwa perkiraannya 10X tabel dasar:

10X permintaan

Superfluous TOPditambahkan untuk mencegah pengoptimal memindahkan tabel. Tanpanya MANY()fungsi dapat diterapkan ke tempat yang salah dalam rencana.

Dimungkinkan untuk menggabungkan kedua teknik jika Anda menginginkan perkiraan yang terlalu tinggi dan bukan hanya mengalikan jumlah baris dengan faktor. Sebagai contoh, anggaplah Anda benar-benar membutuhkan estimasi tabel turunan tepat 10.00000 baris. Salah satu cara untuk mencapai itu:

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));

Kita dapat melihat bahwa perkiraannya adalah 1000000 baris:

1 baris M

Saya perlu mengingatkan Anda bahwa ini adalah teknik canggih yang seringkali tidak diperlukan untuk optimasi kueri. Jika Anda ingin mempelajari lebih lanjut, saya sarankan menonton Clash of the Row Goals yang disajikan oleh Adam Machanic.


Banyak fungsi

-- 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
Joe Obbish
sumber
9

Tidak ada cara untuk menyuntikkan perkiraan kardinalitas langsung ke pengoptimal tetapi tergantung pada apa yang ingin Anda capai ada beberapa opsi.

Anda bisa menggunakan OPTION (FAST N)petunjuk kueri untuk memperkenalkan sasaran baris, dan mungkin menulis ulang kueri Anda menggunakan CTE atau subqueries untuk menyuntikkan TOP...ORDER BYsasaran baris berbasis di berbagai bagian dari rencana eksekusi Anda, tetapi saya tidak yakin seberapa efisien kueri yang dihasilkan akan ketika Anda mulai bermain-main dengan konstruksi yang lebih kompleks.

Lihat Di dalam Pengoptimal: Row Goals In Depth untuk penjelasan yang lebih menyeluruh.

Jika Anda ingin memengaruhi operator yang dipilih pengoptimal, Anda tidak perlu mencoba menyuntikkan perkiraan kardinalitas tetapi Anda dapat menggunakan hal-hal seperti OPTION (MERGE JOIN)atau OPTION (HASH JOIN)misalnya untuk memaksa operator bergabung secara fisik.

Artikel ini membahas lebih rinci tentang bagaimana memengaruhi rencana menggunakan petunjuk: Mengontrol Rencana Eksekusi dengan Petunjuk

Jika Anda ingin memperbaiki rencana, Anda juga dapat menggunakan panduan rencana.

Sekali lagi tidak jelas apa kasus penggunaan aktual Anda, dan jarak tempuh Anda dapat bervariasi dengan menggunakan teknik ini. Dalam banyak kasus, lebih baik membiarkan pengoptimal memutuskan, dan pastikan Anda memiliki statistik terkini sehingga pengoptimal dapat membuat keputusan.


Saran Microsoft Connect yang relevan: Izinkan untuk menentukan petunjuk selektivitas filter dalam kueri oleh xor88. Microsoft merespons:

Terima kasih untuk umpan baliknya. Saya bisa melihat manfaat potensial dari ini. Secara umum kami berusaha keras untuk membuat perilaku otomatis kami sebaik mungkin dan menghindari kebutuhan akan petunjuk semacam ini, tetapi tentu saja kami memiliki banyak petunjuk lain. Kami akan mempertimbangkan ini untuk rilis di masa mendatang tetapi itu akan melampaui versi Denali (11.0).

Salam,
Eric Hanson
Program Manager
SQL Server Query Processing

Tom V - coba topanswers.xyz
sumber
3

Anda dapat menggunakan OPTIMIZE FORpetunjuk kueri SQL Server untuk memaksa estimasi kardinalitas berdasarkan pada nilai yang diisyaratkan daripada menggunakan nilai aktual (parameter) atau nilai yang tidak diketahui (variabel) selama kompilasi. Lihat topik Petunjuk Petunjuk dalam dokumentasi SQL Server untuk detail lengkap.

Misalnya, kueri di bawah ini akan memperkirakan jumlah baris berdasarkan histogram statistik dari nilai yang disiratkan alih-alih kardinalitas rata-rata keseluruhan seperti halnya dengan variabel lokal.

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

Demikian pula, petunjuk itu dapat digunakan untuk parameter sehingga estimasi didasarkan pada histogram statistik dari nilai yang diisyaratkan daripada nilai parameter aktual selama kompilasi.

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;

Kata UNKNOWNkunci dapat ditentukan sebagai ganti literal dalam petunjuk untuk menggunakan kardinalitas rata-rata keseluruhan alih-alih memperkirakan berdasarkan nilai parameter aktual dan histogram statistik.

Dan Guzman
sumber