So I'm tuning this query, and I am pretty sure that in this instance, I can replace an inner join with a left join without affecting the data. However, I'm not entirely sure why this is faster. Here is the query:
SELECT DISTINCT cl.NAME AS company_name,
cl.id AS company_id,
ep.Plan__c AS plan_id,
ep.Employee__c,
ep.id,
do.Subcategory__c,
ep.Plan_Type__c,
pt.SubType,
Sum((pt.[shares] * fvh.[ValuePerShare])) AS TotalValue,
ppe.Deferral_Option__c,
dt.Defer_type_Code,
do.Short_Code__c,
pt.ContributionYear
FROM dbo.ParticipantTrades pt WITH (NOLOCK)
INNER JOIN dbo.PayoutPathElection ppe WITH (NOLOCK)
ON pt.payoutPathElectionID = ppe.Id
INNER JOIN dbo.DeferralOption do WITH (NOLOCK)
ON ppe.Deferral_Option__c = do.id
INNER JOIN dbo.EmployeePlan ep WITH (NOLOCK)
ON pt.employeePlan = ep.Id
LEFT JOIN dbo.DeferralType dt WITH (NOLOCK)
ON pt.deferralType = dt.defID
INNER JOIN dbo.Fnc_lastfundvalue('2019-01-30') AS fvh
ON pt.fund = fvh.Fund
INNER JOIN dbo.Clients cl with (NOLOCK)
ON ep.Company__c = cl.Id
WHERE ep.Company__c = '0017000001WL1HfAAL' AND ep.Plan_Type__c LIKE '%' AND pt.tradeDate <= '2019-01-30'
Group by cl.NAME,
cl.id,
ep.Plan__c,
ep.Employee__c,
ep.id,
do.Subcategory__c,
ep.Plan_Type__c,
pt.SubType,
ppe.Deferral_Option__c,
dt.Defer_type_Code,
do.Short_Code__c,
pt.ContributionYear
The bottleneck is at the table-value function join (Fnc_lastfundvalue). My hunch to why changing it to a left join is faster is that it can then reorder the joins and it causes less spillage into tempdb? Here is the query plan before and after changing INNER JOIN dbo.Fnc_lastfundvalue.. to LEFT JOIN dbo.Fnc_lastfundvalue..
Before (29 seconds): https://www.brentozar.com/pastetheplan/?id=Bk1Y-WqEE
After(3 seconds): https://www.brentozar.com/pastetheplan/?id=B1hoW-544
NB: Execution plans above are created on a dev box. The production server is still on SQL Server 2008.