Sql Server : Why is a query with literals performs faster than a query with variables (parameter sniffing)
- Posted by Sqltimes
- On August 29, 2015
- 0 Comments
Quick one today:
Let’s look at an Interesting, albeit, seemingly anomalous behavior with in Sql Server.
Question
- When does a query with literals perform faster than query with variables? Even with a simple query.
Let’s look at some examples:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
-- -- Query 1 : Query with literals -- SELECT Column1, Col2 FROM dbo.SampleTable WHERE ID BETWEEN 1 AND 100 GO -- -- Query 2 : Query with variables -- DECLARE @L INT = 1, @U INT = 100 SELECT Column1, Col2 FROM dbo.SampleTable AS O WHERE O.ID BETWEEN @L AND @U GO |
The only difference between both they queries is that in the first one, we are providing literals in the WHERE clause; In second one, we provide values through variables. Even for a simple query like this, the execution plan is different along with the execution duration. This applies only for large tables (for small tables it does not make much difference).
Explanation
In the first query, the Query Optimizer, has clear idea on what is being executed. You know the query and its bounds; So it uses statistics available on this column to come up with the best possible query plan. Sql Server knows the exact number of records that are returned, so it comes up with most appropriate plan.
Whereas, the second query, at the time of compilation, the bounds are not available. Sql Server does not know if the query would return 100 records or 100 million records. It still uses statistics, but it uses it to come up with most appropriate plan including the worst case scenario. So, if you end up providing values that return only 10 records; Too bad. Sql Server still uses the ‘gargantuan query plan’ it designed for worst case scenario. So, it ends up being inefficient for most cases.
Alternative
If I run is using dynamic query execution, it runs faster:
1
2
3
4
5
6
7
8
9
10
11
|
-- -- Query 3 : Dynamic Query -- DECLARE @L INT = 1, @U INT = 100 SELECT @SQL = 'SELECT Column1, Col2 FROM dbo.SampleTable WHERE ID BETWEEN ' + CONVERT ( VARCHAR , @L) + ' AND ' + CONVERT ( VARCHAR , @U) EXECUTE (@SQL) GO |
Explanation
Obviously, by the time the execution comes to the EXEC (@SQL) section, the query variables are already replaced with literals. So, essentially Query 3 is same as Query 1.
Hope this helps,
_Sqltimes
0 Comments