Using the SQL function fn_virtualfilestats, you can retrieve cumulative IO information for each database since SQL server started. The following T-SQL script creates a multi-function sproc that when scheduled via SQL Agent job, will periodically save a copy of current fn_virtualfilestats and calculate the difference between the last stored file stats.
The following describes the usage information:
FileStats - Archived fn_virtualfilestats
Usage: FileStats @option , [ @db_id | @db_name ], [@start [, @end]]
@option - Action to perform
log - Adds current file stats to the archive
log_db - Adds current file stats for specified
@db_name or @db_id only
exclude - Adds @db_name or @db_id to exclusion
list. If @db_name/@db_id are not
specified, current exclusions will be
displayed.
include - Removes @db_name or @db_id from the
exclusion list. By default all databases
are included.
bydb - Shows total stats grouped by db_name
optional totals over time ranges
between @start to @end
bytime - Shows stat grouped by time and db optionaly
between @start to @end
bystat - Shows for each stat, optionaly
between @start to @end
delete - Deletes all entries in the stats table
optionally, only those entries that have
a date older than @start
@db_name - Optional database name
@db_id - Optional system database ID
@start - Optional datetime for filtering results
or for deleting older stats
@end - Optional datetime for filtering results
Scheduling a SQL agent job to run the following, however often you want to update the stats:
EXEC FileStats @option = 'log'
To prune off old data from the FileStatsData table, run the following shows deleting all entries prior to 12/1/2004:
EXEC FileStats @option = 'delete', @start = '12/1/2004'
Databases can also be excluded from any of the results commands using the following:
EXEC FileStats @option = 'exclude', @db_name = 'master'
The script was created using Query Analyzer's template parameters so the database, stored procedure and the two tables that are all created can be named as needed. Open the script in QA and press Ctrl+Shift+M to bring up the replace dialog to specify all of the object names.
/************************************************************************ * File Stats - Archived fn_virtualfilestats * <Stats_Proc,sysname,FileStats> - Name of that stored proc * <Data_Table,sysname,FileStatsData> - Name of the results table * <Exclude_Table,sysname,FileStatsExcludes> - Table of db's to exclude ************************************************************************/ IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = '<Stats_Proc,sysname,FileStats>') DROP PROCEDURE <Stats_Proc,sysname,FileStats> GO CREATE PROCEDURE <Stats_Proc,sysname,FileStats>( @option varchar(255) = NULL, @db_id smallint = NULL, @db_name sysname = NULL, @start datetime = NULL, @end datetime = NULL ) AS -- Make sure tables exist IF (@option != 'create_tables') EXEC <Stats_Proc,sysname,FileStats> @option = 'create_tables' IF (@option IS NULL) -- usage BEGIN PRINT '<Stats_Proc,sysname,FileStats> - Archived fn_virtualfilestats' PRINT 'Usage: <Stats_Proc,sysname,FileStats> @option , [ @db_id | @db_name ], [@start [, @end]]' PRINT CHAR(9) + '@option - Action to perform' PRINT CHAR(9) + ' log - Adds current file stats to the archive' PRINT CHAR(9) + ' log_db - Adds current file stats for specified' PRINT CHAR(9) + ' @db_name or @db_id only' PRINT CHAR(9) + ' exclude - Adds @db_name or @db_id to exclusion' PRINT CHAR(9) + ' list. If @db_name/@db_id are not' PRINT CHAR(9) + ' specified, current exclusions will be' PRINT CHAR(9) + ' displayed.' PRINT CHAR(9) + ' include - Removes @db_name or @db_id from the' PRINT CHAR(9) + ' exclusion list. By default all databases' PRINT CHAR(9) + ' are included.' PRINT CHAR(9) + ' bydb - Shows total stats grouped by db_name' PRINT CHAR(9) + ' optional totals over time ranges' PRINT CHAR(9) + ' between @start to @end' PRINT CHAR(9) + ' bytime - Shows stat grouped by time and db optionaly' PRINT CHAR(9) + ' between @start to @end' PRINT CHAR(9) + ' bystat - Shows for each stat, optionaly' PRINT CHAR(9) + ' between @start to @end' PRINT CHAR(9) + ' delete - Deletes all entries in the stats table' PRINT CHAR(9) + ' optionally, only those entries that have' PRINT CHAR(9) + ' a date older than @start' PRINT CHAR(9) + '@db_name - Optional database name' PRINT CHAR(9) + '@db_id - Optional system database ID' PRINT CHAR(9) + '@start - Optional datetime for filtering results' PRINT CHAR(9) + ' or for deleting older stats' PRINT CHAR(9) + '@end - Optional datetime for filtering results' RETURN END IF (@option = 'avg') -- Returns entries averaged over time BEGIN IF (@start IS NOT NULL AND @end IS NULL) SET @end = GETDATE() IF (DB_ID(@db_name) IS NOT NULL) SET @db_id = DB_ID(@db_name) SELECT DB_Name(DbId) AS DBName , SUM(DeltaNumberReads) / CAST(SUM(DeltaTimeStamp / 1000) AS float) AS NumberReadsPerSec , SUM(DeltaNumberWrites) / CAST(SUM(DeltaTimeStamp / 1000) AS float) AS NumberWritesPerSec , SUM(DeltaBytesRead) / CAST(SUM(DeltaTimeStamp / 1000) AS float) AS BytesReadPerSec , SUM(DeltaBytesWritten) / CAST(SUM(DeltaTimeStamp / 1000) AS float) AS BytesWrittenPerSec , SUM(DeltaIOStallMS) / CAST(SUM(DeltaTimeStamp / 1000) AS float) AS IOStallMSPerSec FROM <Data_Table,sysname,FileStatsData> WHERE DbId NOT IN (SELECT db_id FROM <Exclude_Table,sysname,FileStatsExcludes>) AND (@start IS NULL OR Added BETWEEN @start AND @end) AND (@db_id IS NULL OR DbId = @db_id) AND DeltaNumberReads IS NOT NULL -- Skip First entries GROUP BY DB_Name(DbId) ORDER BY DB_Name(DbId) RETURN END IF (@option = 'bytime') -- Returns entries that are grouped by time and group BEGIN IF (@start IS NOT NULL AND @end IS NULL) SET @end = GETDATE() IF (DB_ID(@db_name) IS NOT NULL) SET @db_id = DB_ID(@db_name) SELECT DB_Name(DbId) AS DBName , Added , SUM(DeltaNumberReads) AS NumberReads , SUM(DeltaNumberWrites) AS NumberWrites , SUM(DeltaBytesRead) AS BytesRead , SUM(DeltaBytesWritten) AS BytesWritten , SUM(DeltaIOStallMS) AS IOStallMS FROM <Data_Table,sysname,FileStatsData> WHERE DbId NOT IN (SELECT db_id FROM <Exclude_Table,sysname,FileStatsExcludes>) AND (@start IS NULL OR Added BETWEEN @start AND @end) AND (@db_id IS NULL OR DbId = @db_id) AND DeltaNumberReads IS NOT NULL -- First entries GROUP BY DB_Name(DbId), Added ORDER BY Added DESC, DB_Name(DbId) RETURN END IF (@option = 'bydb') -- Returns grouped by db BEGIN IF (@start IS NOT NULL AND @end IS NULL) SET @end = GETDATE() IF (DB_ID(@db_name) IS NOT NULL) SET @db_id = DB_ID(@db_name) SELECT DB_Name(DbId) AS DBName , SUM(DeltaNumberReads) AS NumberReads , SUM(DeltaNumberWrites) AS NumberWrites , SUM(DeltaBytesRead) AS BytesRead , SUM(DeltaBytesWritten) AS BytesWritten , SUM(DeltaIOStallMS) AS IOStallMS FROM <Data_Table,sysname,FileStatsData> WHERE DbId NOT IN (SELECT db_id FROM <Exclude_Table,sysname,FileStatsExcludes>) AND (@start IS NULL OR Added BETWEEN @start AND @end) AND (@db_id IS NULL OR DbId = @db_id) AND DeltaNumberReads IS NOT NULL -- First entries GROUP BY DB_Name(DbId) ORDER BY DB_Name(DbId) RETURN END IF (@option = 'bystat') -- Returns for each stat in descending order BEGIN IF (@start IS NOT NULL AND @end IS NULL) SET @end = GETDATE() IF (DB_ID(@db_name) IS NOT NULL) SET @db_id = DB_ID(@db_name) SELECT SUM(DeltaNumberReads) AS NumberReads, DB_Name(DbId) AS DBName FROM <Data_Table,sysname,FileStatsData> WHERE DbId NOT IN (SELECT db_id FROM <Exclude_Table,sysname,FileStatsExcludes>) AND (@start IS NULL OR Added BETWEEN @start AND @end) AND (@db_id IS NULL OR DbId = @db_id) GROUP BY DB_Name(DbId) ORDER BY SUM(DeltaNumberReads) DESC SELECT SUM(DeltaNumberWrites) AS NumberWrites, DB_Name(DbId) AS DBName FROM <Data_Table,sysname,FileStatsData> WHERE DbId NOT IN (SELECT db_id FROM <Exclude_Table,sysname,FileStatsExcludes>) AND (@start IS NULL OR Added BETWEEN @start AND @end) AND (@db_id IS NULL OR DbId = @db_id) GROUP BY DB_Name(DbId) ORDER BY SUM(DeltaNumberWrites) DESC SELECT SUM(DeltaBytesRead) AS BytesRead, DB_Name(DbId) AS DBName FROM <Data_Table,sysname,FileStatsData> WHERE DbId NOT IN (SELECT db_id FROM <Exclude_Table,sysname,FileStatsExcludes>) AND (@start IS NULL OR Added BETWEEN @start AND @end) AND (@db_id IS NULL OR DbId = @db_id) GROUP BY DB_Name(DbId) ORDER BY SUM(DeltaBytesRead) DESC SELECT SUM(DeltaBytesWritten) AS BytesWritten, DB_Name(DbId) AS DBName FROM <Data_Table,sysname,FileStatsData> WHERE DbId NOT IN (SELECT db_id FROM <Exclude_Table,sysname,FileStatsExcludes>) AND (@start IS NULL OR Added BETWEEN @start AND @end) AND (@db_id IS NULL OR DbId = @db_id) GROUP BY DB_Name(DbId) ORDER BY SUM(DeltaBytesWritten) DESC SELECT SUM(DeltaIOStallMS) AS IOStallMS, DB_Name(DbId) AS DBName FROM <Data_Table,sysname,FileStatsData> WHERE DbId NOT IN (SELECT db_id FROM <Exclude_Table,sysname,FileStatsExcludes>) AND (@start IS NULL OR Added BETWEEN @start AND @end) AND (@db_id IS NULL OR DbId = @db_id) GROUP BY DB_Name(DbId) ORDER BY SUM(DeltaIOStallMS) DESC RETURN END IF (@option = 'log' OR @option = 'log_db') -- Logs all db's or specified db to stats tbl BEGIN IF (@option = 'log_db') -- Log only specified db BEGIN IF (DB_NAME(@db_id) IS NULL AND DB_ID(@db_name) IS NULL) BEGIN PRINT 'Invalid @db_name or no @db_id specified' --RETURN END IF (@db_id IS NULL) SET @db_id = DB_ID(@db_name) IF (@db_id IS NULL) BEGIN PRINT 'Invalid @db_name or no @db_id specified' RETURN END END -- Last group of stats to calculate deltas SELECT DbId, FileId, MAX(Added) AS Added INTO #last_stat_key FROM <Data_Table,sysname,FileStatsData> WHERE (@db_id IS NULL OR @db_id = DbId) GROUP BY DbId, FileId INSERT INTO <Data_Table,sysname,FileStatsData> (DbId, FileId, TimeStamp, NumberReads, NumberWrites, BytesRead, BytesWritten, IoStallMS, Added, DeltaTimeStamp, DeltaNumberReads, DeltaNumberWrites, DeltaBytesRead, DeltaBytesWritten, DeltaIoStallMS, DeltaAdded) SELECT cur_filestats.DbId, cur_filestats.FileId, cur_filestats.TimeStamp, cur_filestats.NumberReads, cur_filestats.NumberWrites, cur_filestats.BytesRead, cur_filestats.BytesWritten, cur_filestats.IoStallMS, GETDATE() AS Added, cur_filestats.Timestamp - last_filestat.Timestamp AS DeltaTimeStamp, cur_filestats.NumberReads - last_filestat.NumberReads AS DeltaNumberReads, cur_filestats.NumberWrites - last_filestat.NumberWrites AS DeltaNumberWrites, cur_filestats.BytesRead - last_filestat.BytesRead AS DeltaBytesRead, cur_filestats.BytesWritten - last_filestat.BytesWritten AS DeltaBytesWritten, cur_filestats.IoStallMS - last_filestat.IoStallMS AS DeltaIoStallMS, DATEDIFF(ss,last_filestat.Added,GETDATE()) AS DeltaAdded FROM :: fn_virtualfilestats(-1,-1) cur_filestats LEFT OUTER JOIN #last_stat_key ON cur_filestats.DbId = #last_stat_key.DbId AND cur_filestats.FileId = #last_stat_key.FileId LEFT OUTER JOIN <Data_Table,sysname,FileStatsData> last_filestat ON last_filestat.DbId = #last_stat_key.DbId AND last_filestat.FileId = #last_stat_key.FileId AND last_filestat.Added = #last_stat_key.Added WHERE (@db_id IS NULL OR @db_id = cur_filestats.DbID) RETURN END IF (@option = 'exclude') -- Add a db exclude BEGIN IF (@db_id IS NULL) SET @db_id = DB_ID(@db_name) IF (@db_id IS NOT NULL) BEGIN IF NOT EXISTS (SELECT * FROM <Exclude_Table,sysname,FileStatsExcludes> WHERE db_id = @db_id) INSERT INTO <Exclude_Table,sysname,FileStatsExcludes> VALUES (@db_id) PRINT DB_NAME(@db_id) + ' excluded' END ELSE -- Show current excludes SELECT db_id, DB_NAME(@db_id) FROM <Exclude_Table,sysname,FileStatsExcludes> RETURN END IF (@option = 'include') -- Removes a db exclude BEGIN IF (@db_id IS NULL) SET @db_id = DB_ID(@db_name) DELETE FROM <Exclude_Table,sysname,FileStatsExcludes> WHERE db_id = @db_id PRINT DB_NAME(@db_id) + ' exclusion removed' RETURN END IF (@option = 'delete') BEGIN IF (@start IS NULL) TRUNCATE TABLE <Data_Table,sysname,FileStatsData> ELSE DELETE FROM <Data_Table,sysname,FileStatsData> WHERE Added <= @start PRINT 'Stats reset' RETURN END IF (@option = 'create_tables') -- Verifies tables exist BEGIN IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '<Data_Table,sysname,FileStatsData>') BEGIN CREATE TABLE <Data_Table,sysname,FileStatsData>( DbId smallint, FileId smallint, TimeStamp int, NumberReads bigint, NumberWrites bigint, BytesRead bigint, BytesWritten bigint, IoStallMS bigint, Added datetime, DeltaTimeStamp int NULL, DeltaNumberReads bigint NULL, DeltaNumberWrites bigint NULL, DeltaBytesRead bigint NULL, DeltaBytesWritten bigint NULL, DeltaIoStallMS bigint, DeltaAdded bigint, PRIMARY KEY (DbId, FileId, Added)) PRINT 'Created <Data_Table,sysname,FileStatsData> table' END IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '<Exclude_Table,sysname,FileStatsExcludes>') BEGIN CREATE TABLE <Exclude_Table,sysname,FileStatsExcludes>( db_id smallint ) PRINT 'Created <Exclude_Table,sysname,FileStatsExcludes> table' END RETURN END GO
| Attachment | Size |
|---|---|
| Filestats.sql | 11.74 KB |
