Apakah ada dokumentasi atau penelitian tentang perubahan dalam SQL Server 2016 untuk bagaimana kardinalitas diperkirakan untuk predikat yang berisi SUBSTRING () atau fungsi string lainnya?
Alasan saya bertanya adalah karena saya melihat kueri yang kinerjanya menurun dalam mode kompatibilitas 130 dan alasannya terkait perubahan dalam estimasi jumlah baris yang cocok dengan klausa WHERE yang berisi panggilan ke SUBSTRING (). Saya memperbaiki masalah dengan penulisan ulang kueri, tetapi saya bertanya-tanya apakah ada yang mengetahui dokumentasi tentang perubahan di area ini di SQL Server 2016.
Kode demo di bawah. Perkiraan sangat dekat dalam kasus uji ini, tetapi akurasi bervariasi tergantung pada data.
Dalam kasus pengujian, di level compat 120, SQL Server tampaknya menggunakan histogram untuk estimasi, sedangkan di level compat 130 SQL Server tampaknya mengasumsikan 10% tetap dari tabel cocok.
CREATE DATABASE MyStringTestDB;
GO
USE MyStringTestDB;
GO
DROP TABLE IF EXISTS dbo.StringTest;
CREATE TABLE dbo.StringTest ( [TheString] varchar(15) );
GO
INSERT INTO dbo.StringTest
VALUES
( 'Y5_CLV' );
INSERT INTO dbo.StringTest
VALUES
( 'Y5_EG3' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_NE' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_PQT' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_T2V' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_TT4' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_ZKK' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_LW6' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_QO3' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_TZ7' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_UZZ' );
CREATE CLUSTERED INDEX IX_Clustered ON dbo.StringTest (TheString);
/*
Uses fixed % for estimate; 1.1 rows estimated in this case.
Plan for computation:
CSelCalcFixedFilter (0.1) <----
Selectivity: 0.1
*/
ALTER DATABASE MyStringTestDB SET compatibility_level = 130;
GO
SELECT *
FROM dbo.StringTest
WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);
/*
Uses histogram to get estimate of 1
CSelCalcPointPredsFreqBased <----
Distinct value calculation:
CDVCPlanLeaf
0 Multi-Column Stats, 1 Single-Column Stats, 0 Guesses
Individual selectivity calculations:
(none)
Loaded histogram for column QCOL: [DBA].[dbo].[StringTest].TheString from stats with id 1
*/
ALTER DATABASE MyStringTestDB SET compatibility_level = 120;
GO
SELECT *
FROM dbo.StringTest
WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);
/*
-- Simpler rewrite; works fine in both compat levels and gets better estimate.
SELECT *
FROM dbo.StringTest
WHERE TheString LIKE 'ZZ[_]%'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);
*/
Y5_EG3
string hanya kode dan huruf besar, maka Anda selalu dapat mencoba menentukan susunan biner -Latin1_General_100_BIN2
- yang seharusnya meningkatkan kecepatan pada operasi penyaringan. Cukup tambahkanCOLLATE Latin1_General_100_BIN2
keCREATE TABLE
pernyataan, tepat setelahvarchar(15)
. Saya akan penasaran untuk melihat apakah itu juga mempengaruhi pembuatan / estimasi rencana.Jawaban:
Saya tidak mengetahui adanya dokumentasi. Saya memang melihat ini dan melakukan beberapa pengamatan namun itu terlalu lama untuk komentar.
Estimasi 10% tidak selalu merupakan degradasi. Ambil contoh berikut.
dan
WHERE
klausa dalam pertanyaan Anda.Tabel berisi sejuta baris. Semuanya sesuai dengan predikat. Di bawah level compat 130, tebakan 10% menghasilkan perkiraan 100.000. Di bawah 120 baris yang diperkirakan adalah 1.03913.
Perilaku 120 menggunakan histogram tetapi hanya untuk mendapatkan jumlah baris yang berbeda. Vektor kepadatan dalam kasus saya menunjukkan 1.039131E-06 dan ini dikalikan dengan kardinalitas tabel untuk mendapatkan perkiraan jumlah baris. Semua nilai sebenarnya berbeda tetapi semua sesuai dengan predikat.
Menelusuri
query_optimizer_estimate_cardinality
acara yang diperluas menunjukkan bahwa di bawah 130 ada dua<StatsCollection Name="CStCollFilter"
peristiwa yang berbeda . Yang pertama memperkirakan 100.000. Yang kedua memuat histogram dan menggunakan CSelCalcPointPredsFreqBased / DistinctCountCalculator untuk mendapatkan perkiraan 1,04. Hasil kedua ini tampaknya tidak digunakan.Perilaku yang Anda amati tidak secara konsisten diterapkan di 130. Saya menambahkan
ORDER BY TheString
berharap ini menjadi kemenangan yang jelas bagi penaksir 130 karena 120 berjuang dengan pemberian memori untuk satu baris tetapi perubahan kecil ini cukup untuk membawa baris yang diperkirakan ke 1.03913 dalam 130 kasus juga.Menambahkan
OPTION (QUERYRULEOFF SelectToFilter)
mengembalikan estimasi yang masuk ke dalam sortir menjadi 100.000 tetapi hibah memori tidak meningkat dan perkiraan yang keluar semacam itu masih berdasarkan tabel nilai yang berbeda.Demikian pula mengubah ambang biaya untuk paralelisme sehingga permintaan mendapatkan rencana paralel cukup dalam kasus 130 untuk kembali ke perkiraan yang lebih rendah. Menambahkan
QUERYTRACEON 8757
juga menyebabkan perkiraan yang lebih rendah. Sepertinya perkiraan 10% hanya dipertahankan untuk rencana sepele.Usulan penulisan ulang dengan
Memperlihatkan jauh lebih banyak perkiraan untuk keduanya. Output untuk ini adalah
Menunjukkan bahwa itu digunakan mencoba . Info lebih lanjut tentang ini ada di bagian statistik ringkasan string tepat di atas sini .
Namun tidak sama dengan permintaan awal Anda. Sebagai contoh pertama
_
sekarang diasumsikan selalu menjadi karakter ketiga daripada ditemukan secara dinamis.Jika asumsi ini dimasukkan ke dalam kueri asli Anda
Metode estimasi berubah menjadi
CSelCalcHistogramComparison(INTERVAL)
dan baris estimasi menjadi akurat.Itu dapat mengkonversi itu menjadi rentang
dan gunakan histogram untuk memperkirakan jumlah baris dengan nilai dalam rentang itu.
Namun ini hanya berlaku untuk estimasi kardinalitas.
LIKE
lebih disukai karena dapat menggunakan rentang pencarian saat runtime.SUBSTRING(TheString, 1, 3)
atauLEFT(TheString, 3)
tidak bisa.sumber