Take a look at this fiddle: http://sqlfiddle.com/#!6/18324/2
Expand the very first execution plan, for the queries against view B
.
Notice that the first query executes using index seek, while the second one - using index scan. In my real setup, with thousands of rows, this produces a performance hit that is quite considerable.
WTF???
The queries are equivalent, aren't they? Why does a literal produce seek and a variable - scan?
But more importantly: how can I work around this?
This post comes closest to the problem, and the solution that works from there is using option(recompile)
(thank you, Martin Smith). However, that does not work for me, because my queries are being generated by my ORM library (which is Entity Framework) and I cannot amend them manually.
Rather what I'm looking for is a way to reformulate the B
view so that the problem would not occur.
While fiddling with this problem, I have noticed that it is always the "Segment" block in the execution plan that loses the predicate. To verify this, I reformulated the query in terms of a subquery with min
function (see view D
). And voila! - both queries against the D
view produce identical plans.
The bad news, however, is that I cannot use this min
-powered trick, because in my real setup, the column Y
is actually several columns, so that I can order by them, but I cannot take a min()
of them.
So the second question would be: can anyone come up with a trick that is similar to min-powered subquery, but works for several columns?
NOTE 1: this is definitely not related to the tipping point, because there are just 2 records in the table.
NOTE 2: it also doesn't have to do with the presence of a view. See an example with view C
: the server is happily using seek in that case.