Bagaimana SQL Server mengetahui predikat berkorelasi?

15

Saat mendiagnosis SQL Server 2008 R2 kueri dengan estimasi kardinalitas yang buruk (meskipun pengindeksan sederhana, statistik terbaru, dll.) Dan karenanya rencana kueri yang buruk, saya menemukan artikel KB yang mungkin terkait: FIX: Kinerja buruk ketika Anda menjalankan kueri yang berisi predikat berkorelasi DAN di SQL Server 2008 atau di SQL Server 2008 R2 atau di SQL Server 2012

Saya bisa menebak apa yang dimaksud artikel KB dengan "berkorelasi", misalnya predikat # 2 dan predikat # 1 sebagian besar menargetkan baris yang sama.

Tapi saya tidak tahu bagaimana SQL Server tahu tentang korelasi ini. Apakah tabel memerlukan indeks multi-kolom yang berisi kolom dari kedua predikat? Apakah SQL menggunakan statistik untuk memeriksa apakah nilai dari satu kolom berkorelasi dengan yang lain? Atau apakah ada metode lain yang digunakan?

Saya menanyakan ini karena dua alasan:

  1. untuk menentukan tabel dan kueri mana yang mungkin diperbaiki menggunakan perbaikan terbaru ini
  2. untuk mengetahui apa yang harus saya lakukan dalam pengindeksan, statistik, dll untuk mempengaruhi # 1
Justin Grant
sumber

Jawaban:

20

Pertimbangkan permintaan dan rencana eksekusi AdventureWorks sederhana yang ditunjukkan di bawah ini. Kueri berisi predikat yang terhubung dengan AND. Perkiraan kardinalitas pengoptimal adalah 41.211 baris:

-- Estimate 41,211 rows
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE 
    TH.TransactionID BETWEEN 100000 AND 168336
    AND TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13';

Paket eksekusi standar

Menggunakan statistik default

Diberikan hanya statistik satu kolom, optimizer menghasilkan estimasi ini dengan memperkirakan kardinalitas untuk setiap predikat secara terpisah, dan mengalikan selektivitas yang dihasilkan secara bersamaan. Heuristik ini mengasumsikan bahwa predikat sepenuhnya independen.

Memisahkan kueri menjadi dua bagian membuat perhitungan lebih mudah dilihat:

-- Estimate 68,336.4 rows
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE 
    TH.TransactionID BETWEEN 100000 AND 168336;

Tabel Riwayat Transaksi berisi 113.443 baris secara total, sehingga estimasi 68.336.4 mewakili selektivitas 68336.4 / 113443 = 0.60238533 untuk predikat ini. Estimasi ini diperoleh dengan menggunakan informasi histogram untuk TransactionIDkolom, dan nilai konstan yang ditentukan dalam kueri.

-- Estimate 68,413 rows
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE 
    TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13';

Predikat ini memiliki perkiraan selektivitas 68413.0 / 113443 = 0.60306056 . Sekali lagi, dihitung dari nilai konstan predikat dan histogram dari TransactionDateobjek statistik.

Dengan asumsi predikat sepenuhnya independen, kita dapat memperkirakan selektivitas dua predikat bersama dengan mengalikannya bersama. Perkiraan kardinalitas akhir diperoleh dengan mengalikan selektivitas yang dihasilkan dengan 113.443 baris dalam tabel dasar:

0.60238533 * 0.60306056 * 113443 = 41210.987

Setelah pembulatan, ini adalah perkiraan 41.211 yang terlihat dalam kueri asli (pengoptimal juga menggunakan matematika floating point secara internal).

Bukan perkiraan yang bagus

The TransactionIDdan TransactionDatekolom memiliki korelasi yang erat di AdventureWorks data yang ditetapkan (sebagai monoton meningkat kunci dan tanggal kolom sering). Korelasi ini berarti bahwa asumsi independensi dilanggar. Sebagai akibatnya, rencana kueri pasca-eksekusi menunjukkan 68.095 baris daripada yang diperkirakan 41.211:

Rencana pasca-eksekusi

Lacak bendera 4137

Mengaktifkan tanda jejak ini mengubah heuristik yang digunakan untuk menggabungkan predikat. Alih-alih mengasumsikan independensi penuh, pengoptimal menganggap bahwa selektivitas dari dua predikat cukup dekat sehingga cenderung berkorelasi:

-- Estimate 68,336.4
SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE 
    TH.TransactionID BETWEEN 100000 AND 168336
    AND TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13'
OPTION (QUERYTRACEON 4137);

Ingat bahwa TransactionIDpredikat saja diperkirakan 68.336,4 baris dan TransactionDatepredikat sendiri diperkirakan 68.413 baris. Pengoptimal memilih yang lebih rendah dari dua perkiraan ini daripada mengalikan selektivitas.

Ini hanya heuristik yang berbeda, tentu saja, tetapi yang dapat membantu meningkatkan taksiran untuk kueri dengan ANDpredikat berkorelasi . Setiap predikat dipertimbangkan untuk kemungkinan korelasi, dan ada penyesuaian lain yang dibuat ketika banyak ANDklausa terlibat, tetapi contoh itu berfungsi untuk menunjukkan dasar-dasar itu.

Statistik multi-kolom

Ini dapat membantu dalam kueri dengan korelasi, tetapi informasi histogram masih didasarkan hanya pada kolom utama dari statistik. Oleh karena itu, statistik multi-kolom kandidat berikut berbeda dalam cara yang penting:

CREATE STATISTICS
    [stats Production.TransactionHistory TransactionID TransactionDate]
ON Production.TransactionHistory
    (TransactionID, TransactionDate);

CREATE STATISTICS
    [stats Production.TransactionHistory TransactionDate TransactionID]
ON Production.TransactionHistory
    (TransactionDate, TransactionID);

Mengambil hanya satu dari itu, kita dapat melihat bahwa satu-satunya informasi tambahan adalah tingkat tambahan dari kepadatan 'semua'. Histogram masih hanya berisi informasi terperinci tentang TransactionDatekolom.

DBCC SHOW_STATISTICS
    (
        'Production.TransactionHistory', 
        'stats Production.TransactionHistory TransactionDate TransactionID'
    );

Statstik multi-kolom

Dengan statistik multi-kolom ini tersedia ...

SELECT COUNT_BIG(*)
FROM Production.TransactionHistory AS TH
WHERE 
    TH.TransactionID BETWEEN 100000 AND 168336
    AND TH.TransactionDate BETWEEN '2007-09-01' AND '2008-03-13';

... rencana eksekusi menunjukkan perkiraan yang persis sama dengan ketika hanya statistik satu kolom yang tersedia:

Paket statistik multi-kolom

Paul White Reinstate Monica
sumber