Report to Capture Table Growth Statistics for SQL Server

By:   |   Comments (23)   |   Related: > Monitoring


Problem

I would like to monitor table size growth and build a report that will help me monitor table growth. Is there any way I can automate this without buying a third party tool?

Solution

There are a few tools that monitor table size growth, but with the use of sp_spaceused and SQL Server Reporting Services (SSRS) you can make custom reports.

Using sp_spaceused

The system stored procedure sp_spaceused displays the number of rows, disk space reserved, and disk space used by a table, indexed view, or Service Broker queue in the current database in an object is provided or it displays the disk space reserved and used by the whole database.

This procedure, sp_spaceused can be used at the database or table level. For example, sp_spaceused at the database level returns the following:

sp_spaceused at the database level returns the following

And sp_spaceused at the table level displays:

Sp_spaceused executed at the table level displays

Creating the Report

To create our custom report, first we will need to create a table that will store the data from sp_spaceused. For this example, I'll create a table named TableSizeGrowth.

CREATE TABLE [dbo].[TableSizeGrowth](
[id] [int] IDENTITY(1,1) NOT NULL,
[table_schema] [nvarchar](256) NULL,
[table_name] [nvarchar](256) NULL,
[table_rows] [int] NULL,
[reserved_space] [int] NULL,
[data_space] [int] NULL,
[index_space] [int] NULL,
[unused_space] [int] NULL,
[date] [datetime] NULL
) ON [PRIMARY]
    

I'm also going to add a default constraint that will automatically update the date column to the current date for each new row.

ALTER TABLE [dbo].[TableSizeGrowth] ADD CONSTRAINT [DF_TableSizeGrowth_date]  
DEFAULT (dateadd(day,(0),datediff(day,(0),getdate()))) FOR [date]
GO

Next, we need to create a stored procedure that moves the data from sp_spaceused to the table we created above. I have put comments throughout the stored procedure to help you figure out what is going on. Also make sure you change the table names to match your tables. For this example, I want to monitor HumanResources.Employee, Production.Product, and Purchasing.Vendor tables.

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[sp_TableSizeGrowth] 
AS
BEGIN
 SET NOCOUNT ON
 
 --DECLARE VARIABLES
 DECLARE
 @max INT,
 @min INT,
 @table_name NVARCHAR(256),
 @table_schema NVARCHAR(256),
 @sql NVARCHAR(4000)
 
 --DECLARE TABLE VARIABLE
 DECLARE @table TABLE(
 id INT IDENTITY(1,1) PRIMARY KEY,
 table_name NVARCHAR(256),
 table_schema NVARCHAR(256))
 
 --CREATE TEMP TABLE THAT STORES INFORMATION FROM SP_SPACEUSED
 IF (SELECT OBJECT_ID('tempdb..#results')) IS NOT NULL
 BEGIN
  DROP TABLE #results
 END
 
 CREATE TABLE #results
 (
  [table_schema] [nvarchar](256) NULL,
  [table_name] [nvarchar](256) NULL,
  [table_rows] [int] NULL,
  [reserved_space] [nvarchar](55) NULL,
  [data_space] [nvarchar](55) NULL,
  [index_space] [nvarchar](55) NULL,
  [unused_space] [nvarchar](55) NULL
 )
 
 --LOOP THROUGH STATISTICS FOR EACH TABLE
 INSERT @table(table_schema, table_name)
 SELECT  
  table_schema, table_name
 FROM
  information_schema.tables 
 WHERE table_schema + '.' + table_name IN ('HumanResources.Employee','Production.Product', 'Purchasing.Vendor') --INSERT TABLE NAMES TO MONITOR
 
 SELECT
  @min = 1,
  @max = (SELECT MAX(id) FROM @table)
 
 WHILE @min < @max + 1
 BEGIN
  SELECT 
   @table_name = table_name,
   @table_schema = table_schema
  FROM
   @table
  WHERE
   id = @min
   
  --DYNAMIC SQL
  SELECT @sql = 'EXEC sp_spaceused ''[' + @table_schema + '].[' + @table_name + ']'''
  
  --INSERT RESULTS FROM SP_SPACEUSED TO TEMP TABLE
  INSERT #results(table_name, table_rows, reserved_space, data_space, index_space, unused_space)
  EXEC (@sql)
  
  --UPDATE SCHEMA NAME
  UPDATE #results
  SET table_schema = @table_schema
  WHERE table_name = @table_name
  SELECT @min = @min + 1
 END
 
 --REMOVE "KB" FROM RESULTS FOR REPORTING (GRAPH) PURPOSES
 UPDATE #results SET data_space = SUBSTRING(data_space, 1, (LEN(data_space)-3))
 UPDATE #results SET reserved_space = SUBSTRING(reserved_space, 1, (LEN(reserved_space)-3))
 UPDATE #results SET index_space = SUBSTRING(index_space, 1, (LEN(index_space)-3))
 UPDATE #results SET unused_space = SUBSTRING(unused_space, 1, (LEN(unused_space)-3))
 
 --INSERT RESULTS INTO TABLESIZEGROWTH
 INSERT INTO TableSizeGrowth (table_schema, table_name, table_rows, reserved_space, data_space, index_space, unused_space)
 SELECT * FROM #results
 
 DROP TABLE #results
