{"id":920,"date":"2011-07-02T16:34:37","date_gmt":"2011-07-02T18:34:37","guid":{"rendered":"http:\/\/fabriciolima.net\/blog\/?p=920"},"modified":"2016-07-23T23:22:36","modified_gmt":"2016-07-24T02:22:36","slug":"routine-to-update-database-statistics","status":"publish","type":"post","link":"https:\/\/fabriciolima.net\/blog\/2011\/07\/02\/routine-to-update-database-statistics\/","title":{"rendered":"Routine to Update Database Statistics"},"content":{"rendered":"<p>Hi Folks,<\/p>\n<p>First off all, sorry for my English. I hope you understand me.<\/p>\n<p>We always heard that the database statistics must be constantly updated to the Query Optimizer generate the best execution plan for our querys. For this, we should enable the &#8216;Auto Update Statistics&#8217; option in our databases.<\/p>\n<p>However, with this option enabled if our table has more than 500 rows (almost our table have), the statistics in this table will be updated only when we have &#8220;<em>500 + 20% of the table size&#8221;<\/em> of changes in the table. The worst is that can happen in a wrong time and create more cost to your production environment.<\/p>\n<p>In my Job, I have a table that is widely used and it has 70 million of rows, If I wait for the statistics to be updated automatically (ignoring the index statistics that are updated with a REBUILD for example), my statistics would be updated when I reach <em>&#8220;500 + 20% * 70,000,000 = 14,000,500&#8221;<\/em> of changes. In my case, it would take a very long time and querys with bad plans could be generated.<\/p>\n<p>So, with the objective to minimize this problems, I decided to update my statistics when I reach 0.5% of changes on my table (and not 20% as is the default). I set this value because the UPDATE STATISTICS WITH FULLSCAN operation is very expensive and I don&#8217;t have a window to update all my statistics daily. Then, my table will be an update statistics when I get <em>&#8220;70,000,000 * 0,005 = 350,000<\/em>&#8221; of changes.<\/p>\n<p>As I already have a window to indexes maintenance and dozens of routines running at dawn, I get in the VPN at 22:40 and I ran an UPDATE STATISTICS at this time to analyze the impact it would have in my database that is 7 x 24. So, I decided to use a window 22:40 to 23:50 to perform this procedure.<\/p>\n<p>In each database that I want to run the update statistics, I created the procedure below. Then I did a job that runs daily at 22:40 with one step for each database.<\/p>\n<p>CREATE PROCEDURE [dbo].[stpUpdate_Stats]\nAs<br \/>\nBEGIN<\/p>\n<p>SET NOCOUNT ON<\/p>\n<p>&#8212; To go out the routine when the window finish<br \/>\nIF GETDATE()&gt; dateadd(mi,+50,dateadd(hh,+23,cast(floor(cast(getdate()as float))as datetime)))&#8211; Time &gt; 23:50<br \/>\nRETURN<\/p>\n<p>Create table #Update_Stats(<br \/>\nId_Stats int identity(1,1),<br \/>\nDs_Comand varchar(4000),<br \/>\nNr_Rows int)<\/p>\n<p>;WITH Size_Tables AS (<br \/>\nSELECT obj.name, prt.rows<br \/>\nFROM sys.objects obj<br \/>\nJOIN sys.indexes idx on obj.object_id= idx.object_id<br \/>\nJOIN sys.partitions prt on obj.object_id= prt.object_id<br \/>\nJOIN sys.allocation_units alloc on alloc.container_id= prt.partition_id<br \/>\nWHERE obj.type= &#8216;U&#8217; AND idx.index_id IN (0, 1)and prt.rows&gt; 1000<br \/>\nGROUP BY obj.name, prt.rows)<\/p>\n<p>insert into #Update_Stats(Ds_Comand,Nr_Rows)<br \/>\nSELECT &#8216;UPDATE STATISTICS &#8216; + B.name+ &#8216; &#8216; + A.name+ &#8216; WITH FULLSCAN&#8217;, D.rows<br \/>\nFROM sys.stats A<br \/>\njoin sys.sysobjects B on A.object_id = B.id<br \/>\njoin sys.sysindexes C on C.id = B.id and A.name= C.Name<br \/>\nJOIN Size_Tables D on\u00a0 B.name= D.Name<br \/>\nWHERE\u00a0 C.rowmodctr &gt; 100<br \/>\nand C.rowmodctr&gt; D.rows*.005<br \/>\nand substring( B.name,1,3) not in (&#8216;sys&#8217;,&#8217;dtp&#8217;)<br \/>\nORDER BY D.rows<\/p>\n<p>declare @Loop int, @Comand nvarchar(4000)<br \/>\nset @Loop = 1<\/p>\n<p>while exists(select top 1 null from #Update_Stats)<br \/>\nbegin<br \/>\nIF GETDATE()&gt; dateadd(mi,+50,dateadd(hh,+23,cast(floor(cast(getdate()as float))as datetime)))&#8211; Time &gt; 23:50<br \/>\nBEGIN<br \/>\nBREAK &#8212; To go out the routine when the window finish<br \/>\nEND<\/p>\n<p>select @Comand = Ds_Comand<br \/>\nfrom #Update_Stats<br \/>\nwhere Id_Stats = @Loop<\/p>\n<p>EXECUTE sp_executesql @Comand<\/p>\n<p>delete from #Update_Stats<br \/>\nwhere Id_Stats = @Loop<\/p>\n<p>set @Loop= @Loop + 1<br \/>\nend<br \/>\nEND<\/p>\n<p>If someone has another suggestions for an update statistics routine, please feel free to leave a comment.<\/p>\n<p>Remember, if you have a large window, you can update all statistics daily and the Query Optimizer will thank you a lot.<\/p>\n<p>Enjoy,<\/p>\n<p>Fabr\u00edcio Fran\u00e7a Lima<\/p>\n<p>MCITP \u2013 Database Administrator<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hi Folks, First off all, sorry for my English. I hope you understand me. We always heard that the database statistics must be constantly updated to the Query Optimizer generate the best execution plan for our querys. For this, we should enable the &#8216;Auto Update Statistics&#8217; option in our databases. However, with this option enabled [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_exactmetrics_skip_tracking":false,"_exactmetrics_sitenote_active":false,"_exactmetrics_sitenote_note":"","_exactmetrics_sitenote_category":0,"footnotes":""},"categories":[3,33],"tags":[350,362,1094,1093,63,351,357,1092,1095,1091,1090,49,33,358,363,155,359,354,361,355,40,352,353],"class_list":["post-920","post","type-post","status-publish","format-standard","hentry","category-administracao-de-bd","category-sql-server","tag-desempenho","tag-es","tag-help-update-statistics","tag-how-to-update-statistics","tag-performance","tag-query-optimizer","tag-rowmodctr","tag-script-sql-server","tag-script-sql-server-statistics","tag-script-statistics","tag-script-update-statistics","tag-sql","tag-sql-server","tag-sys-allocation_units-alloc","tag-sys-inde","tag-sys-objects","tag-sys-partitions","tag-sys-stats","tag-sys-sysinde","tag-sys-sysobjects","tag-tuning","tag-update-statistics","tag-update-statistics-with-fullscan"],"post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/920","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/comments?post=920"}],"version-history":[{"count":0,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/920\/revisions"}],"wp:attachment":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/media?parent=920"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/categories?post=920"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/tags?post=920"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}