J'ai créé une fonction qui accepte une date de début et de fin, la date de fin étant facultative. J'ai ensuite écrit un CASE
dans le filtre pour utiliser la date de début si aucune date de fin n'est passée.
CASE WHEN @dateEnd IS NULL
THEN @dateStart
ELSE @dateEnd
END
Lorsque j'appelle la fonction pour le mois le plus récent des données:
SELECT * FROM theFunction ('2013-06-01', NULL)
... la requête se bloque. Si je précise la date de fin:
SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
... le résultat est renvoyé normalement. J'ai retiré le code de la fonction et l'ai exécuté correctement dans une fenêtre de requête. Je ne peux pas non plus reproduire le problème du violon. Une requête comme:
SELECT * FROM theFunction ('2013-04-01', '2013-06-01')
... fonctionne aussi très bien.
Y a-t-il quelque chose dans la requête (ci-dessous) qui pourrait entraîner le blocage de la fonction lorsque a NULL
est passé pour la date de fin?
- Plan d'exécution pour
SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
- Plan prévisionnel pour
SELECT * FROM theFunction ('2013-06-01', NULL)
CASE
parCOALESCE(@dateEnd,@dateStart)
, le problème apparaît-il toujours?ISNULL()
?SELECT task_state FROM sys.dm_os_tasks WHERE session_id = x
montre-t-il? S'il passe beaucoup de temps hors de l'RUNNING
état, quels types d'attente cette session entre-sys.dm_os_waiting_tasks
t-elle?COALESCE
.ISNULL
l'a corrigé.Réponses:
Une partie de votre requête initiale est la suivante.
Cette section du plan est illustrée ci-dessous
Votre requête révisée
BETWEEN @dateStart AND ISNULL(@dateEnd,@dateStart)
a ceci pour la même jointureLa différence semble être de
ISNULL
simplifier davantage et, par conséquent, vous obtenez des statistiques de cardinalité plus précises lors de la prochaine jointure. Il s'agit d'une fonction de valeur de table en ligne et vous l'appelez avec des valeurs littérales afin qu'elle puisse faire quelque chose comme.Et comme il existe un prédicat de jointure équi,
b.[Date] = a.d
le plan affiche également un prédicat d'égalitéb.[Date] = '2013-06-01'
. Par conséquent, l'estimation de cardinalité des28,393
lignes est probablement assez précise.Pour la version
CASE
/COALESCE
quand@dateStart
et@dateEnd
ont la même valeur, cela simplifie OK à la même expression d'égalité et donne le même plan mais quand@dateStart = '2013-06-01'
et@dateEnd IS NULL
cela ne va que jusqu'àqu'il applique également comme prédicat implicite
ColleagueList
. Le nombre estimé de lignes cette fois est de79.8
lignes.La prochaine jointure est
colleagueTime
est une3,249,590
table de lignes qui est (encore) apparemment un tas sans index utiles.Cet écart dans les estimations affecte le choix de jointure utilisé. le
ISNULL
plan choisit une jointure de hachage qui scanne la table une seule fois. LeCOALESCE
plan choisit une jointure de boucles imbriquées et estime qu'il lui suffira de scanner une fois la table et de pouvoir spouler le résultat et le rejouer 78 fois. c'est-à-dire qu'il estime que les paramètres corrélés ne changeront pas.Du fait que le plan des boucles imbriquées était toujours en cours au bout de deux heures, cette hypothèse d'un scan unique contre
colleagueTime
semble très imprécise.Quant à savoir pourquoi le nombre estimé de lignes entre les deux jointures est tellement inférieur, je ne suis pas sûr sans pouvoir voir les statistiques sur les tables. La seule façon dont j'ai réussi à fausser le nombre de lignes estimé dans mes tests était d'ajouter une charge de
NULL
lignes (cela a réduit le nombre de lignes estimé même si le nombre réel de lignes retournées est resté le même).Le nombre de lignes estimé dans le
COALESCE
plan avec mes données de test était de l'ordre deOu en SQL
mais cela ne correspond pas à votre commentaire selon lequel la colonne n'a pas de
NULL
valeurs.la source
NULL
valeurs pour les dates dans aucun de ces tableaux.dbo
n'est pas répertorié. Juste d'autres schémas que je n'utilise pas.Il semble qu'il y ait eu un problème avec les types de données.
ISNULL
correction du problème (merci ypercube ). Après quelques recherches,COALESCE
est l'équivalent de laCASE
déclaration que j'utilisais:Paul White explique que:
Pour éviter tout problème de type de données, il semble que
ISNULL
c'est la fonction appropriée à utiliser pour traiter uniquement deux expressions.Extraits de plan XML
Le plan XML utilisant l'
CASE
expression 2 estNULL
:Plan XML utilisant
CASE
, l'expression 2 est une date:Le plan XML utilisant l'
ISNULL
expression 2 estNULL
:Plan XML utilisant
ISNULL
, l'expression 2 est une date:la source
SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
. Le type de données d'expression est toujours le même. Et les deux paramètres sont dedate
toute façon du type de données. Pouvez-vous consulter les plans d'exécution?NULL
.CASE
n'a également eu aucun effet, la requête se bloque toujours.ISNULL
plan semble simplifier mieux. Il a un prédicat d'égalité simple sur ColleagueList[Date]='2013-06-01'
alors queCASE
celui a un prédicat sur[Date]>='2013-06-01' AND [Date]<=CASE WHEN (1) THEN '2013-06-01' ELSE NULL END AND PROBE([Bitmap1067],[Date])
. Les lignes estimées sortant de cette jointure sont 28 393 pour laISNULL
version mais beaucoup plus faibles79.8
pour laCASE
version qui effectue le choix de la jointure plus tard dans le plan. Je ne sais pas pourquoi il y aurait un tel écart.