Estimation de la cardinalité SARG, pourquoi pas un scan complet?

11

Pourquoi n'y a-t-il pas d'analyse complète (sur SQL 2008 R2 et 2012)?

Données de test:

DROP TABLE dbo.TestTable
GO  
CREATE TABLE dbo.TestTable
(
   TestTableID INT IDENTITY PRIMARY KEY,
   VeryRandomText VarChar(50),
   VeryRandomText2 VarChar(50)
)
Go
Set NoCount ON
Declare @i int
Set @i = 0
While @i < 10000
Begin
   Insert Into dbo.TestTable(VeryRandomText, VeryRandomText2)
      Values(Cast(Rand()*10000000 as VarChar(50)), Cast(Rand()*10000000 as VarChar(50)));
   Set @i = @i + 1;
End
Go
CREATE Index IX_VeryRandomText On dbo.TestTable
(
    VeryRandomText
)
Go

Lors de l'exécution de la requête:

Select * From dbo.TestTable Where VeryRandomText = N'111' -- bad

Obtenez un avertissement (comme prévu, car la comparaison des données nchar à la colonne varchar):

<PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(nvarchar(50),[DemoDatabase].[dbo].[TestTable].[VeryRandomText],0)" />

Mais alors je vois le plan d'exécution, et je peux voir, qu'il n'utilise pas le scan complet comme je m'y attendais, mais la recherche d'index à la place.

entrez la description de l'image ici

Bien sûr, c'est plutôt bien, car dans ce cas particulier, l'exécution est beaucoup plus rapide que s'il y avait une analyse complète.

Mais je ne peux pas comprendre comment SQL Server a pris la décision de faire ce plan.

De même, si le classement du serveur est constitué de classements Windows au niveau du serveur et au niveau de la base de données de classement SQL Server, cela entraînera une analyse complète de la même requête.

Jānis
la source

Réponses:

8

Lors de la comparaison des valeurs de différents types de données, SQL Server suit les règles de priorité des types de données . Étant donné que nvarchar a une priorité plus élevée que varchar, SQL Server doit convertir les données de colonne en nvarchar avant de comparer les valeurs. Cela signifie appliquer une fonction sur la colonne et cela rendrait la requête non-sargable.

SQL Server fait cependant de son mieux pour vous protéger de vos erreurs.Il utilise donc une technique décrite par Paul White dans le billet de blog Dynamic Seeks and Hidden Implicit Conversions pour rechercher une plage de valeurs, puis faire la comparaison finale, avec le conversion de la valeur de la colonne en nvarchar, dans un prédicat résiduel pour filtrer les faux positifs.

Comme vous l'avez noté, cela ne fonctionne cependant pas lorsque le classement de la colonne est un classement SQL. La raison pour cela, je crois, peut être trouvée dans l'article Comparaison des classements SQL aux classements Windows

Fondamentalement, un classement Windows utilise le même algorithme pour varchar et nvarchar où un classement SQL utilise un algorithme différent pour les données varchar et le même algorithme qu'un classement Windows pour les données nvarchar.

Ainsi, passer de varchar à nvarchar sous un classement Windows utilisera le même algorithme et SQL Server peut produire une plage de valeurs à partir, dans votre cas, d'un littéral nvarchar pour obtenir des lignes à partir de l'index de la colonne de classement SQL varchar. Cependant, lorsque le classement de la colonne varchar est un classement SQL qui n'est pas possible en raison des différents algorithmes utilisés.


Mise à jour:

Une démonstration des différents ordres de tri pour les colonnes varchar en utilisant le classement windows et sql.

SQL Fiddle

Configuration du schéma MS SQL Server 2014 :

create table T(C varchar(10));

insert into T values('a-b'),('aa'),('ac');

Requête 1 :

select C
from T
order by C collate SQL_Latin1_General_CP1_CI_AS;

Résultats :

|   C |
|-----|
| a-b |
|  aa |
|  ac |

Requête 2 :

select C
from T
order by C collate Latin1_General_100_CI_AS;

Résultats :

|   C |
|-----|
|  aa |
| a-b |
|  ac |
Mikael Eriksson
la source
0

Vous devez vous rappeler que les nœuds terminaux d'un index non cluster sont constitués de pages d'index qui contiennent une clé de cluster ou RID pour localiser la ligne de données.

Dans votre clause where, vous indiquez VeryRandomText = N'111'Puisqu'il existe un index Non clustered sur VeryRandomText (create index créera un index non clustered sauf si vous lui demandez explicitement de créer un clustered) la façon la moins chère de trouver les données est de scanner l'index pour trouver le rowid et puis récupérez les données de la ligne.

Si vous souhaitez créer un index clusterisé

CREATE clustered Index IX_VeryRandomText On dbo.TestTable (VeryRandomText)

ou une clé primaire sur VeryRandomText, vous obtiendrez une analyse de cet index.

Voir les livres en ligne ou ici: http://www.sqlforge.com/w/Clustered_index,_nonclustered_index,_or_heap

Spörri
la source
Oui, je sais ce que vous écrivez. Comme vous pouvez le voir, il existe déjà un index cluster sur TestTableID. Mais le fait est que si le serveur SQL ne peut pas voir les statistiques de distribution des données de colonne (comme dans ce cas, en raison de l'inadéquation du type de données qui devrait nécessiter la conversion de tous les types de données de valeur de ligne), il doit choisir l'analyse d'index clusterisé dans ce cas, pas la recherche d'index .
Jānis
Et il n'est pas toujours moins cher de rechercher / scanner un index non clusterisé - lorsque les valeurs ne sont pas suffisamment distinctes ou ne couvrent pas l'index, il peut être moins cher de faire un scan d'index clusterisé à la place.
Jānis
@ Jānis n'accédant pas à votre script, créer un index ne créera pas un index clusterisé, vous devez le dire explicitement - de même si vous lisez le plan de requête, la recherche d'index (non cluster)
Spörri
"Lorsque vous créez une contrainte PRIMARY KEY, un index cluster unique sur la ou les colonnes est automatiquement créé si un index cluster sur la table n'existe pas déjà et que vous ne spécifiez pas un index non cluster unique." msdn.microsoft.com/en-us/library/ms186342.aspx
Jānis