I have this plan that took ~ 18 seconds to run (generated by SentryOne plan explorer):
The Index-seek
used a non-clustered index, with some seek predicates. While the Filter
operator has some other predicates including the LIKE
condition.
This is the query (kind of):
SELECT TOP(20) *
FROM Table
WHERE Table.Col1 = ''
AND Table.Col2 LIKE '%' + @searchString + '%'
ORDER BY Table.Col3
1st (unrelated) question:
How do you read an execution plan?
As I read it, "SQL" begins with the leftmost operator Top
, then it'll call its child operator (node) on the right to get 1 single row and keep going until it meets its target (20 rows). If there is no Top
, then it'll keep going until its child node saying there are no more left. And the child node will call the child node's child node. So the Filter
has called the Index-seek
2.9 million times to fetch 2.9 million rows in the above plan. I hope I'm correct?
Then I made a small change to the query:
SELECT TOP(20) *
FROM Table
WHERE Col1 = ''
AND Col2 LIKE @searchString + '%' -- search for string prefix only
ORDER BY Table.Col3
This time the query took < 2 seconds to run. The actual execution is exactly the same as the above. I compared the 2 inside SSMS. Index-seek
still the most costly operator, but it returned the same number of rows (2.9 million).
2nd question:
The change I made with the LIKE
condition must have reduced 16s of execution time. So why does Index-seek
is marked as the most costly? Shouldn't it be Filter
because it's the one that handle the LIKE
search right? If I didn't make this experiment, how do I know the LIKE
condition is the evil here?
3rd question:
How do I know how much time SQL takes to handle one task is just the right amount or not?
I'm asking this question because I'm new to optimizing queries. The first time I saw the plan, I don't know if an Index-seek
on 3 million rows that cost 18s is just right or there's something wrong with it.