Pourquoi la variable de table force-t-elle une analyse d'index alors que la table temporaire utilise la recherche et la recherche de signets ?

dba.stackexchange https://dba.stackexchange.com/questions/108352

Question

J'essaie de comprendre pourquoi l'utilisation d'une variable de table empêche l'optimiseur d'utiliser une recherche d'index, puis une recherche de signets par rapport à une analyse d'index.

Remplir le tableau :

CREATE TABLE dbo.Test 
(
    RowKey INT NOT NULL PRIMARY KEY, 
    SecondColumn CHAR(1) NOT NULL DEFAULT 'x',
    ForeignKey INT NOT NULL 
) 

INSERT dbo.Test 
(
    RowKey, 
    ForeignKey
) 
SELECT TOP 1000000 
    ROW_NUMBER() OVER (ORDER BY (SELECT 0)),
    ABS(CHECKSUM(NEWID()) % 10)     
FROM sys.all_objects s1
CROSS JOIN sys.all_objects s2 

CREATE INDEX ix_Test_1 ON dbo.Test (ForeignKey) 

Remplissez une variable de table avec un seul enregistrement et essayez de rechercher la clé primaire et la deuxième colonne en recherchant sur la colonne de clé étrangère :

DECLARE @Keys TABLE (RowKey INT NOT NULL) 

INSERT @Keys (RowKey) VALUES (10)

SELECT 
    t.RowKey,
    t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    @Keys k
ON
    t.ForeignKey = k.RowKey

Ci-dessous le plan d'exécution :

enter image description here

Maintenant, la même requête utilisant une table temporaire :

CREATE TABLE #Keys (RowKey INT NOT NULL) 

INSERT #Keys (RowKey) VALUES (10) 

SELECT 
    t.RowKey,
    t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    #Keys k
ON
    t.ForeignKey = k.RowKey

Ce plan de requête utilise une recherche et une recherche de favoris :

enter image description here

Pourquoi l'optimiseur souhaite-t-il effectuer la recherche de signets avec la table temporaire, mais pas avec la variable de table ?

La variable table est utilisée dans cet exemple pour représenter les données provenant d'un type de table défini par l'utilisateur dans une procédure stockée.

Je me rends compte que la recherche d'index pourrait ne pas être appropriée si la valeur de la clé étrangère se produisait des centaines de milliers de fois.Dans ce cas, une analyse serait probablement un meilleur choix.Pour le scénario que j'ai créé, il n'y avait aucune ligne avec une valeur de 10.Je pense toujours que le comportement est intéressant et j'aimerais savoir s'il y a une raison à cela.

Violon SQL

Ajouter OPTION (RECOMPILE) n'a pas changé le comportement.L'UDDT possède une clé primaire.

@@VERSION est SQL Server 2008 R2 (SP2) - 10.50.4042.0 (X64) (Build 7601 :Service Pack 1) (Hyperviseur)

Était-ce utile?

La solution

La raison de ce comportement est que SQL Server ne peut pas déterminer combien de lignes correspondront à ForeignKey, car il n'y a pas d'index avec RowKey comme première colonne (il peut le déduire des statistiques de la table #temp, mais celles-ci ne le font pas). existent pour les variables de table/UDTT), cela donne donc une estimation de 100 000 lignes, ce qui est mieux géré avec une analyse qu'avec une recherche + recherche.Au moment où SQL Server se rend compte qu'il n'y a qu'une seule ligne, il est trop tard.

Vous pourrez peut-être construire votre UDTT différemment ;dans les versions plus modernes de SQL Server, vous pouvez créer des index secondaires sur des variables de table, mais cette syntaxe n'est pas disponible dans 2008 R2.

BTW, vous pouvez obtenir le comportement de recherche (au moins dans mes essais limités) si vous essayez d'éviter le bitmap/sonde en faisant allusion à une jointure de boucles imbriquées :

DECLARE @Keys TABLE (RowKey INT PRIMARY KEY); -- can't hurt

INSERT @Keys (RowKey) VALUES (10);

SELECT 
     t.RowKey
    ,t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    @Keys k
ON
    t.ForeignKey = k.RowKey
    OPTION (LOOP JOIN);

je J'ai appris cette astuce de Paul White il y a plusieurs années.Bien sûr, vous devez faire attention à l'insertion de tout type d'indication de jointure dans le code de production - cela peut échouer si des personnes apportent des modifications aux objets sous-jacents et que ce type de jointure spécifique n'est plus possible ou n'est plus optimal.

Pour les requêtes plus complexes, et lorsque vous passez à SQL Server 2012 ou supérieur, il est possible que tracer le drapeau 2453 pourrait aider.Ce drapeau n'a cependant pas aidé avec cette simple jointure.Et les mêmes avertissements s'appliqueraient - il s'agit simplement d'une chose alternative que vous ne devriez généralement pas faire sans une tonne de documentation et des procédures rigoureuses de tests de régression en place.

De plus, le Service Pack 1 n'est plus pris en charge depuis longtemps, vous devriez continuer ServicePack3 + MS15-058.

Autres conseils

Les variables de table et les tables temporaires sont gérées différemment de plusieurs manières.Il y a un excellente réponse ici avec beaucoup de détails sur leurs différences.

Plus précisément, dans votre cas, je suppose que le fait que les tables temporaires puissent avoir des statistiques supplémentaires générées et des plans parallèles alors que les variables de table ont des statistiques plus limitées (pas de statistiques au niveau des colonnes) et aucun plan parallèle est votre coupable.

Vous feriez peut-être très bien de vider la variable de table dans une table temporaire pendant la durée de la procédure stockée.

Licencié sous: CC-BY-SA avec attribution
Non affilié à dba.stackexchange
scroll top