END

Now that the stored procedure has been created, let's go ahead and execute it.

Once the stored procedure finishes we can view the table:

view the table

As you can see above our table is now populated with our three tables that we need to monitor as well as the number of rows in each table, reserved space, data space, index space, unused space, and date.

To create a report we need to open Business Intelligence Studio (SQL 2008 and below) or SQL Server Data Tools (SQL 2012). Choose File, New Project. On the New Project dialog choose Report Server Project Wizard and name the project:

open Business Intelligence Studio (SQL 2008 and below) or SQL Server Data Tools (SQL 2012)

Next, create a data source. For this example, I'll use localhost:

use localhost

The next screen is where you will build the query that you want to use in your report. For this example, I only want to monitor number of rows and data space so I'll add them to my SELECT statement along with table name and date and click Next:

add them to my SELECT statement

Next, I'll choose a Matrix report type:

 choose Matrix type

To design the Matrix, I'll choose the following:

design the Matrix

Choose a Matrix style and name the report and click Finish.

Once the report opens in Design view we can choose Preview to see how it looks:

the report opens in Design view we can choose Preview

Looks good, right? But why stop there? Let's add this data to a chart. Open up the Toolbox and drag the Chart control into the Design view:

Open up the Toolbox and drag the Chart control into the Design view

For this example, I'm going to use the Line with Markers chart type:

use the Line with Markers chart type

Once the chart is added to the design view we can make a few customizations such as resizing it and renaming some of the Titles:

we can make a few customizations such as resizing it and renaming some of the Titles

Next, when you click inside the chart, the Chart Data box should appear. Here is where we can define our values for the chart. I'll use the following for this example:

he Chart Data box should appear

If we click Preview again, we now see the data in our chart matches the data in our table:

the data in our chart matches the data in our table

Scheduling Data Collection and Final Review

I'll schedule my stored procedure to execute nightly using a SQL Server Agent Job. When I come into work the next day I notice that data has been added to my tables since the last stored procedure execution:

schedule my stored procedure to execute nightly using a SQL Server Agent Job
Next Steps
  • You can experiment with different chart types to determine what works best for you.
  • To see more tips on SSRS click here
  • Click here to download all of the components for this tip
  • In my example I hardcoded the tables to monitor, but you could also create another table and make this table driven or capture this data for all tables.


sql server categories

sql server webinars

subscribe to mssqltips

sql server tutorials

sql server white papers

next tip



About the author
MSSQLTips author Brady Upton Brady Upton is a Database Administrator and SharePoint superstar in Nashville, TN.

This author pledges the content of this article is based on professional experience and not AI generated.

View all my tips



Comments For This Article




Monday, August 15, 2016 - 12:46:10 PM - Russ Mittler Back To Top (43121)

 Hello - not working for me either, any updates? 

 


Thursday, June 9, 2016 - 5:27:02 PM - kevin Back To Top (41655)

 Hi Robert

 

the issue really resides on this line of code:

 --LOOP THROUGH STATISTICS FOR EACH TABLE
 INSERT @table(table_schema, table_name)
 SELECT 
  table_schema, table_name
 FROM
  information_schema.tables

 

that is because the information_schema.tables is only given public rights and select priviledges on the master database. So when an attempt is made to execute against another database the following error is noticed:

Msg 208, Level 16, State 1, Line 1
Invalid object name 'information_schema.tables'.

 

 

 

 

 


Monday, February 23, 2015 - 8:58:03 AM - dan Back To Top (36317)

Hi , I tried to create table [TableSizeGrowth] on different database other than AdventureWorks and using 

('AdventureWorks.HumanResources.Employee','AdventureWorks.Production.Product', 'AdventureWorks.Purchasing.Vendor') , it's not working!!! what can I insert those table information on Different database?


Wednesday, December 17, 2014 - 2:22:20 PM - Robert Back To Top (35645)

Hi Greg,

 

There is no result with that query :(


Wednesday, December 17, 2014 - 9:54:17 AM - Greg Robidoux Back To Top (35637)

Hi Robert,

After you create the SP and then execute the SP the data should be stored in the table.

Run this command to see the results:

SELECT * FROM dbo.TableSizeGrowth

 

 


Tuesday, December 16, 2014 - 6:27:47 PM - Robert Back To Top (35626)

Thanks for answering Greg,

And No, this isn't working for me, should i've passed something? I mean I Execute sp_TableSizeGrowth and says "Command(s) completed successfully.", after this i should wait until i see some data on TableSizeGrowth Table right?

Did I miss something? thanks :)

 


Tuesday, December 16, 2014 - 4:50:47 PM - Greg Robidoux Back To Top (35625)

Hi Robert,

I think what you have is correct.  Did this work for you?


