{"id":137,"date":"2016-03-03T08:50:42","date_gmt":"2016-03-03T08:50:42","guid":{"rendered":"https:\/\/sqldoubleg.live-website.com\/?p=137"},"modified":"2016-03-03T09:16:57","modified_gmt":"2016-03-03T09:16:57","slug":"fast-with-values-slow-with-variables","status":"publish","type":"post","link":"https:\/\/www.sqldoubleg.com\/es\/2016\/03\/03\/fast-with-values-slow-with-variables\/","title":{"rendered":"Fast with values slow with variables"},"content":{"rendered":"<p>There is a big debate why the same query can run fast with hard coded values and slow when using variables, come see there is a reasonable explanation for all this mess.&nbsp;The other day I saw a thread on linkedin groups that was titled <a href=\"https:\/\/www.linkedin.com\/groups\/86080\/86080-6105764028573900804\" target=\"_blank\">&#8216;Using Parameters in a Query is Killing Performance&#8217;<\/a>, where the author originally complaint because a query was running fine with hard coded values, but horribly slow when running with variables instead.<\/p>\n<p>The variety of answers was big, giving all kind of recommendations always based on experience and best practices&#8230; hm mh<\/p>\n<p>To find a proper explanation, let&#8217;s present a simple example where you can see by yourselves how the frustration the author is absolutely real. <\/p>\n<p>I will use the [AdventureWorks2014] database to illustrate it, although since there are not so many rows in it, we must have a bit of imagination and extrapolate the results as if we were working with a bigger table.<\/p>\n<p>First the query in two different versions, with the hard coded value and with then using an innocent variable to see why those are and behave differently<\/p>\n<pre class=\"brush: tsql; title: ; notranslate\" title=\"\">\r\nUSE [AdventureWorks2014]\r\n\r\nSELECT * \r\n\tFROM Person.Address\r\n\tWHERE [StateProvinceID]  = 1\r\nGO\r\n\r\nUSE [AdventureWorks2014]\r\n\r\nDECLARE @StateProvince INT = 1\r\n\r\nSELECT * \r\n\tFROM Person.Address\r\n\tWHERE [StateProvinceID]  = @StateProvince\r\nGO\r\n<\/pre>\n<p>As I told you earlier, this table has less than 20k rows, hence even the \u00abhorribly slow\u00bb version will be quite fast.<\/p>\n<p>But, if we look at the query plans we can see they are very different, and that should be suspicious enough to investigate a bit more.<\/p>\n<p>The first query&#8217;s execution plan shows an <strong><em>Index Seek<\/em><\/strong> to get the rows that match the condition (there is a non clustered index on this column) plus the <strong><em>Key Lookups<\/em><\/strong> to get the rest of the columns from the clustered index in this case.<\/p>\n<p><a href=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2016\/03\/Good_plan_province_1.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2016\/03\/Good_plan_province_1.png\" alt=\"Good_plan_province_1\" width=\"638\" height=\"473\" class=\"aligncenter size-full wp-image-139\" srcset=\"https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/03\/Good_plan_province_1.png 638w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/03\/Good_plan_province_1-300x222.png 300w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/03\/Good_plan_province_1-150x111.png 150w\" sizes=\"(max-width: 638px) 100vw, 638px\" \/><\/a><\/p>\n<p>But the second one does not look quite the same, the optimizer in this case, has chosen a full <strong><em>Clustered Index Scan<\/em><\/strong>, see it there.<\/p>\n<p><a href=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2016\/03\/Bad_plan_province_1.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2016\/03\/Bad_plan_province_1.png\" alt=\"Bad_plan_province_1\" width=\"590\" height=\"603\" class=\"aligncenter size-full wp-image-140\" \/><\/a><\/p>\n<p>Why the second one has not done the same? If we compare the IO we can see the first one is far more efficient, achieving the result using less reads<\/p>\n<pre class=\"brush: tsql; title: ; notranslate\" title=\"\">\r\n\/*\r\n-- Index seek plus lookups\r\n(25 row(s) affected)\r\nTable 'Address'. Scan count 1, logical reads 52, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.\r\n\r\n(1 row(s) affected)\r\n\r\n\r\n-- Clustered index scan\r\n(25 row(s) affected)\r\nTable 'Address'. Scan count 1, logical reads 346, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.\r\n\r\n(1 row(s) affected)\r\n\r\n*\/\r\n<\/pre>\n<p>Seven[~ish] times more reads using a full scan&#8230; the optimizer just gone crazy or what?<\/p>\n<p>I want you to look at the <strong><em>Estimated Rows<\/em><\/strong> versus the <strong><em>Actual Rows<\/em><\/strong>, in the first query is the same but the second&#8230; Oh dear! more than 10 times bigger.<\/p>\n<p>All this have an explanation (for sure), and we can see it if we look at the statistics for our non clustered index.<\/p>\n<pre class=\"brush: tsql; title: ; notranslate\" title=\"\">\r\n\r\nDBCC SHOW_STATISTICS ('[Person].[Address]', 'IX_Address_StateProvinceID')\r\nGO\r\n\r\n<\/pre>\n<p>Which give us these numbers<\/p>\n<p><a href=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2016\/03\/show_statistics.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2016\/03\/show_statistics.png\" alt=\"show_statistics\" width=\"915\" height=\"447\" class=\"aligncenter size-full wp-image-142\" \/><\/a><\/p>\n<p>When looking at the histogram we can see that for [StateProvinceId] = 1 there are estimated 25 rows, nice! that is the right number.<\/p>\n<p>This, though, does not explain why the second query estimate 265 rows for<br \/>\n[StateProvinceId] = @StateProvince<\/p>\n<p>The thing is that when the query optimizer has to come up with a plan, is not aware of which value the variable will contain at the time of execution, hence it&#8217;s impossible to figure out what the [estimate] number of rows there will be, so it just take the average.<\/p>\n<p>That is calculated multiplying the [Rows] from the first recordset <strong>19614<\/strong> by the [All Density] from the second recordset <strong>0.01351351 = 265.05398514<\/strong> and there you have your 265 estimated rows.<\/p>\n<p>And what the optimizer knows for sure is for that many rows, the most optimal plan would be a Clustered Index Scan, you can prove it if you chose any province where the number of rows is close to 265 (see StateProvince 8, 19,20&#8230;). The plan generated for both is the same, Clustered Index Scan.<\/p>\n<p>That&#8217;s why it&#8217;s so important not only to have accurate statistics, but knowing how SQL Server works internally to avoid unexpected surprises.<\/p>\n<p>The ways to overcome this kind of situations is too long for this writing so I invite you to read about stats and query execution plans by yourselves.<\/p>\n<p>Thanks for reading and please feel free to ask questions you may have.<br \/>\n&nbsp;<br \/>\n&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>There is a big debate why the same query can run fast with hard coded values and slow when using variables, come see there is a reasonable explanation for all this mess.&nbsp;The&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[27,5,12,26],"_links":{"self":[{"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/posts\/137"}],"collection":[{"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/comments?post=137"}],"version-history":[{"count":1,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/posts\/137\/revisions"}],"predecessor-version":[{"id":144,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/posts\/137\/revisions\/144"}],"wp:attachment":[{"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/media?parent=137"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/categories?post=137"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/tags?post=137"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}