Je remarque que lorsqu'il y a un déversement d'événements tempdb (provoquant des requêtes lentes), les estimations de ligne sont souvent éloignées pour une jointure particulière. J'ai vu des événements de déversement se produire avec des jointures de fusion et de hachage et ils augmentent souvent la durée d'exécution de 3 à 10 fois. Cette question concerne la façon d'améliorer les estimations des rangées en supposant que cela réduira les risques d'événements de déversement.
Nombre réel de lignes 40k.
Pour cette requête, le plan affiche une estimation de ligne incorrecte (11,3 lignes):
select Value
from Oav.ValueArray
where ObjectId = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
Pour cette requête, le plan affiche une bonne estimation de ligne (56 000 lignes):
declare @a bigint = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2);
select Value
from Oav.ValueArray
where ObjectId = @a
and PropertyId = 2840
option (recompile);
Des statistiques ou des conseils peuvent-ils être ajoutés pour améliorer les estimations de ligne pour le premier cas? J'ai essayé d'ajouter des statistiques avec des valeurs de filtre particulières (propriété = 2840) mais soit je n'ai pas pu obtenir la combinaison correcte, soit elle est peut-être ignorée car l'ObjectId est inconnu au moment de la compilation et il se peut qu'il choisisse une moyenne parmi tous les ObjectIds.
Existe-t-il un mode dans lequel il ferait d'abord la requête de sonde, puis l'utiliserait pour déterminer les estimations de ligne ou doit-il voler à l'aveugle?
Cette propriété particulière a de nombreuses valeurs (40k) sur quelques objets et zéro sur la grande majorité. Je serais heureux d'avoir une indication où le nombre maximum de lignes attendues pour une jointure donnée pourrait être spécifié. Il s'agit d'un problème généralement obsédant car certains paramètres peuvent être déterminés dynamiquement dans le cadre de la jointure ou seraient mieux placés dans une vue (pas de prise en charge des variables).
Y a-t-il des paramètres qui peuvent être ajustés pour minimiser les risques de déversements sur tempdb (par exemple, minimum de mémoire par requête)? Un plan robuste n'a eu aucun effet sur l'estimation.
Edit 2013.11.06 : Réponse aux commentaires et informations supplémentaires:
Voici les images du plan de requête. Les avertissements concernent le prédicat de cardinalité / recherche avec convert ():
Selon le commentaire de @Aaron Bertrand, j'ai essayé de remplacer convert () comme test:
create table Oav.SeekObject (
LookupId bigint not null primary key,
ObjectId bigint not null
);
insert into Oav.SeekObject (
LookupId, ObjectId
) VALUES (
1, 3540233
)
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.SeekObject
where LookupId = 1)
and PropertyId = 2840
option (recompile);
En tant que point d'intérêt étrange mais réussi, cela lui a également permis de court-circuiter la recherche:
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.ValueArray
where PropertyId = 2840
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
Les deux répertorient une recherche de clé appropriée, mais seules les premières répertorient une "sortie" d'ObjectId. Je suppose que cela indique que le second est en effet un court-circuit?
Quelqu'un peut-il vérifier si des sondes sur une seule ligne ont déjà été effectuées pour aider à l'estimation des lignes? Il semble erroné de limiter l'optimisation aux seules estimations d'histogramme lorsqu'une recherche PK à une seule ligne peut améliorer considérablement la précision de la recherche dans l'histogramme (surtout s'il y a un potentiel de déversement ou un historique). Lorsqu'il y a 10 de ces sous-jointures dans une requête réelle, idéalement, elles se produiraient en parallèle.
Une note latérale, puisque sql_variant stocke son type de base (SQL_VARIANT_PROPERTY = BaseType) dans le champ lui-même, je m'attendrais à ce qu'un convert () soit presque sans coût tant qu'il est "directement" convertible (par exemple, pas de chaîne en décimale mais plutôt d'int int ou peut-être int à bigint). Comme cela n'est pas connu au moment de la compilation mais peut être connu de l'utilisateur, peut-être qu'une fonction "AssumeType (type, ...)" pour sql_variants permettrait de les traiter de manière plus transparente.
declare @a bigint =
que vous avez fait me semble une solution naturelle, pourquoi est-ce inacceptable?
CONVERT()
colonnes, puis à les joindre. Ce n'est certainement pas efficace dans la plupart des cas. Dans celui-ci, ce n'est qu'une seule valeur à convertir, ce n'est probablement pas un problème, mais quels index avez-vous sur la table? Les conceptions EAV fonctionnent généralement bien, uniquement avec une indexation appropriée (ce qui signifie beaucoup d'index dans les tableaux généralement étroits).