Perkiraan kardinalitas di luar histogram

14

Mempersiapkan

Saya mengalami kesulitan memahami perkiraan kardinalitas. Inilah pengaturan pengujian saya:

  • versi 2010 dari database Stack Overflow
  • SQL Server 2017 CU15 + GDR (KB4505225) - 14.0.3192.2
  • CE baru (tingkat kompatibilitas 140)

Saya punya proc ini:

USE StackOverflow2010;
GO

CREATE OR ALTER PROCEDURE #sp_PostsByCommentCount
    @CommentCount int
AS
BEGIN
    SELECT * 
    FROM dbo.Posts p
    WHERE 
        p.CommentCount = @CommentCount
    OPTION (RECOMPILE); 
END;
GO

Tidak ada indeks atau statistik nonclustered di atas dbo.Postsmeja (ada indeks berkerumun di Id).

Ketika meminta perkiraan rencana untuk ini, "baris yang diperkirakan" keluar dbo.Postsadalah 1.934,99:

EXEC #sp_PostsByCommentCount @CommentCount = 51;

Objek statistik berikut secara otomatis dibuat ketika saya meminta perkiraan paket:

DBCC SHOW_STATISTICS('dbo.Posts', [_WA_Sys_00000006_0519C6AF]);

tangkapan layar output statistik dalam SSMS

Sorotan dari itu adalah:

  • Statistik memiliki tingkat sampel cukup rendah 1,81% (67.796 / 3.744.192)
  • Hanya 31 langkah histogram yang digunakan
  • Nilai "Semua kepadatan" adalah 0.03030303(33 nilai berbeda diambil sampelnya)
  • Terakhir RANGE_HI_KEYdalam histogram adalah 50, dengan EQ_ROWS1

Pertanyaan

Melewati nilai apa pun yang lebih tinggi dari 50 (hingga dan termasuk 2.147.483.647) menghasilkan estimasi baris 1.934,99. Perhitungan atau nilai apa yang digunakan untuk menghasilkan estimasi ini? Penaksir kardinalitas warisan menghasilkan perkiraan 1 baris.

Apa yang saya coba

Berikut adalah beberapa teori yang saya miliki, hal-hal yang saya coba, atau sedikit informasi tambahan yang dapat saya gali sambil melihat ke dalam ini.

Vektor Kepadatan

Saya awalnya berpikir itu akan menjadi vektor kerapatan, sama seperti jika saya telah menggunakan OPTION (OPTIMIZE FOR UNKNOWN). Tetapi vektor kerapatan untuk objek statistik ini adalah 3.744.192 * 0,03030303 = 113,460, jadi bukan itu.

Acara yang Diperpanjang

Saya mencoba menjalankan sesi Extended Event yang mengumpulkan query_optimizer_estimate_cardinalityacara (yang saya pelajari dari posting blog Paul White Estimasi Kardinalitas: Menggabungkan Statistik Kepadatan ), dan mendapatkan semacam informasi menarik yang menarik:

<CalculatorList>
  <FilterCalculator CalculatorName="CSelCalcColumnInInterval" Selectivity="-1.000" 
                    CalculatorFailed="true" TableName="[p]" ColumnName="CommentCount" />

  <FilterCalculator CalculatorName="CSelCalcAscendingKeyFilter" Selectivity="0.001" 
                    TableName="[p]" ColumnName="CommentCount" UseAverageFrequency="true" 
                    StatId="4" />
</CalculatorList>

Jadi tampaknya CSelCalcAscendingKeyFilterkalkulator itu digunakan (yang lain mengatakan itu gagal, apa pun artinya). Kolom ini bukan kunci, atau unik, atau harus naik, tetapi apa pun.

Melakukan beberapa Googling dari istilah itu membawa saya ke beberapa posting blog:

Pos-pos ini menunjukkan basis CE baru yang diestimasi di luar histogram ini pada kombinasi vektor kepadatan dan penghitung modifikasi stat. Sayangnya, saya sudah mengesampingkan vektor kerapatan (saya pikir ?!), dan penghitung modifikasi adalah nol ( sys.dm_db_stats_propertiestetap).

Lacak Bendera

Forrest menyarankan saya mengaktifkan TF 2363 untuk mendapatkan informasi lebih lanjut tentang proses estimasi. Saya pikir hal yang paling relevan dari output itu adalah ini:

Plan for computation:

  CSelCalcAscendingKeyFilter(avg. freq., QCOL: [p].CommentCount)

Selectivity: 0.000516798

Ini adalah terobosan (terima kasih, Forrest!): 0.000516798Angka itu (yang tampaknya tidak membantu dalam Selectivity="0.001"atribut XE di atas) dikalikan dengan jumlah baris dalam tabel adalah perkiraan yang saya cari (1,934,99).

Saya mungkin kehilangan sesuatu yang jelas, tetapi saya belum dapat merekayasa balik bagaimana nilai selektivitas dihasilkan di dalam CSelCalcAscendingKeyFilterkalkulator.

Josh Darnell
sumber

Jawaban:

13

Berdasarkan pengujian saya, perkiraan kardinalitas di luar batas hanyalah akar kuadrat dari jumlah baris, dibatasi di bawah ini dengan jumlah baris yang ditambahkan sejak pembaruan statistik terakhir, dan dibatasi di atas oleh rata-rata baris per nilai.

Dalam kasus Anda, 1,934,99 = SQRT (3744192)

Setup pengujian di bawah ini:

--setup
USE TestDB
ALTER DATABASE [TestDB] SET AUTO_UPDATE_STATISTICS OFF
GO

DROP TABLE IF EXISTS dbo.Hist

CREATE TABLE dbo.Hist (
ID int identity primary key,
Num int
)

INSERT dbo.Hist
SELECT TOP 300
(ROW_NUMBER() OVER(ORDER BY(SELECT 1/0)))%3
FROM master..spt_values a
CROSS JOIN master..spt_values b
--Get estimated plan
--don't forget to run right after setup to auto-create stats
SELECT *
FROM dbo.Hist
WHERE Num = 1000
--gradually add rows, then rerun estimate above
INSERT dbo.Hist
SELECT TOP 100
-1
FROM master..spt_values a
--I sure hope you weren't testing this in prod (cleanup)
ALTER DATABASE [TestDB] SET AUTO_UPDATE_STATISTICS ON
GO

Perkiraan bahkan yang mengejutkan dihasilkan dari pendekatan ini: 20 pada 400 total baris, 30 pada 900, 40 pada 1600, dll.

Melewati 10.000, estimasi baris maksimal pada 100, yang merupakan jumlah baris per nilai dalam statistik yang ada. Menambahkan hanya 10 baris akan menetapkan estimasi menjadi 10, karena sqrt (300)> 10.

Dengan demikian estimasi dapat diungkapkan menggunakan rumus ini:

Estimate = MIN(SQRT(AC), MIN(AR, MC))

Perhatikan bahwa jika statistik dijadikan sampel, maka MC tidak dipertimbangkan. Jadi rumusnya menjadi:

Estimate = MIN(SQRT(AC), AR))

Dimana

  • MC adalah "jumlah modifikasi" (# modifikasi sejak statistik dibuat)
  • AC adalah "kardinalitas yang disesuaikan" (# baris dari statistik ditambah MC),
  • AR adalah baris rata-rata per nilai (# baris dari statistik dibagi dengan nilai berbeda di kolom)

Rumus untuk perkiraan ini, dan detail lain tentang kalkulator, dapat ditemukan di posting blog ini: Menganalisa Perkiraan dari CSelCalcAscendingKeyFilter Calculator

Forrest
sumber