Parte della query iniziale è la seguente.
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)
Quella sezione del piano è mostrata di seguito
La tua query rivista BETWEEN @dateStart AND ISNULL(@dateEnd,@dateStart)
ha questo per lo stesso join
La differenza sembra essere quella che ISNULL
semplifica ulteriormente e di conseguenza si ottengono statistiche sulla cardinalità più accurate andando al join successivo. Questa è una funzione con valori di tabella incorporata e la stai chiamando con valori letterali in modo che possa fare qualcosa del genere.
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'
E poiché esiste un predicato equi join, b.[Date] = a.d
il piano mostra anche un predicato di uguaglianza b.[Date] = '2013-06-01'
. Di conseguenza, 28,393
è probabile che la stima della cardinalità delle righe sia piuttosto accurata.
Per la versione CASE
/ COALESCE
quando @dateStart
e @dateEnd
hanno lo stesso valore, allora semplifica OK con la stessa espressione di uguaglianza e fornisce lo stesso piano ma quando @dateStart = '2013-06-01'
e @dateEnd IS NULL
va solo fino a
a.[d]>='2013-06-01' AND a.[Date]<=CASE WHEN (1) THEN '2013-06-01' ELSE NULL END
che si applica anche come predicato implicito ColleagueList
. Il numero stimato di righe questa volta è 79.8
righe.
Il prossimo join è
LEFT JOIN colleagueTime
ON colleagueTime.TC_DATE = colleagueList.Date
AND colleagueTime.ASSOC_ID = CAST(colleagueList.ID AS VARCHAR(10))
colleagueTime
è una 3,249,590
tabella di righe che è (di nuovo) apparentemente un heap senza indici utili.
Questa discrepanza nelle stime influisce sulla scelta del join utilizzata. Il ISNULL
piano sceglie un hash join che scansiona il tavolo una sola volta. Il COALESCE
piano sceglie un'unione di cicli nidificati e stima che sarà ancora necessario scansionare la tabella una volta ed essere in grado di eseguire lo spooling del risultato e riprodurlo 78 volte. cioè stima che i parametri correlati non cambieranno.
Dal fatto che il piano di cicli annidati stava ancora andando avanti dopo due ore, questa ipotesi di una singola scansione contro colleagueTime
sembra essere altamente imprecisa.
Per quanto riguarda il motivo per cui il numero stimato di righe tra i due join è molto più basso, non sono sicuro senza poter vedere le statistiche sulle tabelle. L'unico modo in cui sono riuscito a distorcere i conteggi delle righe stimati così tanto nei miei test è stato l'aggiunta di un carico di NULL
righe (questo ha ridotto il conteggio delle righe stimato anche se il numero effettivo di righe restituite è rimasto lo stesso).
Il conteggio delle righe stimato nel COALESCE
piano con i miei dati di test era nell'ordine di
number of rows matching >= condition * 30% * (proportion of rows in the table not null)
O in SQL
SELECT 1E0 * COUNT([Date]) / COUNT(*) * ( COUNT(CASE
WHEN [Date] >= '2013-06-01' THEN 1
END) * 0.30 )
FROM [dbo].[colleagueList]
ma questo non quadra con il tuo commento che la colonna non ha NULL
valori.