Click here to monitor SSC
SQLServerCentral is supported by Red Gate Software Ltd.
 
Log in  ::  Register  ::  Not logged in
 
 
 

Richard Douglas

Richard Douglas is a SQL Server Professional working in the UK where he recently worked as a DBA in women's clothing, not literally he hastens to add! He is certified in SQL Server 2008, runs the Maidenhead SQL Server User Group Pass Chapter and is on the organising committee for SQL Relay. In his spare time plays the trumpet in local symphony orchestras.

His online presence includes:

T-SQL Tuesday #025 – How to view the size of all your databases – #TSQL2sDay

T-SQLLogo

It’s that time of the month again, no not time, T-SQL time! If you have not heard of T-SQL Tuesday before it was set up by Adam Machanic (Blog|Twitter) and you can read all about it here: T-SQL Tuesday. This months host is Allen White who asked the question “What T-SQL tricks do you use today to make your job easier?” I’m guessing that people will be going mad with DMV’s so I’m going to go down a slightly different route. I must admit I haven’t used it recently due to changing jobs which has meant a change of duties, but it’s just too good not to share.

At some point in your career you’ll be asked by somebody “so just how big is the database?” At this point you have a couple of choices, you can look at the properties of the database or you could even use the system stored procedure sp_spaceused. The problem I found with this sproc was it returned data in two result sets making storing the data more complicated than it actually needs to be, as you can see from the image below.

image

I decided to find out if there was an easier way to collate the data for all databases on an instance and indeed there was. The following script uses the internals of sp_spaceused which I have tweaked to return a single result set and is called from within the most documented of undocumented commands sp_msforeachdb the results are written to a temporary table. A good way to use this script is to schedule it within a job to write to a permanent table that can then be used to monitor database growth.

/*
Script written by Richard Douglas
HTTP://SQL.RichardDouglas.co.uk

Script will insert data about the sizes of all databases on the instance into a predetermined table.
This can be used as a snapshot or made into a job to chart the growth of databases over time.

Please note that the DATA_COMPRESSION functionality was introduced in SQL 2008.
If you're running on SQL Server 2005 comment out the use of DATA_COMPRESSION WHEN creating storage table. */ USE TempDB GO /* Create a table to store results. */ IF NOT EXISTS (SELECT 1 FROM sys.Objects WHERE Name like '#DatabaseGrowth%' AND OBJECT_SCHEMA_NAME(object_id) = 'dbo' ) BEGIN CREATE TABLE [dbo].[#DatabaseGrowth] ( [Database_Name] [char](128) NOT NULL ,[Database_Size_MB] DECIMAL(15,2) NOT NULL ,[Unallocated_Space_MB] DECIMAL(15,2) NOT NULL ,[Reserved_MB] DECIMAL(15,2) NOT NULL ,[Data_MB] BIGINT NOT NULL ,[Index_Size_MB] BIGINT NULL ,[Unused_MB] BIGINT NOT NULL ,[DateTimeStamp] DATETIME NOT NULL DEFAULT GETDATE() ) ON [PRIMARY] WITH (DATA_COMPRESSION = PAGE) END TRUNCATE TABLE [dbo].[#DatabaseGrowth] /* Insert the data into storage table */ INSERT INTO [dbo].[#DatabaseGrowth] ([Database_Name] ,[Database_Size_MB] ,[Unallocated_Space_MB] ,[Reserved_MB] ,[Data_MB] ,[Index_Size_MB] ,[Unused_MB]) EXEC sp_MSforeachdb 'DECLARE
    @pages    BIGINT            -- Working variable for size calc.
    ,@dbname SYSNAME
    ,@dbsize BIGINT
    ,@logsize BIGINT
    ,@reservedpages  BIGINT
    ,@usedpages  BIGINT
    ,@rowCount BIGINT

SET NOCOUNT ON

    SELECT
         @dbsize = SUM(CONVERT(BIGINT,CASE WHEN STATUS & 64 = 0 THEN Size ELSE 0 END))
        ,@logsize = SUM(CONVERT(BIGINT,CASE WHEN STATUS & 64 <> 0 THEN Size ELSE 0 END))
        FROM [?].dbo.sysfiles

    SELECT
         @reservedpages = SUM(a.total_pages)
        ,@usedpages = SUM(a.used_pages)
        ,@pages = SUM(
                CASE
                    /* XML-Index and FT-Index internal tables are not considered "data", but is part of "index_size" */
                    WHEN it.internal_type IN (202,204,211,212,213,214,215,216) THEN 0
                    WHEN a.type <> 1 THEN a.used_pages
                    WHEN p.index_id < 2 THEN a.data_pages
                    ELSE 0
                END
            )
    FROM [?].sys.partitions p
    JOIN [?].sys.allocation_units a ON p.partition_id = a.container_id
    LEFT JOIN [?].sys.internal_tables it on p.object_id = it.object_id

    /* unallocated space could not be negative */
    SELECT
         database_name = ''?''
        ,database_size = (@dbsize + @logsize) * 8192 / 1048576
        ,''unallocated space'' = LTRIM(STR((CASE WHEN @dbsize >= @reservedpages THEN (CONVERT (dec (15,2),@dbsize) - CONVERT (DEC (15,2),@reservedpages)) * 8192 / 1048576 ELSE 0 END),15,2))
        ,reserved = LTRIM(STR((@reservedpages * 8192 / 1024.)/1024,15,0))
        ,data = LTRIM(STR((@pages * 8192 / 1024.)/1024,15,0))
        ,index_size = LTRIM(STR(((@usedpages - @pages) * 8192 / 1024.)/1024,15,0))
        ,unused = LTRIM(STR(((@reservedpages - @usedpages) * 8192 / 1024.)/1024,15,0))'

/*Show data*/

SELECT
 Database_Name
,Database_Size_MB
,Unallocated_Space_MB
,Reserved_MB
,Data_MB
,Index_Size_MB
,Unused_MB
,DateTimeStamp
FROM [dbo].[#DatabaseGrowth]

 

Thanks for hosting Allen.

Comments

Leave a comment on the original post [sql.richarddouglas.co.uk, opens in a new window]

Loading comments...