DATEADD not producing a SARGable expectation of an index seek
The OR
condition evaluates at compile-time, rather than at runtime, which means that your WHERE
condition does not generate a seek.
And just to clean up the code, I refactored out your CONVERT
to make the code a bit more readable.
I would try changing the WHERE
clause to:
UA.[ActivityDate]>CONVERT(datetime2(0), (CASE
WHEN @DurationInterval IN ('year', 'yy', 'yyyy') THEN DATEADD(year, -@DurationIncrement, GETDATE())
WHEN @DurationInterval IN ('month', 'mm', 'm') THEN DATEADD(month, -@DurationIncrement, GETDATE())
WHEN ...
END))
I don't have access to an environment where I can verify this, but please let me know if it works out.
At compilation, SQL Server does not know the value of @DurationInterval
and so compiles the plan that is best-suited to retrieving the data for any possible scenario.
You can prove that out by adding a WITH (FORCESEEK)
option to the query, which shows an that, in order to do an Index Seek for the given query, there will be an individual seek for each OR
condition.
https://www.brentozar.com/pastetheplan/?id=HkE3lkuqf
The scan is determined to be a more optimal way of retrieving the data than 6 seeks.
@Daniel Hutmacher provides an optimal solution that performs a single Index Seek on IX_UserActivity_ActivityDate
. Alternatively, you could add an OPTION(RECOMPILE)
, although this would force recompilation every time the query is run, potentially causing more harm than good.
A "kitchen sink" query like that (multiple distinct filtering clauses one or more of which is used depending on the value of an input) is never going to be sargable even if all its individual clauses are.
The two quick options are to break them into individual procedures and call each as needed by a master procedure or use ad-hoc SQL.
For a detailed article describing a number of options for this type of query/procedure, see http://www.sommarskog.se/dyn-search.html