{"id":507,"date":"2012-03-13T11:30:00","date_gmt":"2012-03-13T11:30:00","guid":{"rendered":"\/blogs\/paul\/post\/How-are-per-column-modification-counts-tracked.aspx"},"modified":"2017-04-13T09:50:47","modified_gmt":"2017-04-13T16:50:47","slug":"how-are-per-column-modification-counts-tracked","status":"publish","type":"post","link":"https:\/\/www.sqlskills.com\/blogs\/paul\/how-are-per-column-modification-counts-tracked\/","title":{"rendered":"How are per-column modification counts tracked?"},"content":{"rendered":"<p>\n<font face=\"verdana,geneva\" size=\"2\">Earlier today there was a question on the MVP mailing list asking how SQL Server keeps track of per-column modification counts.<\/font>\n<\/p>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">From 2008 onwards, the hidden system table <font face=\"courier new,courier\">sys.sysrscols<\/font> tracks modifications to table columns using the <font face=\"courier new,courier\">rcmodified<\/font> column. The hidden system tables (introduced in 2005 when we rewrote the entire metadata management system) are only accessible when connected using the <\/font><a href=\"https:\/\/msdn.microsoft.com\/Areas\/Epx\/Content\/500.htm?aspxerrorpath=\/en-us\/library\/ms189595.aspx\" target=\"_blank\"><font face=\"verdana,geneva\" size=\"2\">Dedicated Administrator Connection<\/font><\/a><font face=\"verdana,geneva\" size=\"2\"> (DAC), as I&#39;ve blogged about many times before. This means connecting using <font face=\"courier new,courier\">SQLCMD -A<\/font> or with the prefix &#39;<font face=\"courier new,courier\">admin:<\/font>&#39; on your connection string.<\/font>\n<\/p>\n<p>\n<font size=\"2\">It&#39;s also exposed in the <font face=\"courier new,courier\">sys.system_internals_partition_columns<\/font> catalog view too, which doesn&#39;t require the DAC. (I&#39;d forgotten this &#8211; thanks to my friend Remus Rusanu for reminding me!)<\/font>\n<\/p>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">Bear in mind, however, that this is inferred behavior based on my background knowledge and observations and may well change completely in future versions &#8211; it&#39;s all undocumented and so you really shouldn&#39;t build anything that relies on this programmatically. If I don&#39;t say that then my good friend Conor Cunningham will slap me next time he sees me :-)<\/font>\n<\/p>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">Here&#39;s an example using a simple table:<\/font>\n<\/p>\n<blockquote>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">CREATE TABLE t1 (c1 INT, c2 INT, c3 INT);<br \/>\n\tGO<\/font>\n\t<\/p>\n<\/blockquote>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">And we can look at the per-column modification counters using the DAC query:<\/font>\n<\/p>\n<blockquote>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">SELECT<br \/>\n\t&nbsp;&nbsp;&nbsp; p.[object_id],<br \/>\n\t&nbsp;&nbsp;&nbsp; p.[index_id],<br \/>\n\t&nbsp;&nbsp;&nbsp; rs.[rscolid],<br \/>\n\t&nbsp;&nbsp; &nbsp;rs.[rcmodified]<br \/>\n\tFROM sys.sysrscols rs<br \/>\n\tJOIN sys.partitions p<br \/>\n\t&nbsp;&nbsp;&nbsp; ON rs.[rsid] = p.[partition_id]<br \/>\n\tWHERE p.[object_id] = OBJECT_ID (&#39;t1&#39;);<br \/>\n\tGO<\/font>\n\t<\/p>\n<\/blockquote>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">And we get:<\/font>\n<\/p>\n<blockquote>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">object_id&nbsp;&nbsp; index_id&nbsp;&nbsp;&nbsp; rscolid&nbsp;&nbsp;&nbsp;&nbsp; rcmodified<br \/>\n\t&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211;<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<\/font>\n\t<\/p>\n<\/blockquote>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">And you can use the <font face=\"courier new,courier\">sys.system_internals_partition_columns<\/font> view as well:<\/font>\n<\/p>\n<blockquote>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">SELECT<br \/>\n\t&nbsp;&nbsp;&nbsp; p.[object_id],<br \/>\n\t&nbsp;&nbsp;&nbsp; p.[index_id],<br \/>\n\t&nbsp;&nbsp;&nbsp; pc.[partition_column_id],<br \/>\n\t&nbsp;&nbsp;&nbsp; pc.[modified_count]<br \/>\n\tFROM sys.system_internals_partition_columns pc<br \/>\n\tJOIN sys.partitions p<br \/>\n\t&nbsp;&nbsp;&nbsp; ON pc.[partition_id] = p.[partition_id]<br \/>\n\tWHERE p.[object_id] = OBJECT_ID (&#39;t1&#39;);<br \/>\n\tGO&nbsp;<\/font>\n\t<\/p>\n<\/blockquote>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">I&#39;ll continue with the DAC query against <font face=\"courier new,courier\">sysrscols<\/font> directly.<\/font>\n<\/p>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">If we do a modification and run the DAC query again:<\/font>\n<\/p>\n<blockquote>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">INSERT INTO t1 VALUES (1, 1, 1);<br \/>\n\tGO<\/font>\n\t<\/p>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">object_id&nbsp;&nbsp; index_id&nbsp;&nbsp;&nbsp; rscolid&nbsp;&nbsp;&nbsp;&nbsp; rcmodified<br \/>\n\t&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<\/font>\n\t<\/p>\n<\/blockquote>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">Huh? It didn&#39;t show up yet. This is because updates to some system tables are not flushed from the in-memory metadata cache until a checkpoint occurs.<\/font>\n<\/p>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">Let&#39;s try that, and then run the DAC query again:<\/font>\n<\/p>\n<blockquote>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">CHECKPOINT;<br \/>\n\tGO<\/font>\n\t<\/p>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">object_id&nbsp;&nbsp; index_id&nbsp;&nbsp;&nbsp; rscolid&nbsp;&nbsp;&nbsp;&nbsp; rcmodified<br \/>\n\t&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1<\/font>\n\t<\/p>\n<\/blockquote>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">And to prove it, I&#39;ll update just <font face=\"courier new,courier\">c2<\/font> twice, checkpoint, and run the DAC query again.<\/font>\n<\/p>\n<blockquote>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">UPDATE t1 SET c2 = 2;<br \/>\n\tUPDATE t1 SET c2 = 3;<br \/>\n\tCHECKPOINT;<br \/>\n\tGO<\/font>\n\t<\/p>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">object_id&nbsp;&nbsp; index_id&nbsp;&nbsp;&nbsp; rscolid&nbsp;&nbsp;&nbsp;&nbsp; rcmodified<br \/>\n\t&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1<\/font>\n\t<\/p>\n<\/blockquote>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">Cool eh?<\/font>\n<\/p>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">What about the <font face=\"courier new,courier\">sysindexes<\/font> column <font face=\"courier new,courier\">rowmodctr<\/font>? How is that tracked\/calculated?<\/font>\n<\/p>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">It&#39;s calculated as&nbsp;the delta of the&nbsp;<font face=\"courier new,courier\">sysrscols.rcmodified<\/font> counter for the leading column in the index since the&nbsp;index column statistics were last rebuilt (or initially created).<\/font>\n<\/p>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">Here are some&nbsp;simple indexes on our table, plus a <font face=\"courier new,courier\">sysindexes<\/font>&nbsp;query to&nbsp;get the <font face=\"courier new,courier\">rowmodctr<\/font> for it:<\/font>\n<\/p>\n<blockquote>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">CREATE NONCLUSTERED INDEX t1_c1_c2 ON t1 (c1, c2);<br \/>\n\tCREATE NONCLUSTERED INDEX t1_c3 ON t1 (c3);<br \/>\n\tGO<\/font>\n\t<\/p>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">SELECT<br \/>\n\t&nbsp;&nbsp;&nbsp; [name],<br \/>\n\t&nbsp;&nbsp;&nbsp; [rowmodctr]<br \/>\n\tFROM sysindexes<br \/>\n\tWHERE [id] = OBJECT_ID (&#39;t1&#39;);<br \/>\n\tGO<\/font>\n\t<\/p>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rowmodctr<br \/>\n\t&#8212;&#8212;&#8212;&#8212;&#8212;- &#8212;&#8212;&#8212;&#8211;<br \/>\n\tNULL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3<br \/>\n\tt1_c1_c2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br \/>\n\tt1_c3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br \/>\n\t<\/font>\n\t<\/p>\n<\/blockquote>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">The first row is for the heap, as I didn&#39;t created a clustered index.&nbsp;<\/font>\n<\/p>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">Let&#39;s make some changes and see how the <font face=\"courier new,courier\">sysindexes.rowmodctr<\/font> and <font face=\"courier new,courier\">sysrscols.rcmodified<\/font> columns change:<\/font>\n<\/p>\n<blockquote>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">UPDATE t1 SET c1 = 4;<br \/>\n\tUPDATE t1 SET c1 = 5;<br \/>\n\tUPDATE t1 SET c1 = 6;<br \/>\n\tUPDATE t1 SET c2 = 2;<br \/>\n\tUPDATE t1 SET c2 = 3;<br \/>\n\tUPDATE t1 SET c3 = 2;<br \/>\n\tCHECKPOINT;<br \/>\n\tGO<\/font>\n\t<\/p>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">object_id&nbsp;&nbsp; index_id&nbsp;&nbsp;&nbsp; rscolid&nbsp;&nbsp;&nbsp;&nbsp; rcmodified<br \/>\n\t&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2<br \/>\n\t277576027&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br \/>\n\t277576027&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br \/>\n\t277576027&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br \/>\n\t277576027&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br \/>\n\t277576027&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<\/font>\n\t<\/p>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rowmodctr<br \/>\n\t&#8212;&#8212;&#8212;&#8212;&#8212;- &#8212;&#8212;&#8212;&#8211;<br \/>\n\tNULL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5<br \/>\n\tt1_c1_c2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3<br \/>\n\tt1_c3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1<\/font>\n\t<\/p>\n<\/blockquote>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">Since the nonclustered indexes were created, I performed 3 updates to <font face=\"courier new,courier\">c1<\/font>, 2 updates to <font face=\"courier new,courier\">c2<\/font>, and 1 update to <font face=\"courier new,courier\">c3<\/font>. The <font face=\"courier new,courier\">sysrscols.rcmodified<\/font> counter for each of these columns increased by the correct number, but you can see that it isn&#39;t tracked for the columns in the nonclustered indexes themselves. Btw, the final column in each of the nonclustered indexes is referring to the hidden physical RID &#39;column&#39; in the index that points back to the corresponding heap data record.<\/font>\n<\/p>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">However, the <font face=\"courier new,courier\">sysindexes.rowmodctr<\/font> changed unintuitively. I performed 5 separate modifications to the columns contained in the <font face=\"courier new,courier\">t1_c1_c2<\/font> index and yet its <font face=\"courier new,courier\">rowmodctr<\/font> is only set to 3. This is because the algorithm for <font face=\"courier new,courier\">rowmodctr<\/font> is to take the per-column delta of <font face=\"courier new,courier\">sysrscols.rcmodified<\/font> for the leading index column since the index column statistics were last rebuilt (or initially created).<\/font>\n<\/p>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">I&#39;ll prove it by updating the statistics, doing&nbsp;2 modifications to <font face=\"courier new,courier\">c1<\/font>,&nbsp;4 modifications to <font face=\"courier new,courier\">c2<\/font> and checkpointing. We should see the <font face=\"courier new,courier\">sysrscols.rcmodified<\/font> for <font face=\"courier new,courier\">c1<\/font> go to 6, for <font face=\"courier new,courier\">c2<\/font> go to 9, and the <font face=\"courier new,courier\">sysindexes.rowmodctr<\/font> for <font face=\"courier new,courier\">t1_c1_c2<\/font> changes to 2.<\/font>\n<\/p>\n<blockquote>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">UPDATE STATISTICS t1;<br \/>\n\tGO<\/font>\n\t<\/p>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">UPDATE t1 SET c1 = 7;<br \/>\n\tUPDATE t1 SET c1 = 8;<br \/>\n\tUPDATE t1 SET c2 = 4;<br \/>\n\tUPDATE t1 SET c2 = 5;<br \/>\n\tUPDATE t1 SET c2 = 6;<br \/>\n\tUPDATE t1 SET c2 = 7;<br \/>\n\tCHECKPOINT;<br \/>\n\tGO<\/font>\n\t<\/p>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">object_id&nbsp;&nbsp; index_id&nbsp;&nbsp;&nbsp; rscolid&nbsp;&nbsp;&nbsp;&nbsp; rcmodified<br \/>\n\t&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9<br \/>\n\t277576027&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2<br \/>\n\t277576027&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br \/>\n\t277576027&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br \/>\n\t277576027&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br \/>\n\t277576027&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br \/>\n\t277576027&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<\/font>\n\t<\/p>\n<p>\n\t<font face=\"courier new,courier\" size=\"2\">name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rowmodctr<br \/>\n\t&#8212;&#8212;&#8212;&#8212;&#8212;- &#8212;&#8212;&#8212;&#8211;<br \/>\n\tNULL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9<br \/>\n\tt1_c1_c2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2<br \/>\n\tt1_c3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<\/font>\n\t<\/p>\n<\/blockquote>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">Like so. Even though we updated <font face=\"courier new,courier\">c2<\/font> 4 times, the <font face=\"courier new,courier\">sysindexes.rowmodctr<\/font> for <font face=\"courier new,courier\">t1_c1_c2<\/font> is only set to 2, clearly using the <font face=\"courier new,courier\">sysrscols.rcmodified<\/font> delta for c1.<\/font>\n<\/p>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">You can play around with these examples to further convince yourself of the current behavior.<\/font>\n<\/p>\n<p>\n<font face=\"verdana,geneva\" size=\"2\">Hope this has helped remove some confusion about this. There are unfortunately many, many twists to this and when counts are modified&#8230;<\/font>\n<\/p>\n<p>\n<font size=\"2\">Enjoy playing around!<\/font>\n<\/p>\n<p>\n<font face=\"verdana,geneva\" size=\"2\"><\/font>\n<\/p>\n<p>\n<font face=\"verdana,geneva\" size=\"2\"><\/font><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Earlier today there was a question on the MVP mailing list asking how SQL Server keeps track of per-column modification counts. From 2008 onwards, the hidden system table sys.sysrscols tracks modifications to table columns using the rcmodified column. The hidden system tables (introduced in 2005 when we rewrote the entire metadata management system) are only [&hellip;]<\/p>\n","protected":false},"author":5,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[47,48,62,90],"tags":[],"class_list":["post-507","post","type-post","status-publish","format-standard","hentry","category-indexes-from-every-angle","category-inside-the-storage-engine","category-on-disk-structures","category-statistics"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>How are per-column modification counts tracked? - Paul S. Randal<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.sqlskills.com\/blogs\/paul\/how-are-per-column-modification-counts-tracked\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How are per-column modification counts tracked? - Paul S. Randal\" \/>\n<meta property=\"og:description\" content=\"Earlier today there was a question on the MVP mailing list asking how SQL Server keeps track of per-column modification counts. From 2008 onwards, the hidden system table sys.sysrscols tracks modifications to table columns using the rcmodified column. The hidden system tables (introduced in 2005 when we rewrote the entire metadata management system) are only [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.sqlskills.com\/blogs\/paul\/how-are-per-column-modification-counts-tracked\/\" \/>\n<meta property=\"og:site_name\" content=\"Paul S. Randal\" \/>\n<meta property=\"article:published_time\" content=\"2012-03-13T11:30:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2017-04-13T16:50:47+00:00\" \/>\n<meta name=\"author\" content=\"Paul Randal\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Paul Randal\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/how-are-per-column-modification-counts-tracked\/\",\"url\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/how-are-per-column-modification-counts-tracked\/\",\"name\":\"How are per-column modification counts tracked? - Paul S. Randal\",\"isPartOf\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/#website\"},\"datePublished\":\"2012-03-13T11:30:00+00:00\",\"dateModified\":\"2017-04-13T16:50:47+00:00\",\"author\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/ffcec826c18782e1e0adf173826a7fce\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/how-are-per-column-modification-counts-tracked\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.sqlskills.com\/blogs\/paul\/how-are-per-column-modification-counts-tracked\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/how-are-per-column-modification-counts-tracked\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How are per-column modification counts tracked?\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/#website\",\"url\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/\",\"name\":\"Paul S. Randal\",\"description\":\"In Recovery...\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/ffcec826c18782e1e0adf173826a7fce\",\"name\":\"Paul Randal\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/0b6a266bba2f088f2551ef529293001bd73bf026bc1908b9866728c062beeeb6?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/0b6a266bba2f088f2551ef529293001bd73bf026bc1908b9866728c062beeeb6?s=96&d=mm&r=g\",\"caption\":\"Paul Randal\"},\"sameAs\":[\"http:\/\/3.209.169.194\/blogs\/paul\"],\"url\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/author\/paul\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"How are per-column modification counts tracked? - Paul S. Randal","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.sqlskills.com\/blogs\/paul\/how-are-per-column-modification-counts-tracked\/","og_locale":"en_US","og_type":"article","og_title":"How are per-column modification counts tracked? - Paul S. Randal","og_description":"Earlier today there was a question on the MVP mailing list asking how SQL Server keeps track of per-column modification counts. From 2008 onwards, the hidden system table sys.sysrscols tracks modifications to table columns using the rcmodified column. The hidden system tables (introduced in 2005 when we rewrote the entire metadata management system) are only [&hellip;]","og_url":"https:\/\/www.sqlskills.com\/blogs\/paul\/how-are-per-column-modification-counts-tracked\/","og_site_name":"Paul S. Randal","article_published_time":"2012-03-13T11:30:00+00:00","article_modified_time":"2017-04-13T16:50:47+00:00","author":"Paul Randal","twitter_misc":{"Written by":"Paul Randal","Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/how-are-per-column-modification-counts-tracked\/","url":"https:\/\/www.sqlskills.com\/blogs\/paul\/how-are-per-column-modification-counts-tracked\/","name":"How are per-column modification counts tracked? - Paul S. Randal","isPartOf":{"@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/#website"},"datePublished":"2012-03-13T11:30:00+00:00","dateModified":"2017-04-13T16:50:47+00:00","author":{"@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/ffcec826c18782e1e0adf173826a7fce"},"breadcrumb":{"@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/how-are-per-column-modification-counts-tracked\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.sqlskills.com\/blogs\/paul\/how-are-per-column-modification-counts-tracked\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/how-are-per-column-modification-counts-tracked\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.sqlskills.com\/blogs\/paul\/"},{"@type":"ListItem","position":2,"name":"How are per-column modification counts tracked?"}]},{"@type":"WebSite","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/#website","url":"https:\/\/www.sqlskills.com\/blogs\/paul\/","name":"Paul S. Randal","description":"In Recovery...","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.sqlskills.com\/blogs\/paul\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/ffcec826c18782e1e0adf173826a7fce","name":"Paul Randal","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/0b6a266bba2f088f2551ef529293001bd73bf026bc1908b9866728c062beeeb6?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/0b6a266bba2f088f2551ef529293001bd73bf026bc1908b9866728c062beeeb6?s=96&d=mm&r=g","caption":"Paul Randal"},"sameAs":["http:\/\/3.209.169.194\/blogs\/paul"],"url":"https:\/\/www.sqlskills.com\/blogs\/paul\/author\/paul\/"}]}},"_links":{"self":[{"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/posts\/507","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/comments?post=507"}],"version-history":[{"count":0,"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/posts\/507\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/media?parent=507"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/categories?post=507"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/tags?post=507"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}