Existe-t-il de la documentation ou des recherches sur les modifications dans SQL Server 2016 concernant la façon dont la cardinalité est estimée pour les prédicats contenant SUBSTRING () ou d'autres fonctions de chaîne?
La raison pour laquelle je demande, c'est que je regardais une requête dont les performances se sont dégradées en mode de compatibilité 130 et la raison était liée à un changement d'estimation du nombre de lignes correspondant à une clause WHERE qui contenait un appel à SUBSTRING (). J'ai corrigé le problème avec une réécriture de requête, mais je me demande si quelqu'un est au courant de la documentation sur les modifications dans ce domaine dans SQL Server 2016.
Le code de démonstration est ci-dessous. Les estimations sont très proches dans ce cas de test, mais la précision varie en fonction des données.
Dans le cas de test, dans le niveau de compatibilité 120, SQL Server semble utiliser l'histogramme pour l'estimation, tandis que dans le niveau de compatibilité 130, SQL Server semble supposer que 10% des correspondances de table sont fixes.
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
chaînes ne sont que des codes et toujours en majuscules, vous pouvez toujours essayer de spécifier un classement binaire -Latin1_General_100_BIN2
- ce qui devrait améliorer la vitesse des opérations de filtrage. Ajoutez simplementCOLLATE Latin1_General_100_BIN2
à laCREATE TABLE
déclaration, juste après levarchar(15)
. Je serais curieux de voir si cela affectait également la génération / estimation du plan.