Perkiraan Kardinalitas untuk operator LIKE (Variabel Lokal)

24

Saya mendapat kesan bahwa ketika menggunakan LIKEoperator dalam semua mengoptimalkan untuk skenario yang tidak diketahui baik legacy dan CE baru menggunakan perkiraan 9% (dengan asumsi bahwa statistik yang relevan tersedia dan pengoptimal permintaan tidak harus menggunakan tebakan selektivitas).

Saat menjalankan kueri di bawah ini terhadap basis data kredit, saya mendapatkan taksiran berbeda di bawah CE yang berbeda. Di bawah CE baru saya menerima estimasi 900 baris yang saya harapkan, di bawah CE warisan saya menerima estimasi 241,416 dan saya tidak tahu bagaimana estimasi ini diturunkan. Apakah ada yang bisa menjelaskan?

-- New CE (Estimate = 900)
DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM [Credit].[dbo].[member]
WHERE [lastname] LIKE @LastName;

-- Forcing Legacy CE (Estimate = 241.416)
DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM [Credit].[dbo].[member]
WHERE [lastname] LIKE @LastName
OPTION (
QUERYTRACEON 9481,
QUERYTRACEON 9292,
QUERYTRACEON 9204,
QUERYTRACEON 3604
);

Dalam skenario saya, saya sudah memiliki basis data kredit yang disetel ke tingkat kompatibilitas 120, karenanya mengapa dalam kueri kedua saya menggunakan bendera jejak untuk memaksa legacy CE dan juga memberikan informasi tentang statistik apa yang digunakan / dipertimbangkan oleh pengoptimal kueri. Saya dapat melihat statistik kolom tentang 'nama belakang' sedang digunakan, tetapi saya masih belum bisa mengetahui bagaimana perkiraan 241.416 diturunkan.

Saya tidak dapat menemukan sesuatu yang online selain artikel Itzik Ben-Gan ini , yang menyatakan "Ketika menggunakan predikat LIKE dalam semua optimalkan untuk skenario yang tidak diketahui, baik legacy dan CE baru menggunakan estimasi 9 persen.". Informasi dalam posting itu tampaknya salah.

Fza
sumber

Jawaban:

28

Tebakan untuk LIKE dalam kasus Anda didasarkan pada:

  • G: Tebakan standar 9% ( sqllang!x_Selectivity_Like)
  • M: Faktor 6 (angka ajaib)
  • D: Panjang data rata-rata dalam byte (dari statistik), dibulatkan ke bawah menjadi bilangan bulat

Secara khusus, sqllang!CCardUtilSQL7::ProbLikeGuessmenggunakan:

Selectivity (S) = G / M * LOG(D)

Catatan:

  • The LOG(D)Istilah dihilangkan jika Dantara 1 dan 2.
  • Jika Dkurang dari 1 (termasuk yang hilang atau NULLstatistik):
    D = FLOOR(0.5 * maximum column byte length)

Keunikan dan kerumitan semacam ini sangat khas pada CE asli.

Dalam contoh pertanyaan, panjang rata-rata adalah 5 (5,6154 dari DBCC SHOW_STATISTICSdibulatkan ke bawah):

Perkiraan = 10.000 * (0,09 / 6 * LOG (5)) = 241,416

Nilai contoh lainnya:

 D   = Perkirakan menggunakan rumus untuk S
 15 = 406.208
 14 = 395.859
 13 = 384.742
 12 = 372.736
 11 = 359.684
 10 = 345.388
 09 = 329.584
 08 = 311.916
 07 = 291.887
 06 = 268.764
 05 = 241.416
 04 = 207.944
 03 = 164.792
 02 = 150.000 (LOG tidak digunakan)
 01 = 150.000 (LOG tidak digunakan)
 00 = 291.887 (LOG 7) / * FLOOR (0,5 * 15) [15 karena nama belakangnya adalah varchar (15)] * /

Alat uji

DECLARE
    @CharLength integer = 5, -- Set length here
    @Counter integer = 1;

CREATE TABLE #T (c1 varchar(15) NULL);

-- Add 10,000 rows
SET NOCOUNT ON;
SET STATISTICS XML OFF;

BEGIN TRANSACTION;
WHILE @Counter <= 10000
BEGIN
    INSERT #T (c1) VALUES (REPLICATE('X', @CharLength));
    SET @Counter = @Counter + 1;
END;
COMMIT TRANSACTION;

SET NOCOUNT OFF;
SET STATISTICS XML ON;

-- Test query
DECLARE @Like varchar(15);
SELECT * FROM #T AS T 
WHERE T.c1 LIKE @Like;

DROP TABLE #T;
Paul White mengatakan GoFundMonica
sumber
15

Saya diuji pada SQL Server 2014 dengan legacy CE dan tidak mendapatkan 9% sebagai perkiraan kardinalitas. Saya tidak dapat menemukan sesuatu yang akurat secara online sehingga saya melakukan beberapa pengujian dan saya menemukan model yang sesuai dengan semua kasus pengujian yang saya coba, tetapi saya tidak dapat memastikan bahwa itu lengkap.

