Esiste documentazione o ricerca sulle modifiche in SQL Server 2016 su come viene stimata la cardinalità per predicati contenenti SUBSTRING () o altre funzioni stringa?
Il motivo per cui sto chiedendo è che stavo esaminando una query le cui prestazioni sono diminuite nella modalità di compatibilità 130 e il motivo è stato correlato a una modifica nella stima del numero di righe che corrispondono a una clausola WHERE che conteneva una chiamata a SUBSTRING (). Ho corretto il problema con una riscrittura delle query, ma mi chiedo se qualcuno è a conoscenza di documentazione sulle modifiche in quest'area in SQL Server 2016.
Il codice demo è sotto. Le stime sono molto vicine in questo caso di test, ma l'accuratezza varia a seconda dei dati.
Nel caso di test, nel livello 120 di compatibilità, SQL Server sembra utilizzare l'istogramma per la stima, mentre nel livello di compatibilità 130 SQL sembra assumere un 10% fisso delle corrispondenze di tabella.
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
stringhe sono solo codici e sempre in maiuscolo, puoi sempre provare a specificare un confronto binarioLatin1_General_100_BIN2
- che dovrebbe migliorare la velocità sulle operazioni di filtro. Basta aggiungereCOLLATE Latin1_General_100_BIN2
allaCREATE TABLE
dichiarazione, subito dopo ilvarchar(15)
. Sarei curioso di vedere se influiva anche sulla generazione / stima del piano.