| Automated Benchmarking and Trend 
				Metrics Collection Techniques with T-SQL Part 2 
				Performance Counters via sysperfinfo   
				By Bill 
				Wunder 
				A couple of weeks ago I set out to pirate a phrase from the 
				life sciences to describe the shortcutting often used to 
				determine when and if a SQL Server is in need of a hardware 
				upgrade or possibly even an application design re-evaluation. In 
				a manner far too similar to that which Social Darwinism in the 
				early 20th century helped lay waste to any prospects of a humane 
				humanity and helped to propagate a world order of oppression and 
				violent wars, so is SQL Darwinism propping up an explosion of 
				big iron solutions to rather more modest data applications. Not 
				that I would want to even pretend that any flaws in current 
				hardware sizing practices carry anything even close to the 
				consequences for life on our planet as the might-is-right 
				syllogism spawned in part from Social Darwinism. Rather, my 
				contention is simply that in both cases there is a large and 
				inexplicable subjective element necessary to get from the 
				underlying science - or probably more appropriately the 
				underlying engineering in the case of SQL Darwinism - to the 
				overt manifestations. 
				As often as not, the decision on when to upgrade hardware is 
				based on the ubiquitous use-it-or-lose-it budgetary practice 
				over any quantifiable prediction of the need for more 
				throughput. As often as not the the determination of a target 
				upgrade platform is couched in some mystical boogie factor - 
				ever used "estimate the need and multiply by 2" calculus? - and 
				driven more by vendor marketing spiel than even the most 
				fundamental of formulae. In part, these shortfalls can be 
				overcome if the organization is well prepared to be able to 
				quantify the next move.  
				I don't want to spend to much time here expounding upon the 
				possibility that the business thought process that is SQL 
				Darwinism is flawed similarly to the socio-political thought 
				process that is Social Darwinism. I just wanted to stir that 
				idea up a little bit before I dive into the topic at hand. If 
				you'd like to explore that notion please refer to the discussion 
				and links I've provided in that earlier
				SQL Darwinism article. 
				Here my intention is only to use that as spring board into what 
				seems to me to be a more reasonable approach: quantifiable 
				analytical trend analysis.  
				The way to objectively evaluate current behavioral qualities 
				and the performance and throughput trends of a SQL Server is of 
				course to measure them over time. In order to provide 
				fundamental meaning to any
				
				time series analysis it is necessary to establish a base or 
				beginning point of measurement. System Monitor (aka Perfmon) is 
				commonly referenced as a valid tool to capture behavior over 
				time on a Microsoft Windows server. While system monitor does a 
				very good job of collecting and graphically depicting behavior 
				over time, it does not lend itself as nicely to ad hoc, a priori 
				or otherwise deductive analysis. Typically, when you do find 
				someone that has gone through an exercise in using Perfmon 
				captured data points in a trend analysis, there will be a time 
				intensive component in the analysis where the analyst has 
				manually moved data points from Perfmon into a spreadsheet or 
				database. One outstanding feature within SQL Server 2000 is that 
				the SQL Server Perfmon metrics are already available. Most in 
				table form in the system table master.dbo.sysperfinfo. A few 
				others from the system function ::fn_virtualfilestats. And even 
				some low level statistics accessible via the System Statistical 
				Functions and  sp_monitor as we have already discovered in the 
				previous article: 
				Benchmarking Techniques Using T-SQL Part 1 - System Statistical 
				Functions. To continue to build upon that article, in this 
				installment we will first give sysperfinfo a good look, then we 
				can give due consideration to the file level metrics available 
				through ::fn_virtualfilestats.  
				sysperfinfo 
				sysperfino is a system supplied virtual table much like 
				sysprocesses and syslocks and a few others. Virtual tables are 
				like views with the exception being that the system virtual 
				tables are views into the SQL Server's protected memory space 
				rather than views based on tables stored in a database on disk. 
				It's somewhat amusing to contemplate - and undeniably true - 
				that when the SQL Server process is not running there is no data 
				in sysprocesses or sysperfinfo. Conversely, those 23 rows in 
				pubs.dbo.authors are there whether the SQL Server is running or 
				not. It's perplexing to note that sysperfinfo is not listed on 
				the Books Online "Monitoring Tools" page. Personally I think 
				it's an awesome monitoring tool component and based on some of 
				the comments I've heard over time my suspicion is there are 
				folks on the SQL Server team at Microsoft that might agree. 
				sysperfinfo contains rows to correspond to counters from 
				these System Monitor objects: 
				  
				
					
						| 
						 object name  | 
						
						 counter name  | 
						
						 instance name  | 
					 
					
						| SQLServer:Access Methods | 
						Extent Deallocations/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Extents Allocated/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Forwarded Records/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						FreeSpace Page Fetches/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						FreeSpace Scans/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Full Scans/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Index Searches/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Mixed page allocations/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Page Deallocations/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Page Splits/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Pages Allocated/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Probe Scans/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Range Scans/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Scan Point Revalidations/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Skipped Ghosted Records/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Table Lock Escalations/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Workfiles Created/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Worktables Created/sec | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Worktables From Cache Base | 
						  | 
					 
					
						| SQLServer:Access Methods | 
						Worktables From Cache Ratio | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						AWE lookup maps/sec | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						AWE stolen maps/sec | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						AWE unmap calls/sec | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						AWE unmap pages/sec | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						AWE write maps/sec | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Buffer cache hit ratio base | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Buffer cache hit ratio | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Checkpoint pages/sec | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Database pages | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Free list stalls/sec | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Free pages | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Lazy writes/sec | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Page life expectancy | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Page lookups/sec | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Page reads/sec | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Page writes/sec | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Procedure cache pages | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Readahead pages/sec | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Reserved pages | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Stolen pages | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Target pages | 
						  | 
					 
					
						| SQLServer:Buffer Manager | 
						Total pages | 
						  | 
					 
					
						| SQLServer:Buffer Partition | 
						Free list empty/sec | 
						  | 
					 
					
						| SQLServer:Buffer Partition | 
						Free list requests/sec | 
						  | 
					 
					
						| SQLServer:Buffer Partition | 
						Free pages | 
						  | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio Base | 
						_Total | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio Base | 
						Adhoc Sql Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio Base | 
						Cursors | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio Base | 
						Execution Contexts | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio Base | 
						Misc. Normalized Trees | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio Base | 
						Prepared Sql Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio Base | 
						Procedure Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio Base | 
						Replication Procedure Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio Base | 
						Trigger Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio | 
						_Total | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio | 
						Adhoc Sql Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio | 
						Cursors | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio | 
						Execution Contexts | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio | 
						Misc. Normalized Trees | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio | 
						Prepared Sql Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio | 
						Procedure Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio | 
						Replication Procedure Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Hit Ratio | 
						Trigger Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Object Counts | 
						_Total | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Object Counts | 
						Adhoc Sql Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Object Counts | 
						Cursors | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Object Counts | 
						Execution Contexts | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Object Counts | 
						Misc. Normalized Trees | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Object Counts | 
						Prepared Sql Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Object Counts | 
						Procedure Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Object Counts | 
						Replication Procedure Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Object Counts | 
						Trigger Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Pages | 
						_Total | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Pages | 
						Adhoc Sql Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Pages | 
						Cursors | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Pages | 
						Execution Contexts | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Pages | 
						Misc. Normalized Trees | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Pages | 
						Prepared Sql Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Pages | 
						Procedure Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Pages | 
						Replication Procedure Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Pages | 
						Trigger Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Use Counts/sec | 
						_Total | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Use Counts/sec | 
						Adhoc Sql Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Use Counts/sec | 
						Cursors | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Use Counts/sec | 
						Execution Contexts | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Use Counts/sec | 
						Misc. Normalized Trees | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Use Counts/sec | 
						Prepared Sql Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Use Counts/sec | 
						Procedure Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Use Counts/sec | 
						Replication Procedure Plans | 
					 
					
						| SQLServer:Cache Manager | 
						Cache Use Counts/sec | 
						Trigger Plans | 
					 
					
						| SQLServer:Databases | 
						Active Transactions | 
						(one row per database)  | 
					 
					
						| SQLServer:Databases | 
						Backup/Restore Throughput/sec | 
						(one row per database)  | 
					 
					
						| SQLServer:Databases | 
						Bulk Copy Rows/sec | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Bulk Copy Throughput/sec | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Data File(s) Size (KB) | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						DBCC Logical Scan Bytes/sec | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Log Bytes Flushed/sec | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Log Cache Hit Ratio Base | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Log Cache Hit Ratio | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Log Cache Reads/sec | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Log File(s) Size (KB) | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Log File(s) Used Size (KB) | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Log Flush Wait Time | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Log Flush Waits/sec | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Log Flushes/sec | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Log Growths | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Log Shrinks | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Log Truncations | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Percent Log Used | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Repl. Pending Xacts | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Repl. Trans. Rate | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Shrink Data Movement Bytes/sec | 
						 (one row per database) | 
					 
					
						| SQLServer:Databases | 
						Transactions/sec | 
						 (one row per database) | 
					 
					
						| SQLServer:General Statistics | 
						Logins/sec | 
						  | 
					 
					
						| SQLServer:General Statistics | 
						Logouts/sec | 
						  | 
					 
					
						| SQLServer:General Statistics | 
						User Connections | 
						  | 
					 
					
						| SQLServer:Latches | 
						Average Latch Wait Time (ms) | 
						  | 
					 
					
						| SQLServer:Latches | 
						Average Latch Wait Time Base | 
						  | 
					 
					
						| SQLServer:Latches | 
						Latch Waits/sec | 
						  | 
					 
					
						| SQLServer:Latches | 
						Total Latch Wait Time (ms) | 
						  | 
					 
					
						| SQLServer:Locks | 
						Average Wait Time (ms) | 
						_Total | 
					 
					
						| SQLServer:Locks | 
						Average Wait Time (ms) | 
						Database | 
					 
					
						| SQLServer:Locks | 
						Average Wait Time (ms) | 
						Extent | 
					 
					
						| SQLServer:Locks | 
						Average Wait Time (ms) | 
						Key | 
					 
					
						| SQLServer:Locks | 
						Average Wait Time (ms) | 
						Page | 
					 
					
						| SQLServer:Locks | 
						Average Wait Time (ms) | 
						RID | 
					 
					
						| SQLServer:Locks | 
						Average Wait Time (ms) | 
						Table | 
					 
					
						| SQLServer:Locks | 
						Average Wait Time Base | 
						_Total | 
					 
					
						| SQLServer:Locks | 
						Average Wait Time Base | 
						Database | 
					 
					
						| SQLServer:Locks | 
						Average Wait Time Base | 
						Extent | 
					 
					
						| SQLServer:Locks | 
						Average Wait Time Base | 
						Key | 
					 
					
						| SQLServer:Locks | 
						Average Wait Time Base | 
						Page | 
					 
					
						| SQLServer:Locks | 
						Average Wait Time Base | 
						RID | 
					 
					
						| SQLServer:Locks | 
						Average Wait Time Base | 
						Table | 
					 
					
						| SQLServer:Locks | 
						Lock Requests/sec | 
						_Total | 
					 
					
						| SQLServer:Locks | 
						Lock Requests/sec | 
						Database | 
					 
					
						| SQLServer:Locks | 
						Lock Requests/sec | 
						Extent | 
					 
					
						| SQLServer:Locks | 
						Lock Requests/sec | 
						Key | 
					 
					
						| SQLServer:Locks | 
						Lock Requests/sec | 
						Page | 
					 
					
						| SQLServer:Locks | 
						Lock Requests/sec | 
						RID | 
					 
					
						| SQLServer:Locks | 
						Lock Requests/sec | 
						Table | 
					 
					
						| SQLServer:Locks | 
						Lock Timeouts/sec | 
						_Total | 
					 
					
						| SQLServer:Locks | 
						Lock Timeouts/sec | 
						Database | 
					 
					
						| SQLServer:Locks | 
						Lock Timeouts/sec | 
						Extent | 
					 
					
						| SQLServer:Locks | 
						Lock Timeouts/sec | 
						Key | 
					 
					
						| SQLServer:Locks | 
						Lock Timeouts/sec | 
						Page | 
					 
					
						| SQLServer:Locks | 
						Lock Timeouts/sec | 
						RID | 
					 
					
						| SQLServer:Locks | 
						Lock Timeouts/sec | 
						Table | 
					 
					
						| SQLServer:Locks | 
						Lock Wait Time (ms) | 
						_Total | 
					 
					
						| SQLServer:Locks | 
						Lock Wait Time (ms) | 
						Database | 
					 
					
						| SQLServer:Locks | 
						Lock Wait Time (ms) | 
						Extent | 
					 
					
						| SQLServer:Locks | 
						Lock Wait Time (ms) | 
						Key | 
					 
					
						| SQLServer:Locks | 
						Lock Wait Time (ms) | 
						Page | 
					 
					
						| SQLServer:Locks | 
						Lock Wait Time (ms) | 
						RID | 
					 
					
						| SQLServer:Locks | 
						Lock Wait Time (ms) | 
						Table | 
					 
					
						| SQLServer:Locks | 
						Lock Waits/sec | 
						_Total | 
					 
					
						| SQLServer:Locks | 
						Lock Waits/sec | 
						Database | 
					 
					
						| SQLServer:Locks | 
						Lock Waits/sec | 
						Extent | 
					 
					
						| SQLServer:Locks | 
						Lock Waits/sec | 
						Key | 
					 
					
						| SQLServer:Locks | 
						Lock Waits/sec | 
						Page | 
					 
					
						| SQLServer:Locks | 
						Lock Waits/sec | 
						RID | 
					 
					
						| SQLServer:Locks | 
						Lock Waits/sec | 
						Table | 
					 
					
						| SQLServer:Locks | 
						Number of Deadlocks/sec | 
						_Total | 
					 
					
						| SQLServer:Locks | 
						Number of Deadlocks/sec | 
						Database | 
					 
					
						| SQLServer:Locks | 
						Number of Deadlocks/sec | 
						Extent | 
					 
					
						| SQLServer:Locks | 
						Number of Deadlocks/sec | 
						Key | 
					 
					
						| SQLServer:Locks | 
						Number of Deadlocks/sec | 
						Page | 
					 
					
						| SQLServer:Locks | 
						Number of Deadlocks/sec | 
						RID | 
					 
					
						| SQLServer:Locks | 
						Number of Deadlocks/sec | 
						Table | 
					 
					
						| SQLServer:Memory Manager | 
						Connection Memory (KB) | 
						  | 
					 
					
						| SQLServer:Memory Manager | 
						Granted Workspace Memory (KB) | 
						  | 
					 
					
						| SQLServer:Memory Manager | 
						Lock Blocks Allocated | 
						  | 
					 
					
						| SQLServer:Memory Manager | 
						Lock Blocks | 
						  | 
					 
					
						| SQLServer:Memory Manager | 
						Lock Memory (KB) | 
						  | 
					 
					
						| SQLServer:Memory Manager | 
						Lock Owner Blocks Allocated | 
						  | 
					 
					
						| SQLServer:Memory Manager | 
						Lock Owner Blocks | 
						  | 
					 
					
						| SQLServer:Memory Manager | 
						Maximum Workspace Memory (KB) | 
						  | 
					 
					
						| SQLServer:Memory Manager | 
						Memory Grants Outstanding | 
						  | 
					 
					
						| SQLServer:Memory Manager | 
						Memory Grants Pending | 
						  | 
					 
					
						| SQLServer:Memory Manager | 
						Optimizer Memory (KB) | 
						  | 
					 
					
						| SQLServer:Memory Manager | 
						SQL Cache Memory (KB) | 
						  | 
					 
					
						| SQLServer:Memory Manager | 
						Target Server Memory(KB) | 
						  | 
					 
					
						| SQLServer:Memory Manager | 
						Total Server Memory (KB) | 
						  | 
					 
					
						| SQLServer:SQL Statistics | 
						Auto-Param Attempts/sec | 
						  | 
					 
					
						| SQLServer:SQL Statistics | 
						Batch Requests/sec | 
						  | 
					 
					
						| SQLServer:SQL Statistics | 
						Failed Auto-Params/sec | 
						  | 
					 
					
						| SQLServer:SQL Statistics | 
						Safe Auto-Params/sec | 
						  | 
					 
					
						| SQLServer:SQL Statistics | 
						SQL Compilations/sec | 
						  | 
					 
					
						| SQLServer:SQL Statistics | 
						SQL Re-Compilations/sec | 
						  | 
					 
					
						| SQLServer:SQL Statistics | 
						Unsafe Auto-Params/sec | 
						  | 
					 
					
						| SQLServer:User Settable | 
						Query | 
						User counter 1 | 
					 
					
						| SQLServer:User Settable | 
						Query | 
						User counter 10 | 
					 
					
						| SQLServer:User Settable | 
						Query | 
						User counter 2 | 
					 
					
						| SQLServer:User Settable | 
						Query | 
						User counter 3 | 
					 
					
						| SQLServer:User Settable | 
						Query | 
						User counter 4 | 
					 
					
						| SQLServer:User Settable | 
						Query | 
						User counter 5 | 
					 
					
						| SQLServer:User Settable | 
						Query | 
						User counter 6 | 
					 
					
						| SQLServer:User Settable | 
						Query | 
						User counter 7 | 
					 
					
						| SQLServer:User Settable | 
						Query | 
						User counter 8 | 
					 
					
						| SQLServer:User Settable | 
						Query | 
						User counter 9 | 
					 
				 
 
				The data in sysperfinfo represents a real time value. With 
				only a little effort you can easily capture periodic snapshots 
				of the data you deem interesting. There are a few different data 
				capture methods necessary in order to assure proper 
				interpretation of the data collected. Some metrics such as the 
				"Data File(s) Size (KB)" or "Log File(s) Used Size (KB)" 
				counters of the database object provide a point in time value. 
				The value represent the actual size of the data files or the 
				actual amount of the log now in use. If you capture these values 
				it's a pretty straight forward query to determine the file 
				growth patterns.  
				If you refer to the  sysperfinfo 
				scripts you can review the table schema and 
				stored procedures I use to capture sysperfinfo data and a few 
				T-SQL examples of how I use that data. It's as easy to render 
				the results through your favorite charting tool as it is T-SQL 
				and often provides a much more intuitive view for those non-DBA 
				types we all have to deal with from time to time. 
				For example, a query of the collected data can show you when 
				data files grew and allow you to plot a growth trend: 
				
					  
				select DbName, cast(max(SampleDate) as varchar(11)) as [Latest Date at this size],  
				        DataFileSize_KB, 
				        LogFileSize_KB 
				from admin.dbo.DbPerfHistory 
				where DbName like @DbName + '%' 
				group by DbName, DataFileSize_KB, LogFileSize_KB 
				order by case DBName when '_Total' then 1 else 0 end, DbName, max(SampleDate) desc 
				
					  
				Other metrics are accumulated values and increase over time 
				such as the "Transactions/sec" counter of the Database 
				performance object or the "Readahead pages/sec" of the Buffer 
				Manager object. These values require that you know the elapsed 
				time between samples in order to produce a meaningful metric 
				from the collected data. In general the algorithm for this type 
				of metric is: 
				    (current counter value - previous counter value) / 
				DateDiff(second, previous sample datetime, this sample datetime) 
				; 
				
					where second can be replaced with another time division 
					when appropriate. When you are using a metric that is 
					described as value/sec you're probably best to stay with 
					seconds as your time division. 
				 
				You could pre-calculate such values at insert time such as in 
				this example taken from the admin.dbo.GetPerfHistory procedure 
				included in the
				
				sysperfinfo scripts: 
				
					  
				update d 
				set LastTranRateSample = t.TxnPerSec, 
				    LastSampleDt = @CurDt,      
				    CurTxnPerSec = case when ((t.TxnPerSec - d.LastTranRateSample) >= 0) 
				                        then (t.TxnPerSec - d.LastTranRateSample)  
				                             / DateDiff(s, d.LastSampleDt, @CurDt) 
				                        else (t.TxnPerSec) / DateDiff(s, d.LastSampleDt, @CurDt)  
				                        end, -- handle a counter reset  
				    MinTxnPerSec = case when ((t.TxnPerSec - d.LastTranRateSample)  
				                              / DateDiff(s,d.LastSampleDt,@CurDt) < d.MinTxnPerSec)   
				                       then case when ((t.TxnPerSec - d.LastTranRateSample) >= 0) 
				                                 then (t.TxnPerSec - d.LastTranRateSample)  
				                                      / DateDiff(s,d.LastSampleDt,@CurDt)  
				                                 else (t.TxnPerSec) / DateDiff(s,d.LastSampleDt,@CurDt)  
				                                 end 
				                       else d.MinTxnPerSec  
				                       end, 
				    MinTxnDt = case when ((t.TxnPerSec - d.LastTranRateSample)  
				                           / DateDiff(s,d.LastSampleDt,@CurDt) < d.MinTxnPerSec)  
				                    then @CurDt  
				                    else d.MinTxnDt  
				                    end,     
				    MaxTxnPerSec = case when ((t.TxnPerSec - d.LastTranRateSample)  
				                                   / DateDiff(s,d.LastSampleDt,@CurDt) > d.MaxTxnPerSec)  
				                    then case when ((t.TxnPerSec - d.LastTranRateSample) >= 0) 
				                              then (t.TxnPerSec - d.LastTranRateSample)  
				                                   / DateDiff(s,d.LastSampleDt,@CurDt)  
				                              else (t.TxnPerSec) / DateDiff(s,d.LastSampleDt,@CurDt)  
				                              end 
				                    else d.MaxTxnPerSec  
				                    end, 
				    MaxTxnDt = case when ((t.TxnPerSec - d.LastTranRateSample) 
				                           /DateDiff(s,d.LastSampleDt,@CurDt) > d.MaxTxnPerSec)  
				                    then @CurDt  
				                    else d.MaxTxnDt  
				                    end,             
				    TransActivityDetected = case when (t.TxnPerSec - d.LastTranRateSample) = 0 
				                                 then TransActivityDetected 
				                                 else TransActivityDetected  + 1 
				                                 end 
				from @t t 
				inner join admin.dbo.DbPerfHistory d  
				on t.DbName = d.DbName 
				where SampleDate = @today                     -- work on today's record 
				and (   LastSampleDt < @CurDt)                -- unless inserted this time 
				
					  
				And then to query the collected data thusly to show you the 
				relative throughput patterns over time: 
				
					  
				select p1.dbName, 
				       Datepart(yyyy, p1.SampleDate) as year,  
				        Datepart(mm, p1.SampleDate) as month, 
				        cast(avg(p1.MinTxnPerSec) as numeric(12,2)) as [AvgDailyMinTxn/Sec],  
				        cast(avg(p1.MaxTxnPerSec) as numeric(12,2)) as [AvgDailyMaxTxn/Sec],  
				        avg(cast(cast(p1.TransActivityDetected as numeric(12,2)) 
				            /cast(p2.TransActivityDetected as numeric(12,2)) * 100 as int)) 
              as [Avg%Active/Day]
				from admin.dbo.dbperfhistory p1 
				inner join admin.dbo.dbperfhistory p2 
				on p1.SampleDate = p2.SampleDate 
				where Datepart(d, p1.SampleDate) = 1 
				and p2.DbName = '_Total' 
				and p1.dbName like @DbName + '%' -- allows query to be for all or for a specified db 
				group by p1.dbName,Datepart(yyyy, p1.SampleDate),Datepart(mm, p1.SampleDate) 
				order by case p1.DBName when '_Total' then 1 else 0 end,  
				         p1.dbname,  
				         Datepart(yyyy, p1.SampleDate) desc,Datepart(mm, p1.SampleDate) desc 
				
					  
				Or , if it makes more sense to the way you want to use the 
				data, you could do the calculation within the trend analysis 
				query such as this query to compute the batch request/second 
				trend from a once a day sample table: 
				
					  
				select p1.SampleDate 
				        , (p1.BatchRequestPerSec - p2.BatchRequestPerSec) 
				           /(24*60*60) [BatchRequest/Sec]-- seconds in a day 
				from admin.dbo.SrvPerfHistory p1 
				inner join admin.dbo.SrvPerfHistory p2 
				on p1.SampleDate = p2.SampleDate + 1 
				order by p1.SampleDate Desc 
				
					  
				Lastly there is the data that includes a column for the the 
				current value and a base value such as the "Average Wait Time(ms)" 
				counter of the Locks object or "Cache Hit Ratio" of the Cache 
				Manager object. For example to insert a meaningful Buffer Cache 
				Hit Ratio into a trend capture table you would need to compute 
				the ratio of the 'Buffer Cache Hit Ratio' rows data value and 
				the 'Buffer Cache Hit Ratio Base' rows data value in sysperfinfo: 
				
					  
				insert admin.dbo.srvperfhistory (BufferCacheHitRatio) 
				select cast(p1.cntr_value as numeric(12,2))
            / cast(p2.cntr_value as numeric(12,2))
				from master.dbo.sysperfinfo p1 
				inner join master.dbo.sysperfinfo p2 
				on p1.object_name = p2.object_name 
				where p1.object_name = 'SQLServer:Buffer Manager' 
				and p1.counter_name = 'Buffer cache hit ratio' 
				and p2.counter_name = 'Buffer cache hit ratio base'   
				and p1.instance_name = p2.instance_name 
				
					  
				Alternately you could simply record the counter value and the 
				base value in the data capture table and compute the ratio at 
				query time. 
				With that assortment of techniques you can easily define a 
				regularly scheduled task that at it's first execution will 
				provide you with a baseline for your SQL Server and at each 
				subsequent execution will refine the behavioral trend 
				documentation for your SQL Server and customize the reporting 
				queries to meet the needs of your organization.  
				I'm not trying to spew dogma here as a SQL Darwinist might, 
				I'm just trying to give you adequate engineering tools to meet 
				the needs of your organization. The technique I use is to 
				execute the the admin.dbo.GetPerfHistory procedure you can 
				download from the
				
				scripts provided in the
				
				swwug.org Script Library to complement the sysperfinfo 
				discussion in this article every half hour from a SQL Agent job. 
				This gives me a new record each night at midnight and then 
				throughout the day I can update such things as the min and max 
				transactions per second so I can make a determination as to when 
				during the day a server is working the hardest (and when it's 
				not working so hard which is useful information when you are 
				trying to establish a maintenance window for those ugly delete 
				archive processes or to reorg your indexes.  
				 ::fn_virtualfilestats 
				For all practical purposes you can get performance and 
				throughput details at the database file level through the System 
				Statistical Function fn_virtualfilestats using the very same 
				techniques I have outlined above for the system virtual table 
				sysperfinfo. It's actually a rather limited set of metrics and 
				because you need to provide the function with a database id and 
				a file id some special tactics are necessary when collection 
				data. 
				I certainly don't want to give the idea that 
				fn_virtulafilestats - or sysperfinfo for that matter - are only 
				useful for occasional data collection. These data sources can 
				provide powerful troubleshooting information when your are in 
				the thick of a problem. I strongly encourage you to become 
				familiar with fn_virtualfilestats and add it to your tool belt. 
				It has some great information.   
				For baseline and trend analysis I suggest a stored procedure 
				that uses sp_msforeachdb to iterate through the databases and 
				then captures a row for each file in each database once a day. 
				You may want to start with the
				
				stored procedure I have posted. You can schedule it once a day in 
				the SQL Agent or you could consider adding it as a job step in 
				the job suggested above to capture sysperfinfo data. There are 
				only 5 data points for each file (Reads, Writes, Read Bytes, 
				Write Bytes and Stalls) so you end up with a fairly narrow but 
				meaty row.  
				With these T-SQL based resources for establishing baselines 
				and identifying performance and throughput trends, there really 
				is little reason for anyone to have to resort to 20th century 
				approaches like SQL Darwinism to assure proper scale and 
				hardware class for a SQL Server database in 2004. Deciding what 
				to include in your benchmarking and trend capture collection is 
				again another science. Each application will likely require a 
				different set of data to properly do the job. For more details 
				on selecting counters and how they can be used see the 
				"Monitoring with System Monitor" chapter of SQL Server Books 
				Online, Ken Henderson's awesome The Guru's Guide to SQL Server 
				Architecture and Internals, Brian Kelly's very practical e-book 
				Start to finish Guide to SQL Server Performance Tuning and the 
				performance monitor tips at
				
				sql-server-performance.com (look toward the bottom of the 
				home page for the tips index), and don't overlook the incredible 
				group of unbelievable smart, savvy and generous folks on the SQL 
				Server Worldwide User Groups list server at
				
				SQL Server 2000 discussions as you define the correct 
				baseline and trend data points for your organization. 
				There are two scripts referenced above that you may want to 
				investigate for additional details for this article: 
				
				
				admin db - baseline and trends using sysperfinfo 
				 
				use admin
GO
SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS ON 
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[GetPerfHistory]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[GetPerfHistory]
GO
Create proc dbo.GetPerfHistory
as
/*******************************************************************************************************
*	admin.dbo.GetPerfHistory 
*	Creator: Bill W	
*	Outline: query the system table sysperfinfo daily to capture: 
*			DB file size
*			Log File size
*			Log File size used
* 			Table Scans
*			Data Cache Hit Ratio
*			Procedure Cache Hit Ratio	
*		query sysperfinfo throughout the day to capture a daily distribution: 
*			transaction/sec 	
*			
*	How it Works:	The procedure is run from the SQL Agent scheduler on a regular interval.   
*			
*	usage:
		EXECUTE admin.dbo.GetPerfHistory 
*	Notes:		Query this table to determine the avg, max and min transactions per second by Date
*			use daily deltas to determine change trends
*	Modifications   
*
********************************************************************************************************/
declare @today datetime, 
	@CurDt datetime
declare @t table (DbName varchar(30) primary key,
		  TxnPerSec numeric(16,6))	
set nocount on
-- get a timestamp for now and midnight
set @CurDt = getDate()
set @today = cast(@CurDt as varchar(11))
-- don't do anything if wrong database
if db_name() <> 'admin'
	begin
		raiserror ('This utility may only be installed in the admin database!',16,1)
		return -1
	end
-- init the tables if first time on this server
if (object_id('dbo.DbPerfHistory','U') is null)
	create table dbo.DbPerfHistory (DbName varchar(30) NOT NULL, 
				SampleDate datetime NOT NULL,
				DataFileSize_KB int NULL,
				LogFileSize_KB int NULL,
				LogFileSizeUsed_KB int NULL,
				FirstTranRateSample int NULL,
				LastTranRateSample int NULL, 
				CurTxnPerSec numeric(16,6) NULL,
				MinTxnPerSec numeric(16,6) NULL,  
				MinTxnDt datetime NULL,  
				MaxTxnPerSec numeric(16,6) NULL, 
				MaxTxnDt datetime NULL, 
				TransActivityDetected int NULL,
				FirstSampleDt datetime NULL,	
				LastSampleDt datetime NULL,
				constraint pk_DbPerfHistory__SampleDate__DbName primary key
				(SampleDate, DbName))
			
		
if (object_id('dbo.SrvPerfHistory','U') is null)
	create table dbo.SrvPerfHistory (SampleDate datetime NOT NULL,
				TableScans numeric(16,6) NULL,
                                BatchRequestPerSec numeric(16,6) NULL,
				DataCacheHitRatio numeric(12,2) NULL,
				DataCacheHitRatioBase numeric(12,2) NULL,
				ProcCacheHitRatio numeric(12,2) NULL,
				ProcCacheHitRatioBase numeric(12,2) NULL,
				constraint pk_SrvPerfHistory__SampleDate primary key
				(SampleDate))
-- can add other values that require intraday range tracking here
insert @t (DbName, TxnPerSec)
select 	substring(instance_name,1,30), 
		cast(cntr_value as numeric(16,6))
	from master.dbo.sysperfinfo
	where counter_name= 'Transactions/sec'
-- if no rows for today, init a row
insert admin.dbo.DbPerfHistory (DbName, 
			FirstTranRateSample,
			FirstSampleDt,
			LastTranRateSample,
			LastSampleDt,
			CurTxnPerSec,
			MinTxnPerSec,
			MinTxnDt,
			MaxTxnPerSec,
			MaxTxnDt,
			TransActivityDetected,
			SampleDate)
select DbName,
	t.txnPerSec,
	@CurDt,
	t.txnPerSec,
	@CurDt,
	999999999.999999,
	999999999.999999,
	@CurDt,
	0,
	@CurDt,
	0,
	@today
from @t t
where not exists(select 1 from admin.dbo.DbPerfHistory
			where dbName = t.DbName
			and SampleDate = @today)
if not exists (select 1 from admin.dbo.SrvPerfHistory
			where SampleDate = @today)
	begin
		-- if buffer cache hit ratio getting smaller and scans increasing, new queries not using indexes
		insert admin.dbo.SrvPerfHistory (SampleDate, TableScans) 
		select @today, p.cntr_value 
		from master.dbo.sysperfinfo p
		where p.object_name = 'SQLServer:Access Methods'
		and p.counter_name = 'Full Scans/sec'
		-- as falls away from 100% more memory can help
		--select cast(cast(p2.cntr_value as numeric(14,4))/cast(p1.cntr_value as numeric(14,4)) as numeric(6,4)) * 100
		-- calc daily delta to plot over time (elim days where counter rolls over or restarts)
		update h
			set DataCacheHitRatio = p1.cntr_value,
		 	    DataCacheHitRatioBase = p2.cntr_value
		from master.dbo.sysperfinfo p1
		inner join master.dbo.sysperfinfo p2
		on p1.object_name = p2.object_name
		and p1.instance_name = p2.instance_name
		cross join admin.dbo.SrvPerfHistory h
		where p1.object_name = 'SQLServer:Buffer Manager'
		and p1.counter_name = 'Buffer cache hit ratio'
		and p2.counter_name = 'Buffer cache hit ratio base'  
		and h.SampleDate = @today   	   	-- work on today's record
		-- lower value may mean high dynamic sql use
		--select cast(cast(p1.cntr_value as numeric(14,4))/cast(p2.cntr_value as numeric(14,4)) as numeric(6,4)) * 100
		-- calc daily delta to plot over time (elim days where counter rolls over or restarts)
		update h
			set ProcCacheHitRatio = p1.cntr_value,
		 	    ProcCacheHitRatioBase = p2.cntr_value
		from master.dbo.sysperfinfo p1
		inner join master.dbo.sysperfinfo p2
		on p1.object_name = p2.object_name
		and p1.instance_name = p2.instance_name
		cross join admin.dbo.SrvPerfHistory h
		where p1.object_name = 'SQLServer:Cache Manager'
		and p1.instance_name = 'Prepared Sql Plans' 
		and p1.counter_name = 'Cache Hit Ratio'
		and p2.counter_name = 'Cache Hit Ratio Base'  
		and h.SampleDate = @today   	   	-- work on today's record
		-- use batch request per second as a general indicater of read/write activity over time 
		-- calc daily delta to plot over time (elim days where counter rolls over or restarts)
		update h 
			set BatchRequestPerSec = p.cntr_value 
		from master.dbo.sysperfinfo p
		cross join admin.dbo.SrvPerfHistory h 
		where p.object_name = 'SQLServer:SQL Statistics'
		and p.counter_name = 'Batch Requests/sec'
		and h.SampleDate > = @today   	   	-- work on today's record
	end
if exists (select 1 from admin.dbo.DbPerfHistory
			where SampleDate = @today
			and FirstSampleDt = @CurDt)
	begin
		update h 
			set DataFileSize_KB = p.cntr_value 
		from master.dbo.sysperfinfo p
		inner join admin.dbo.DbPerfHistory h
		on p.instance_name = h.DbName
		where p.counter_name = 'Data File(s) Size (KB)'
		and h.SampleDate = @today   	   	-- work on today's record
		and h.FirstSampleDt = @CurDt 		-- only if inserted this time
		update h
			set LogFileSize_KB = p.cntr_value 
		from master.dbo.sysperfinfo p
		inner join admin.dbo.DbPerfHistory h
		on p.instance_name = h.DbName
		where p.counter_name = 'Log File(s) Size (KB)'
		and h.SampleDate = @today   	   	-- work on today's record
		and h.FirstSampleDt = @CurDt 		-- only if inserted this time
		update h 
			set LogFileSizeUsed_KB = p.cntr_value 
		from master.dbo.sysperfinfo p
		inner join admin.dbo.DbPerfHistory h
		on p.instance_name = h.DbName
		where p.counter_name = 'Log File(s) Used Size (KB)'
		and h.SampleDate = @today   	   	-- work on today's record
		and h.FirstSampleDt = @CurDt 		-- only if inserted this time
	end
else
	-- if already rows for today, do tran rate aggregations
	-- use 0 as LastTranRateSample if counter has been reset
	-- used values returned in perfmon to determine calculations
	update d
	set LastTranRateSample = t.TxnPerSec,
	    LastSampleDt = @CurDt,	
	    CurTxnPerSec = case when ((t.TxnPerSec - d.LastTranRateSample) >= 0)
				then (t.TxnPerSec - d.LastTranRateSample) / DateDiff(s, d.LastSampleDt, @CurDt)
				else (t.TxnPerSec) / DateDiff(s, d.LastSampleDt, @CurDt) 
			end, -- handle a counter reset	
	    MinTxnPerSec = case when ((t.TxnPerSec - d.LastTranRateSample) / DateDiff(s,d.LastSampleDt,@CurDt) < d.MinTxnPerSec)  
				then case when ((t.TxnPerSec - d.LastTranRateSample) >= 0)
						then (t.TxnPerSec - d.LastTranRateSample) / DateDiff(s,d.LastSampleDt,@CurDt) 
						else (t.TxnPerSec) / DateDiff(s,d.LastSampleDt,@CurDt) 
				     end
				else d.MinTxnPerSec 
			   end,
	    MinTxnDt = case when ((t.TxnPerSec - d.LastTranRateSample) / DateDiff(s,d.LastSampleDt,@CurDt) < d.MinTxnPerSec) 
				then @CurDt 
				else d.MinTxnDt 
			       end,	
	    MaxTxnPerSec = case when ((t.TxnPerSec - d.LastTranRateSample) / DateDiff(s,d.LastSampleDt,@CurDt) > d.MaxTxnPerSec) 
				then case when ((t.TxnPerSec - d.LastTranRateSample) >= 0)
						then (t.TxnPerSec - d.LastTranRateSample) / DateDiff(s,d.LastSampleDt,@CurDt) 
						else (t.TxnPerSec) / DateDiff(s,d.LastSampleDt,@CurDt) 
				     end
				else d.MaxTxnPerSec 
			   end,
	    MaxTxnDt = case when ((t.TxnPerSec - d.LastTranRateSample)/DateDiff(s,d.LastSampleDt,@CurDt) > d.MaxTxnPerSec) 
				then @CurDt 
				else d.MaxTxnDt 
		       end,		
	    TransActivityDetected = case when (t.TxnPerSec - d.LastTranRateSample) = 0
					then TransActivityDetected
					else TransActivityDetected  + 1
					end
	from @t t
	inner join admin.dbo.DbPerfHistory d 
	on t.DbName = d.DbName
	where SampleDate = @today   		   	-- work on today's record
	and (   LastSampleDt < @CurDt) 		-- unless inserted this time
return
GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO
use admin
GO
SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS ON 
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[DbActivityTrend]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[DbActivityTrend]
GO
CREATE PROCEDURE [dbo].[DbActivityTrend] 
	@DbName varchar(30) = ''
	  
AS
/*******************************************************************************************************
*	admin.dbo.DbActivityTrend 
*	Creator:	Bill Wunder
*	Outline:	Query the stored samples from master..sysperfinfo
*			to display the transaction rate history for a database
*			
*			
*	usage:
		EXECUTE admin.dbo.DbActivityTrend customer
*			
*	Modifications   
*         developer name     date     brief description
*         ------------------ -------- ------------------------------------------------------------
*
********************************************************************************************************/
-- declare variables
-- set session
SET NOCOUNT ON
-- create temp tables
-- body of stored procedure
select p1.dbName,
	cast((cast(p1.LastTranRateSample as numeric(12,2)) - cast(p1.FirstTranRateSample as numeric(12,2)))/cast(datediff(s, p1.FirstSampleDt, p1.LastSampleDt) as numeric(12,2)) as numeric(12,2)) as avgTxnPerSec, 
	cast(p1.MinTxnPerSec as numeric(12,2)) as MinTxnPerSec, 
	cast(p1.MaxTxnPerSec as numeric(12,2)) as MaxTxnPerSec, 
	cast(cast(p1.TransActivityDetected as numeric(12,2))/cast(p2.TransActivityDetected as numeric(12,2)) * 100 as int) as PctActive,
	convert(varchar(21),p1.SampleDate,102) as [Date],
	datepart(dw, p1.SampleDate) as [Day of Week]
from admin.dbo.dbperfhistory p1
inner join admin.dbo.dbperfhistory p2
on p1.SampleDate = p2.SampleDate
where p1.DbName like @dbName + '%' 
and p2.DbName = '_Total'
order by p1.dbname, p1.sampledate
RETURN
GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO
use admin
GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[DbLogSizeTrend]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[DbLogSizeTrend]
GO
CREATE PROCEDURE [dbo].[DbLogSizeTrend] 
	@DbName sysname = null
	  
AS
/*******************************************************************************************************
*	admin.dbo.dbLogSizeTrend 
*	Creator:	Bill Wunder
*	Outline:	show log space over time
*	usage:
		EXECUTE admin.dbo.dbLogSizeTrend 
*			
*	Modifications   
*         developer name     date     brief description
*         ------------------ -------- ------------------------------------------------------------
*
********************************************************************************************************/
-- declare variables
-- create temp tables
-- set session
SET NOCOUNT ON
-- body of stored procedure
if @DbName is null -- all
	select DbName, cast(max(SampleDate) as varchar(11)) as [Latest Date at this size], LogFileSize_KB, LogFileSizeUsed_KB, datepart(dw, SampleDate) as [weekday]
	from admin.dbo.DbPerfHistory
	group by DbName, LogFileSize_KB, LogFileSizeUsed_KB, datepart(dw, SampleDate)
	order by DbName, max(SampleDate)
else -- only one db
	select Dbname, cast(max(SampleDate) as varchar(11)) as [Latest date at this size], LogFileSize_KB, LogFileSizeUsed_KB,  datepart(dw, SampleDate) as [weekday]
	from admin.dbo.DbPerfHistory
	where DbName = @DbName
	group by DbName, LogFileSize_KB , LogFileSizeUsed_KB, datepart(dw, SampleDate)
	order by max(SampleDate)
RETURN
GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO
use admin
GO
SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS ON 
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[DbSizeTrend]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[DbSizeTrend]
GO
CREATE PROCEDURE [dbo].[DbSizeTrend] 
	@DbName sysname = null
	  
AS
/*******************************************************************************************************
*	admin.dbo.dbSizeTrend 
*	Creator:	Bill Wunder
*	Outline:	show data space over time
*			does not include log space
*			
*			
*	usage:
		EXECUTE admin.dbo.dbSizeTrend 
*			
*	Modifications   
*         developer name     date     brief description
*         ------------------ -------- ------------------------------------------------------------
*
********************************************************************************************************/
-- declare variables
-- create temp tables
-- set session
SET NOCOUNT ON
-- body of stored procedure
if @DbName is null -- all
	select DbName, max(SampleDate) as [Latest Date at this size], DataFileSize_KB
	from admin.dbo.DbPerfHistory
	group by DbName, DataFileSize_KB 
	order by DbName, max(SampleDate)
else -- only one db
	select Dbname, max(SampleDate) as [Latest date at this size], DataFileSize_KB
	from admin.dbo.DbPerfHistory
	where DbName = @DbName
	group by DbName, DataFileSize_KB 
	order by max(SampleDate)
RETURN
GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
use admin
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON 
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[GetSrvActivityTrend]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[GetSrvActivityTrend]
GO
CREATE PROCEDURE [dbo].[GetSrvActivityTrend] 
	@DbName sysname = null
	  
AS
/*******************************************************************************************************
*	admin.dbo.GetSrvActivityTrend 
*	Creator:	Bill Wunder
*	Outline:	show server activity over time
*	usage:
		EXECUTE admin.dbo.GetSrvActivityTrend 
*			
*	Modifications   
*         developer name     date     brief description
*         ------------------ -------- ------------------------------------------------------------
*
********************************************************************************************************/
-- declare variables
-- create temp tables
-- set session
SET NOCOUNT ON
-- body of stored procedure
select p1.SampleDate
	, (p1.TableScans - p2.TableScans)/(24*60*60) [TableScans/Sec]-- seconds in a day	
	, (p1.BatchRequestPerSec - p2.BatchRequestPerSec)/(24*60*60) [BatchRequest/Sec]-- seconds in a day
	, p1.DataCacheHitRatio/p1.DataCacheHitRatioBase [Data Cache Hit Ratio]
	, p1.ProcCacheHitRatio/p1.ProcCacheHitRatioBase [Procedure Cache Hit Ratio]
from admin.dbo.SrvPerfHistory p1
inner join admin.dbo.SrvPerfHistory p2
on p1.SampleDate = p2.SampleDate + 1
order by p1.SampleDate Desc
RETURN
GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO
				 
				
				
				admin db - db file performance baseline and trends using ::fn_virtualfilestats 
 
use admin
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON 
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[GetFileStats]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[GetFileStats]
GO
create procedure dbo.GetFileStats
/*******************************************************************************************************
*	dbo.GetFileStats.PRC
*	Creator:       Bill Wunder
*
*	Description:   create a trend history of ::fn_virtualfilestats output
*	Notes:         TimeStamp - Time at which the data was taken 
*                NumberReads - Number of reads issued on the file 
*                NumberWrites - Number of writes made on the file 
*                BytesRead - Number of bytes read issued on the file 
*                BytesWritten - Number of bytes written made on the file 
*                IoStallMS - Total amount of time, in milliseconds, that users 
*                            waited for the I/Os to complete on the file 
*
*
*	Usage:
	     EXECUTE admin.dbo.GetFileStats
*
*
*	Modifications:   
*          Developer Name     Date     Brief Description
*          ------------------ -------- ------------------------------------------------------------
*                    
********************************************************************************************************/
as
set nocount on
create table #file
  ( Name varchar(128) not null
  , FileId smallint not null
  , FileName nvarchar(260) 
  , FileGroup varchar(128)
  , Size nvarchar(18)
  , MaxSize nvarchar(18)  
  , growth nvarchar(18)
  , usage varchar(9))
if object_id('admin.dbo.FileStat','U') is null
  create table admin.dbo.FileStat
    (DbName varchar(128) not null
    , FileName varchar(128) not null
    , RecCreatedDt datetime not null
      constraint dft_FileStats__RecCreatedDt
      default getdate()
    , PathName nvarchar(260)
    , FileGroup varchar(128)
    , Size nvarchar(18)
    , usage varchar(9)
    , TimeStamp int
    , NumberReads bigint 
    , NumberWrites bigint 
    , BytesRead bigint 
    , BytesWritten bigint 
    , IoStallMS bigint
    , constraint pkc_FileStats__DbName__FileName__RecCreatedDt
      Primary key (DbName, FileName, RecCreatedDt))
  
exec sp_msforeachdb 'use ?
declare @dbid smallint
  , @fileid smallint
set @dbid = db_id()
truncate table #file
insert #file
  (name
  , fileid
  , filename
  , filegroup
  , size
  , maxsize
  , growth
  , usage)
exec sp_helpfile
select @fileid = min(FileId) 
from #file
while @Fileid is not null
  begin
 
   insert admin.dbo.FileStat
      (DbName
      , FileName
      , PathName
      , FileGroup
      , Size
      , usage
      , TimeStamp
      , NumberReads
      , NumberWrites
      , BytesRead
      , BytesWritten
      , IoStallMS)
    SELECT db_name(@dbid)
        , file_name(@fileid)
        , filename
        , filegroup
        , size
        , usage
        , TimeStamp
        , NumberReads
        , NumberWrites
        , BytesRead
        , BytesWritten
        , IoStallMS
    FROM :: fn_virtualfilestats(@dbid,@fileid) fn
    join #file f
    on fn.fileid = f.fileid
 
    select @fileid = min(FileId) 
    from #file
    where fileid > @fileid
  end'
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON 
GO
use [admin]
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON 
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[FileStatDailyTrend]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[FileStatDailyTrend]
GO
CREATE PROCEDURE [dbo].[FileStatDailyTrend] 
	@DbName varchar(128) = null   -- all
  , @FileGroup varchar(128) = null -- all
	, @BeginDate datetime = 0        -- all
  , @EndDate datetime = null       -- thru today
AS
/*******************************************************************************************************
*	dbo.FileStatDailyTrend.PRC 
*	Creator:       Bill Wunder
*
*	Description:   Produce report of captured files statistics
*	Notes:         can specify by database and/or daterange
*
*	Usage:
	     EXECUTE admin.dbo.FileStatDailyTrend 'admin'
*
*
*	Modifications:   
*          Developer Name     Date     Brief Description
*          ------------------ -------- ------------------------------------------------------------
*                    
********************************************************************************************************/
SET NOCOUNT ON
create table #filestat
  (DbName varchar(128)
  , FileName varchar(128)
  , RecCreatedDt datetime
  , FileGroup varchar(128)
  , usage varchar(9)
  , NumberReads bigint
  , NumberWrites bigint
  , BytesRead bigint
  , BytesWritten bigint
  , IoStallMS bigint
  , constraint pkc_#filestat__DbName__FileName__RecCreatedDt
    primary key (DbName, Filename, RecCreatedDt))
insert #filestat
  (DbName
  , FileName
  , RecCreatedDt
  , FileGroup
  , usage
  , NumberReads
  , NumberWrites
  , BytesRead
  , BytesWritten
  , IoStallMS)
select 
  DbName
  , FileName
  , convert(varchar(10),RecCreatedDt,102)
  , FileGroup
  , usage
  , min(NumberReads)
  , min(NumberWrites)
  , min(BytesRead)
  , min(BytesWritten)
  , min(IoStallMS)
from admin.dbo.FileStat
where BytesRead > BytesRead -- skip bad datapoint due to restart
and DbName like isnull(@DbName,'%')
and FileGroup like isnull(@FileGroup,'%')
and (RecCreatedDt >= @BeginDate) or @BeginDate is null
and (RecCreatedDt <= @EndDate) or @EndDate is null
group by DbName
  , FileName
  , convert(varchar(10),RecCreatedDt,102)
  , FileGroup
  , usage
order by DbName
  , FileName
  , convert(varchar(10),RecCreatedDt,102)
  , FileGroup
  , usage
select f2.DbName
  , f2.FileName
  , f2.FileGroup
  , f2.usage  
  , (f2.NumberReads - f1.NumberReads)/datediff(minute,f1.RecCreatedDt, f2.RecCreatedDt) [Reads/Min] 
  , (f2.NumberWrites - f1.NumberWrites)/datediff(minute,f1.RecCreatedDt, f2.RecCreatedDt) [Writes/Min]  
  , (f2.BytesRead - f1.BytesRead)/datediff(minute,f1.RecCreatedDt, f2.RecCreatedDt) [Bytes Read/Min] 
  , (f2.BytesWritten - f1.BytesWritten)/datediff(minute,f1.RecCreatedDt, f2.RecCreatedDt) [Bytes Written/Min]  
  , (f2.IoStallMS - f1.IoStallMS) * 1000 * 60/datediff(minute,f1.RecCreatedDt, f2.RecCreatedDt) [% Wait IO] 
from #filestat f1
join #filestat f2
on f1.DbName = f2.DbName
and f1.FileName = F2.FileName
and convert(varchar(10),f1.RecCreatedDt,102) = convert(varchar(10),f2.RecCreatedDt - 1,102)
RETURN
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON 
GO
Bill |