COMME utilise l'index, CHARINDEX non?

22

Cette question est liée à ma vieille question . L'exécution de la requête ci-dessous prenait 10 à 15 secondes:

SELECT [customer].[Customer name],[customer].[Sl_No],[customer].[Id]
FROM [company].dbo.[customer]
WHERE (Charindex('123456789',CAST([company].dbo.[customer].[Phone no] AS VARCHAR(MAX)))>0) 

Dans certains articles, j'ai vu que l'utilisation CASTet CHARINDEXne bénéficiera pas de l'indexation. Certains articles indiquent également que l'utilisation LIKE '%abc%'ne bénéficiera pas de l'indexation, alors que LIKE 'abc%':

http://bytes.com/topic/sql-server/answers/81467-using-charindex-vs-like-where /programming/803783/sql-server-index-any-improvement-for -like-queries http://www.sqlservercentral.com/Forums/Topic186262-8-1.aspx#bm186568

Dans mon cas, je peux réécrire la requête comme suit:

SELECT [customer].[Customer name],[customer].[Sl_No],[customer].[Id]
FROM [company].dbo.[customer]
WHERE [company].dbo.[customer].[Phone no]  LIKE '%123456789%'

Cette requête donne la même sortie que la précédente. J'ai créé un index non cluster pour la colonne Phone no. Lorsque j'exécute cette requête, elle s'exécute en seulement 1 seconde . C'est un énorme changement par rapport aux 14 secondes précédentes.

Quels sont les LIKE '%123456789%'avantages de l'indexation?

Pourquoi les articles répertoriés indiquent-ils que cela n'améliorera pas les performances?

J'ai essayé de réécrire la requête à utiliser CHARINDEX, mais les performances sont toujours lentes. Pourquoi ne CHARINDEXbénéficie pas de l'indexation comme il semble que la LIKErequête en bénéficie ?

Requête en utilisant CHARINDEX:

SELECT [customer].[Customer name],[customer].[Sl_No],[customer].[Id]
 FROM [Company].dbo.[customer]
 WHERE ( Charindex('9000413237',[Company].dbo.[customer].[Phone no])>0 ) 

Plan d'exécution:

entrez la description de l'image ici

Requête en utilisant LIKE:

SELECT [customer].[Customer name],[customer].[Sl_No],[customer].[Id]
 FROM [Company].dbo.[customer]
 WHERE[Company].dbo.[customer].[Phone no] LIKE '%9000413237%'

Plan d'exécution:

Plan de requête LIKE

Chercheur informatique
la source

Réponses:

28

Comment LIKE '% 123456789%' bénéficie-t-il de l'indexation?

Rien qu'un peu. Le processeur de requêtes peut analyser l'intégralité de l'index non cluster à la recherche de correspondances au lieu de la table entière (l'index cluster). Les index non cluster sont généralement plus petits que la table sur laquelle ils sont construits, donc l'analyse de l'index non cluster peut être plus rapide.

L'inconvénient est que toutes les colonnes nécessaires à la requête qui ne sont pas incluses dans la définition d'index non cluster doivent être recherchées dans la table de base, par ligne.

L'optimiseur prend une décision entre l'analyse de la table (index cluster) et l'analyse de l'index non cluster avec des recherches, en fonction des estimations de coûts. Les coûts estimés dépendent dans une large mesure sur le nombre de lignes l'optimiseur attend votre LIKEou CHARINDEXprédicats pour sélectionner.

Pourquoi les articles répertoriés indiquent-ils que cela n'améliorera pas les performances?

Pour une LIKEcondition qui ne commence pas par un caractère générique, SQL Server peut effectuer une analyse partielle de l'index au lieu d'analyser le tout. Par exemple, LIKE 'A%peut être correctement évalué en testant uniquement les enregistrements d'index >= 'A'et < 'B'(les valeurs limites exactes dépendent du classement).

Ce type de requête peut utiliser la capacité de recherche des index b-tree: nous pouvons aller directement au premier enregistrement en >= 'A'utilisant le b-tree, puis parcourir en avant dans l'ordre des clés d'index jusqu'à atteindre un enregistrement qui échoue au < 'B'test. Comme nous n'avons besoin d'appliquer le LIKEtest qu'à un plus petit nombre de lignes, les performances sont généralement meilleures.

