Część twojego początkowego zapytania jest następująca.
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)
Ta sekcja planu jest pokazana poniżej

Twoje poprawione zapytanie BETWEEN @dateStart AND ISNULL(@dateEnd,@dateStart)ma to samo dla tego samego sprzężenia

Wydaje się, że różnica polega na tym, że ISNULLupraszcza to dalej, w wyniku czego otrzymujesz dokładniejsze statystyki liczności przechodząc do następnego łączenia. Jest to funkcja ceniona w tabeli wbudowanej i wywoływana jest z wartościami dosłownymi, aby mogła zrobić coś takiego.
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'
A ponieważ istnieje predykat sprzężenia równań, b.[Date] = a.dplan pokazuje również predykat równości b.[Date] = '2013-06-01'. W rezultacie oszacowanie liczności 28,393wierszy prawdopodobnie będzie dość dokładne.
W przypadku wersji CASE/, COALESCEgdy @dateStarti @dateEndsą tej samej wartości, upraszcza to OK do tego samego wyrażenia równości i daje ten sam plan, ale kiedy @dateStart = '2013-06-01'i @dateEnd IS NULLidzie tylko tak daleko, jak
a.[d]>='2013-06-01' AND a.[Date]<=CASE WHEN (1) THEN '2013-06-01' ELSE NULL END
które stosuje się również jako dorozumiany wyrok ColleagueList. Szacowana liczba wierszy tym razem to 79.8rzędy.
Następnym dołączeniem jest
LEFT JOIN colleagueTime
ON colleagueTime.TC_DATE = colleagueList.Date
AND colleagueTime.ASSOC_ID = CAST(colleagueList.ID AS VARCHAR(10))
colleagueTimejest 3,249,590tabelą wierszy, która jest (ponownie) najwyraźniej stertą bez przydatnych indeksów.
Ta rozbieżność w szacunkach wpływa na zastosowany wybór łączenia. ISNULLPlan wybiera hash dołącz że właśnie przeszukuje tabelę raz. COALESCEPlan wybiera zagnieżdżone pętle sprzężenia i szacuje się, że będzie ona nadal wystarczy zeskanować tabelę raz i móc szpula wynik i powtórzyć go 78 razy. tj. szacuje, że skorelowane parametry nie zmienią się.
Z faktu, że plan zagnieżdżonych pętli nadal działał po dwóch godzinach, założenie o pojedynczym skanie colleagueTimewydaje się być wysoce niedokładne.
Co do tego, dlaczego szacunkowa liczba wierszy między dwoma złączeniami jest o wiele niższa, nie jestem pewien, nie widząc statystyk w tabelach. Jedynym sposobem, w jaki udało mi się przesunąć szacunkową liczbę wierszy tak dużo w moich testach, było dodanie obciążenia NULLwierszy (zmniejszyło to szacunkową liczbę wierszy, chociaż rzeczywista liczba zwróconych wierszy pozostała taka sama).
Szacowana liczba wierszy w COALESCEplanie z moimi danymi testowymi była rzędu
number of rows matching >= condition * 30% * (proportion of rows in the table not null)
Lub w SQL
SELECT 1E0 * COUNT([Date]) / COUNT(*) * ( COUNT(CASE
WHEN [Date] >= '2013-06-01' THEN 1
END) * 0.30 )
FROM [dbo].[colleagueList]
ale to nie odpowiada twojemu komentarzowi, że kolumna nie ma NULLwartości.