Tuesday, December 16, 2014 - 4:03:22 PM - Robert Back To Top (35623)

Hi Greg,

yes as far I know i change that to following code:

WHERE table_schema + '.' + table_name IN ('dbo.Cycles','dbo.Times') --INSERT TABLE NAMES TO MONITOR

  SELECT

  @min = 1,

  @max = (SELECT MAX(id) FROM @table)

 WHILE @min < @max+1

 

Is this ok?


Tuesday, December 16, 2014 - 2:46:24 PM - Greg Robidoux Back To Top (35622)

Hi Robert,

there was a mistake in the script that was pointed out below, but the script was never updated.

This line

WHILE @min = @max

should be this

WHILE @min < @max + 1

 

I update the above script.

Thanks
Greg


Tuesday, December 16, 2014 - 1:38:58 PM - Robert Back To Top (35621)

Hi Greg, 

Yes I did, i change to this:

DB -> Transact

Table1-> dbo.Cycles

Table2-> dbo.Times

 

So if I understand the code line will be:

 

USE [Transact]

WHERE table_schema + '.' + table_name IN ('dbo.Cycles','dbo.Times')


Is this ok?, Thanks.

- Rob


Tuesday, December 16, 2014 - 12:14:43 PM - Greg Robidoux Back To Top (35619)

Hi Robert,

did you change this line in the SP to include the tables you want to monitor?

WHERE table_schema + '.' + table_name IN ('HumanResources.Employee','Production.Product', 'Purchasing.Vendor') --INSERT TABLE NAMES TO MONITOR

-Greg 


Tuesday, December 16, 2014 - 10:20:08 AM - Robert Back To Top (35617)

Hello everyone,

 

I'm trying this, but without a happy ending, i execute the sp__TableSizeGrowth and says completed successfully, and the table TableSizeGrowth has none data, don't really  know what's going on, so can anybody help me here?


Tuesday, May 13, 2014 - 1:29:51 PM - dab Back To Top (30759)

Can you tell me how I can get table report of 'adventurework' dabases's tables and get insert into anthother database "Growth" into 'GrowthTable"?


Friday, April 11, 2014 - 3:19:41 PM - Brady Back To Top (30050)

Bismarck,

Yes, you can definitely do this same report at a database level. You can use this query to find database size:

 

SELECT DB_NAME(database_id) AS DatabaseName,
name AS Logical_Name,
(size*8)/1024 SizeMB
FROM sys.master_files



Friday, April 11, 2014 - 10:32:06 AM - Bismarck Back To Top (30047)

Great Article I can put this to use.  One question,  Could something similar be done for the Database as a whole?  I would like to monitor the growth of my SharePoint content databases like this.

Bismarck


Monday, November 18, 2013 - 11:45:34 AM - PolishPaul Back To Top (27532)

The formatting in my last comment was malformed. It should read:

  •  WHILE @min <= @max
Wish there was a preview option for the comments.

Monday, November 18, 2013 - 11:43:29 AM - PolishPaul Back To Top (27531)

Hi, I found a bug in the stored procedure which was causing the problem i described in the last post.

In the stored procedure, there's a comparison:

WHILE @min = @max

But it should be:

WHILE @min 

This caused any result larger than 1 to be ignored. In order to process more than 1 table, I had to make this change.


Friday, November 15, 2013 - 5:07:01 PM - PolishPaul Back To Top (27514)

I've got the SP to work, however I'm trying to automatically get all the table names instead of specifying them in the WHERE IN clause and its not working. I added a select * from @table to see if its returning the expected results and it is.

I changed the SP simply by commenting out the where clause:

WHERE table_schema + '.' + table_name IN ('HumanResources.Employee','Production.Product', 'Purchasing.Vendor') --INSERT TABLE NAMES TO MONITOR

This doesn't work and nothing is inserted into the table. Any ideas or suggestions? 

What I'm trying to accomplish is to have one stored procedure like this that i can use on various DB's without altering the code.

 


Friday, November 1, 2013 - 9:00:28 AM - ESL Back To Top (27360)

works fine thank you.

i have an issue with a database containing multiple tables  > 100 , the chart is not readable ,

how can i do to split chart by table 

 


Tuesday, November 13, 2012 - 7:23:04 PM - Sasi Back To Top (20325)

Made a few modifications to the code (excluded the schema part of it) and it worked fine for me. Thanks for the wonderful work.


Tuesday, November 13, 2012 - 6:22:20 PM - ND Back To Top (20324)

Very good stor proc.

There is small mistype: Replace WHILE @min = @max with WHILE @min < @max+1


Tuesday, November 13, 2012 - 5:31:42 PM - Shane Back To Top (20322)

This worked fine for me. Remember Sasi, to execute the procedure before selecting from the table. (EXECUTE sp_TableSizeGrowth)

GREAT WORK BRADY! This will be a very handy tool to have!

 

 

 


Tuesday, November 13, 2012 - 4:09:18 PM - Sasi Back To Top (20317)

The stored proc does nothing. I dont see the result when I query the table.















get free sql tips
agree to terms