En revanche, LIKE '%Ane peut pas être transformé en une analyse partielle, car nous ne savons pas par où commencer ou se terminer; n'importe quel enregistrement pourrait se terminer 'A', nous ne pouvons donc pas améliorer l'analyse de l'index entier et tester chaque ligne individuellement.

J'ai essayé de réécrire la requête à utiliser CHARINDEX, mais les performances sont toujours lentes. Pourquoi ne CHARINDEXbénéficie pas de l'indexation comme il semble que la requête LIKE bénéficie?

L'optimiseur de requêtes a le même choix entre l'analyse de la table (index cluster) et l'analyse de l'index non cluster (avec des recherches) dans les deux cas.

Le choix se fait entre les deux en fonction de l' estimation des coûts . Il se trouve que SQL Server peut produire une estimation différente pour les deux méthodes. Pour la LIKEforme de la requête, l'estimation peut être en mesure d'utiliser des statistiques de chaîne spéciales pour produire une estimation raisonnablement précise. Le CHARINDEX > 0formulaire produit une estimation basée sur une supposition.

Les différentes estimations sont suffisantes pour que l'optimiseur choisisse une analyse d'index en cluster pour CHARINDEXet une analyse d'index non en cluster avec des recherches pour LIKE. Si vous forcez la CHARINDEXrequête à utiliser l'index non cluster avec un indice, vous obtiendrez le même plan que pour LIKE, et les performances seront à peu près les mêmes:

SELECT
    [Customer name],
    [Sl_No],
    [Id]
FROM dbo.customer WITH (INDEX (f))
WHERE 
    CHARINDEX('9000413237', [Phone no]) >0;

Le nombre de lignes traitées lors de l'exécution sera le même pour les deux méthodes, c'est juste que le LIKEformulaire produit une estimation plus précise dans ce cas, donc l'optimiseur de requête choisit un meilleur plan.

Si vous avez LIKE %thing%souvent besoin de recherches, vous voudrez peut-être envisager une technique que j'ai décrite dans Trigram Wildcard String Search dans SQL Server .

Paul White dit GoFundMonica
la source
16

SQL Server conserve des statistiques sur les sous-chaînes dans les colonnes de chaînes sous la forme d' essais utilisables par la LIKErequête mais pas par le CHARINDEX.

Consultez la section Statistiques récapitulatives des chaînes pour en savoir plus.

Quelques mises en garde importantes sont que tout échappement des caractères génériques doit être effectué avec la technique propriétaire de crochets plutôt que le ESCAPEmot - clé et que pour les chaînes de plus de 80 caractères, seuls les premier et 40 derniers caractères sont utilisés.

WHERE ( Charindex('9000413237',[Company].dbo.[customer].[Phone no])>0 ) 

utilisera simplement la supposition standard pour un prédicat d'inégalité selon lequel 30% des lignes seront renvoyées.

La LIKErequête (dans votre cas) estime probablement que beaucoup moins de lignes correspondront au prédicat.

Notez que le caractère générique de tête empêche toujours une recherche d'index. Un index entier est toujours analysé mais il en utilise un autre qui est plus étroit que l'index clusterisé. L'index plus étroit ne couvre pas toutes les colonnes utilisées par la requête, le deuxième plan nécessite donc une recherche de clé pour récupérer les colonnes manquantes.

Il est extrêmement peu probable que ce plan soit choisi avec l'estimation de 30%. SQL Server considérera qu'il est moins coûteux d'analyser l'intégralité de l'index cluster et d'éviter autant de recherches. Voir cet article sur le point de basculement pour des exemples supplémentaires.

Martin Smith
la source
je ne suis pas clair avec votre explication. Voulez-vous dire que l'utilisation de like est meilleure que charindex?
Chercheur informatique
3
@ITresearcher - Oui, potentiellement, au lieu d'utiliser simplement une estimation générale du nombre de lignes qui correspondra à la condition ( 30%), il peut examiner le LIKEmodèle fourni et les statistiques récapitulatives des chaînes et obtenir une estimation plus précise. Armé de cela, il pourrait choisir un plan différent et plus approprié.
Martin Smith
3
... ou, dans le "pire des cas", le même plan.
Aaron Bertrand