Dalam model yang saya temukan, taksiran diperoleh dari jumlah baris dalam tabel, panjang kunci rata-rata statistik untuk kolom yang difilter, dan terkadang panjang jenis data dari kolom yang difilter. Ada dua formula berbeda yang digunakan untuk estimasi.

Jika FLOOR (panjang kunci rata-rata) = 0 maka rumus estimasi mengabaikan statistik kolom dan membuat estimasi berdasarkan panjang tipe data. Saya hanya menguji dengan VARCHAR (N) sehingga ada kemungkinan bahwa ada formula yang berbeda untuk NVARCHAR (N). Berikut ini rumus untuk VARCHAR (N):

(estimasi baris) = (baris dalam tabel) * (-0.004869 + 0,032649 * log10 (panjang tipe data))

Ini sangat pas, tapi tidak sepenuhnya akurat:

grafik rumus pertama

Sumbu x adalah panjang tipe data dan sumbu y adalah jumlah baris yang diperkirakan untuk tabel dengan 1 juta baris.

Pengoptimal kueri akan menggunakan rumus ini jika Anda tidak memiliki statistik pada kolom atau jika kolom memiliki nilai NULL yang cukup untuk mengarahkan panjang kunci rata-rata ke bawah 1.

Misalnya, anggap Anda memiliki tabel dengan baris 150k dengan pemfilteran pada VARCHAR (50) dan tidak ada statistik kolom. Prediksi perkiraan baris adalah:

150000 * (-0.004869 + 0.032649 * log10 (50)) = 7590.1 baris

SQL untuk mengujinya:

CREATE TABLE X_CE_LIKE_TEST_1 (
STRING VARCHAR(50)
);

CREATE STATISTICS X_STAT_CE_LIKE_TEST_1 ON X_CE_LIKE_TEST_1 (STRING) WITH NORECOMPUTE;

WITH
    L0 AS (SELECT 1 AS c UNION ALL SELECT 1),
    L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B),
    L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B),
    L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B),
    L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B CROSS JOIN L2 C),
    NUMS AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS NUM FROM L4)  
    INSERT INTO X_CE_LIKE_TEST_1 WITH (TABLOCK) (STRING)
    SELECT TOP (150000) 'ZZZZZ'
    FROM NUMS
    ORDER BY NUM;

DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM X_CE_LIKE_TEST_1
WHERE STRING LIKE @LastName;

SQL Server memberikan perkiraan jumlah baris 7242.47 yang merupakan jenis dekat.

Jika FLOOR (panjang kunci rata-rata)> = 1 maka formula yang berbeda digunakan yang didasarkan pada nilai FLOOR (panjang kunci rata-rata). Berikut adalah tabel dari beberapa nilai yang saya coba:

1    1.5%
2    1.5%
3    1.64792%
4    2.07944%
5    2.41416%
6    2.68744%
7    2.91887%
8    3.11916%
9    3.29584%
10   3.45388%

Jika FLOOR (panjang kunci rata-rata) <6 maka gunakan tabel di atas. Kalau tidak, gunakan persamaan berikut:

(estimasi baris) = (baris dalam tabel) * (-0.003381 + 0,034539 * log10 (LANTAI (panjang kunci rata-rata))))

Yang satu ini memiliki kecocokan yang lebih baik daripada yang lain, tetapi masih belum sepenuhnya akurat.

grafik rumus kedua

Sumbu x adalah panjang kunci rata-rata dan sumbu y adalah jumlah baris yang diperkirakan untuk sebuah tabel dengan 1 juta baris.

Untuk memberikan contoh lain, anggaplah Anda memiliki tabel dengan baris 10k dengan panjang kunci rata-rata 5,5 untuk statistik pada kolom yang difilter. Estimasi baris adalah:

10000 * 0,241416 = 241,416 baris.

SQL untuk mengujinya:

CREATE TABLE X_CE_LIKE_TEST_2 (
STRING VARCHAR(50)
);

WITH
    L0 AS (SELECT 1 AS c UNION ALL SELECT 1),
    L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B),
    L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B),
    L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B),
    L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B CROSS JOIN L2 C),
    NUMS AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS NUM FROM L4)  
    INSERT INTO X_CE_LIKE_TEST_2 WITH (TABLOCK) (STRING)
    SELECT TOP (10000) 
    CASE 
      WHEN NUM % 2 = 1 THEN REPLICATE('Z', 5) 
      ELSE REPLICATE('Z', 6)
    END
    FROM NUMS
    ORDER BY NUM;

CREATE STATISTICS X_STAT_CE_LIKE_TEST_2 ON X_CE_LIKE_TEST_2 (STRING) 
WITH NORECOMPUTE, FULLSCAN;

DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM X_CE_LIKE_TEST_2
WHERE STRING LIKE @LastName;

Estimasi baris adalah 241,416 yang cocok dengan yang Anda miliki dalam pertanyaan. Akan ada beberapa kesalahan jika saya menggunakan nilai yang tidak ada dalam tabel.

Model-model di sini tidak sempurna tetapi saya pikir mereka menggambarkan perilaku umum dengan cukup baik.

Joe Obbish
sumber