Une partie de votre requête initiale est la suivante.
FROM [dbo].[calendar] a
LEFT JOIN [dbo].[colleagueList] b
ON b.[Date] = a.d
WHERE DAY(a.[d]) = 1
AND a.[d] BETWEEN @dateStart AND COALESCE(@dateEnd,@dateStart)
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 jointure
La 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.
a.[d] BETWEEN @dateStart AND ISNULL(@dateEnd,@dateStart)
a.[d] BETWEEN '2013-06-01' AND ISNULL(NULL,'2013-06-01')
a.[d] BETWEEN '2013-06-01' AND '2013-06-01'
a.[d] = '2013-06-01'
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é des 28,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'à
a.[d]>='2013-06-01' AND a.[Date]<=CASE WHEN (1) THEN '2013-06-01' ELSE NULL END
qu'il applique également comme prédicat implicite ColleagueList
. Le nombre estimé de lignes cette fois est de 79.8
lignes.
La prochaine jointure est
LEFT JOIN colleagueTime
ON colleagueTime.TC_DATE = colleagueList.Date
AND colleagueTime.ASSOC_ID = CAST(colleagueList.ID AS VARCHAR(10))
colleagueTime
est une 3,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é. leISNULL
plan choisit une jointure de hachage qui scanne la table une seule fois. Le COALESCE
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 deNULL
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 de
number of rows matching >= condition * 30% * (proportion of rows in the table not null)
Ou en SQL
SELECT 1E0 * COUNT([Date]) / COUNT(*) * ( COUNT(CASE
WHEN [Date] >= '2013-06-01' THEN 1
END) * 0.30 )
FROM [dbo].[colleagueList]
mais cela ne correspond pas à votre commentaire selon lequel la colonne n'a pas de NULL
valeurs.