¿Existe alguna documentación o investigación sobre los cambios en SQL Server 2016 sobre cómo se estima la cardinalidad para los predicados que contienen SUBSTRING () u otras funciones de cadena?
La razón por la que pregunto es que estaba mirando una consulta cuyo rendimiento se degradó en el modo de compatibilidad 130 y la razón se relacionó con un cambio en la estimación del número de filas que coinciden con una cláusula WHERE que contenía una llamada a SUBSTRING (). Corrigí el problema con una reescritura de consultas, pero me pregunto si alguien conoce alguna documentación sobre cambios en esta área en SQL Server 2016.
El código de demostración está debajo. Las estimaciones son muy cercanas en este caso de prueba, pero la precisión varía según los datos.
En el caso de prueba, en el nivel de compatibilidad 120, SQL Server parece estar usando el histograma para la estimación, mientras que en el nivel de compatibilidad 130 SQL Server parece estar asumiendo que un 10% fijo de la tabla coincide.
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
cadenas son solo códigos y siempre mayúsculas, entonces siempre puede intentar especificar una intercalación binariaLatin1_General_100_BIN2
, lo que debería mejorar la velocidad en las operaciones de filtrado. Simplemente agregueCOLLATE Latin1_General_100_BIN2
a laCREATE TABLE
declaración, justo después devarchar(15)
. Me gustaría saber si también afectó la generación / estimación del plan.