web-dev-qa-db-fra.com

COMME utilise l'index, CHARINDEX non?

Cette question est liée à mon ancienne 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 de CAST et CHARINDEX ne bénéficierait pas de l'indexation. Il y a aussi des articles qui disent que l'utilisation de LIKE '%abc%' ne bénéficiera pas de l'indexation tant que LIKE 'abc%' volonté:

http://bytes.com/topic/sql-server/answers/81467-using-charindex-vs-like-wherehttps://stackoverflow.com/questions/803783/ sql-server-index-any-amélioration-for-like-querieshttp://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 . Il s'agit d'un énorme changement par rapport à 14 secondes précédemment.

Comment LIKE '%123456789%' bénéficier 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 pour utiliser CHARINDEX, mais les performances sont toujours lentes. Pourquoi CHARINDEX ne bénéficie-t-il pas de l'indexation comme il semble que la requête LIKE en bénéficie?

Requête à l'aide de 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:

enter image description here

Requête à l'aide de 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:

LIKE query plan

22
IT researcher

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

Rien qu'un peu. Le processeur de requêtes peut analyser l'ensemble 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 du nombre de lignes que l'optimiseur attend votre prédicat LIKE ou CHARINDEX pour sélectionner .

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

Pour une condition LIKE qui ne démarre pas avec un caractère générique, SQL Server peut effectuer un partiel scan de l'index au lieu de scanner 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 >= 'A' En utilisant le b-tree, puis parcourir en avant dans l'ordre des clés d'index jusqu'à atteindre un enregistrement qui échoue < 'B' Test. Comme nous n'avons besoin d'appliquer le test LIKE qu'à un plus petit nombre de lignes, les performances sont généralement meilleures.

En revanche, LIKE '%A Ne peut pas être transformé en analyse partielle car nous ne savons ni par où commencer ni par terminer; tout enregistrement peut se terminer par '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 pour utiliser CHARINDEX, mais les performances sont toujours lentes. Pourquoi CHARINDEX ne bénéficie pas de l'indexation car il semble que la requête LIKE le fasse?

L'optimiseur de requête 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 forme LIKE 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 formulaire CHARINDEX > 0 Produit une estimation basée sur une supposition.

Les différentes estimations sont suffisantes pour que l'optimiseur choisisse une analyse d'index cluster pour CHARINDEX et une analyse d'index non cluster avec des recherches pour LIKE. Si vous forcez la requête CHARINDEX à 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 formulaire LIKE produit une estimation plus précise dans ce cas, donc l'optimiseur de requête choisit un meilleur plan.

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

28
Paul White 9

SQL Server conserve des statistiques sur les sous-chaînes dans les colonnes de chaîne sous la forme essais utilisables par la requête LIKE mais pas par CHARINDEX.

Voir la section String Summary Statistics 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 mot clé ESCAPE 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 requête LIKE (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 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.

16
Martin Smith