Technical Article

sp_spaceused2

,

An update to sp_spaceused2. This update fixes some problems related to dbs that are non-accessible such being offline. This stored proc can be run from any database when compiled in master and can report information on all databases at once. Get information on all dbs, one db, one db and all its tables or one db and one table. Use sp_spaceused2 'all' to get all dbs. Use sp_spaceused2 '?' to get back syntax information, a list of other databases on the server and a list of tables in the current db. Also, load the results periodically into a table created in msdb. There is a SQL 2K version and a SQL 70 version. Both are included below.

/*
sp_spaceused2 '?'
sp_spaceused2 'all'
sp_spaceused2 null
sp_spaceused2 null, sysobjects
sp_spaceused2 pubs, sysobjects
sp_spaceused2 pubs, 'all'
sp_spaceused2 pubs, 'user'
sp_spaceused2 pubs
sp_spaceused2 pubs, null
sp_spaceused2 null, 'all'
*/
-- For SQL 2000

if exists (select * 
             from dbo.sysobjects 
            where id = object_id('dbo.sp_spaceused2') 
              and sysstat & 0xf = 4)
   drop procedure dbo.sp_spaceused2
Go

Create procedure dbo.sp_spaceused2
       @vcDBName       sysname = null,
       @vcTable        sysname = null,
       @cUpdateUsage   char(1) = '0',
       @cUpdateRowCnt  char(1) = '0',
       @tySaveDbInfo   tinyint = 0
as

/***5***10***15***20***25***30***35***40***45***50***55***60***65***70***75**/--Name        : sp_spaceused2        for SQL 2K
--
--Description : Similiar to sp_spaceused but returns more information. Use 
--              sp_spaceused2 '?' to return the syntax and a list of dbs.
--
--Parameters  : @vcDBName      - Specified database or 'all' or null or '?'.
--              @vcTable       - Specified table in current (null) or specified 
--                               database or 'all'.
--              @cUpdateUsage  - 0 or 1; 1 to run Update Usage.
--              @cUpdateRowCnt - 0 or 1; 1 to include 'Count Rows' in Update 
--                               Usage.
--              @tySaveDbInfo  - 0 or 1; 1 stores info in msdb table.
--
--Comments    : The data returned is in bytes. The old sp_spacedused
--              returns data in computer KB (data*1024 = bytes).
--
--Date        : 06/06/2001
--Author      : Clinton Herring
--
--History     : 06/27/2001 WCH Added @tySaveDbInfo and msdb.dbo.DbSizeInfo
--                             in order to save data permanently.
--
/***5***10***15***20***25***30***35***40***45***50***55***60***65***70***75**/
Set nocount on

-- If temp table exists drop them before attempting to create them. 
If (Select object_id('tempdb.dbo.#dbData')) > 0
   Exec ('Drop table #dbData')
If (Select object_id('tempdb.dbo.#TableData')) > 0
   Exec ('Drop table #TableData')
If (Select object_id('tempdb.dbo.#logspaceinfo')) > 0
   Exec ('Drop table #logspaceinfo')
If (Select object_id('tempdb.dbo.#names')) > 0
   Exec ('Drop table #names')

-- Create some holding tables for results.
Create table #dbData(
       name sysname null,
       db_tbl_cnt int null,
       row_cnt int null,
       db_file_size decimal(13) null,
       db_space_free decimal(13) null,
       total_reserved decimal(13) null,
       data decimal(13) null,
       indexes decimal(13) null,
       unused decimal(13) null,
       log_file_size decimal(13) null,
       usable_log_space decimal(13) null,
       log_space_free dec(10,6) null,
       sort_order tinyint
       )
-- Create some holding tables for results.
Create table #TableData(
       name sysname null,
       row_count bigint null,
       reserved decimal(13) null,
       data decimal(13) null,
       indexes decimal(13) null,
       unused decimal(13) null,
       sort_order tinyint
       )
Create table #logspaceinfo(
       name sysname null,
       lspaceused dec(15,9) null,
       lspercent dec(10,6) null,
       status int null
       )
Create table #names(
       name sysname null,
       sort_order tinyint null
       )

-- Create a holding table in msdb to store db data
If @tySaveDbInfo = 1 and datepart(weekday, GetDate()) = 7
   If not exists (select * from msdb.dbo.sysobjects 
                   where name = 'DbSizeInfo' and uid = 1)
      Exec('Use msdb ' +
           'Create table DbSizeInfo( ' +
           'ServerName sysname null, ' +
           'DbName sysname null, ' +
           'DbFileSize decimal(15) null, ' +
           'DbSpaceFree decimal(15) null, ' +
           'TotalReserved decimal(15) null, ' +
           'Data decimal(15) null, ' +
           'Indexes decimal(15) null, ' +
           'Unused decimal(15) null, ' +
           'LogFileSize decimal(15) null, ' +
           'UsableLogSpace decimal(15) null, ' +
           'LogSpaceFree dec(10,6) null, ' +
           'SortOrder tinyint, ' +
           'SaveDate datetime ' +
           ') ')

-- Declare variables.
declare @iTblCnt int, 
        @dcRowCnt decimal(13), 
        @dcDBFileSpace decimal(13), 
        @dcFreeDBspace decimal(13),
        @dcReservedSpace decimal(13), 
        @dcDataSpace decimal(13), 
        @dcIndexSpace decimal(13), 
        @dcUnusedSpace decimal(13),
        @dcLogFileSpace decimal(13),
        @dcActualLogSpace decimal(13),
        @dcPercentFreeLogSpace dec(10,6),
        @iTblCnt2 int, 
        @dcRowCnt2 decimal(13), 
        @dcDBFileSpace2 decimal(13), 
        @dcFreeDBspace2 decimal(13),
        @dcReservedSpace2 decimal(13), 
        @dcDataSpace2 decimal(13), 
        @dcIndexSpace2 decimal(13), 
        @dcUnusedSpace2 decimal(13),
        @dcLogFileSpace2 decimal(13),
        @dcActualLogSpace2 decimal(13),
        @dcPercentFreeLogSpace2 dec(10,6),
        @vcBytesperPage varchar(5),
        @vcObjCnt varchar(5),
        @tiLoopRtn tinyint,
        @vcName sysname,
        @vcNote varchar(50),
        @vcCmd varchar(4000)

-- Retrieve the bytes per page value.
Select @vcBytesperPage = str(low,5) 
  from master.dbo.spt_values 
 where number = 1 and type = 'E'


-- Print help information.
If @vcDBName = '?'
   Begin
      Print 'Syntax: dbo.sp_spaceused2'
      Print '        @vcDBName      = <database name|[all]|[?]|null> -- Leave null to get info about the current'
      Print '                                                           database or use [all] to get info about'
      Print '                                                           all databases. '
      Print '        @vcTable        = <table name|[all]|[user]|null>  -- Any valid table within the specified database.'
      Print '                                                             Use [all] to get all tables for the specified database.'
      Print '                                                             Use [user] to get just user tables for the specified database.'
      Print '        @cUpdateUsage  = <0|1>  -- Set to 1 to first run Update Usage. If you see'
      Print '                                     negative results run again with this option'
      Print '                                     set to one. The default is 0.'
      Print '        @cUpdateRowCnt = <0|1>  -- Set to 1 to update row counts in sysindexes.'
      Print '        @tySaveDbInfo  = <0|1>  -- A value of 1 specifies that the results be saved'
      Print '                                     to the table msdb.dbo.DbSizeInfo.'
      Print ''
      Print 'The current database is ' + db_name() + '.'
      Print ''
      Select name [Other databases] from master.dbo.sysdatabases order by name
      Select name [Tables in the current database] from sysobjects where xtype = 'U' or xtype = 'S'
             order by xtype desc, name asc
      Return
   End

-- If @vcDBName is null grab the current db name
If @vcDBName is null 
   Begin
      Select @vcDBName = db_name()
   End

-- Check for a valid database name.
If @vcDBName <> 'all'
   Begin
      -- Is the databse name valid?
      If not exists (select * from master.dbo.sysdatabases where name = @vcDBName and
                     (status&32)|(status&64)|(status&128)|(status&256)|(status&512)|
                     (status&1024)|(status&32768)|(status&1073741824) = 0 )
         Begin
            Print '''' + @vcDBName + ''' is not a valid database on this server or is in ' +
                  'a non-accessible mode such as offline.'
            Print ''
            Select name [Valid accessible databases are...] from master.dbo.sysdatabases 
             where (status&32)|(status&64)|(status&128)|(status&256)|(status&512)|
                   (status&1024)|(status&32768)|(status&1073741824) = 0 order by name 
            Return
         End
      Else -- Check for a valid table.
         Begin
            -- Check for single user mode.
            If (Select status&4096 from master.dbo.sysdatabases where name = @vcDBName) <> 0
               Begin
                  Print 'Database ' + @vcDBName + ' is in single user mode ' +
                        'and this process requires a subordinate connection.'
                  Print 'A report cannot not be generated while the database is in single user mode.'
                  Return
               End
            
            If @vcTable is not null and @vcTable <> 'all' and @vcTable <> 'user'
               Begin
                  -- Check for an owner otherwise assume dbo
                  If charindex('.',@vcTable) = 0 
                     Set @vcTable = 'dbo.' + @vcTable
                   
                  -- SQL functions are local to the db so use dynamic SQL to test existance
                  -- and capture the results in a temp table.
                  If (Select object_id('tempdb.dbo.#CaptureResults')) > 0 
                     Exec ('Drop table #CaptureResults')
                  Create table #CaptureResults(value tinyint null)
                  Exec ('Use ' + @vcDBName + ' If exists (select * from ' + 
                         @vcDBName + '.dbo.sysobjects ' +
                        'where id = object_id(''' + @vcTable + ''')) ' +
                        'Insert into #CaptureResults values (1) ' +
                        'Else Insert into #CaptureResults values (0) ')
               
                  -- Check the results table and print a message if 0.
                  If (select value from #CaptureResults) = 0
                     Begin
                        Print 'The table ''' + @vcDBName + '.' + @vcTable +
                              ''' does not exist.'
                        Print ''
                        Exec('Select name [Tables in ' + @vcDBName + '] from  ' + 
                              @vcDBName + '.dbo.sysobjects ' +
                             'where xtype = ''U'' or xtype = ''S'' ' +
                             'order by xtype desc, name asc')
                        Return
                     End
                  Else -- I have a single table in a given database.
                     Begin
                        -- Check to see if we run UpdateUsage.
                        If @cUpdateUsage = '1'
                           Begin
                              -- Skip if the database is in read only mode or some other offline mode.
                              If (Select (status&32)|(status&64)|(status&128)|
                                         (status&256)|(status&512)|(status&1024)|
                                         (status&32768)|(status&1073741824) 
                                    from master.dbo.sysdatabases where name = @vcDBName) = 0
                                 Begin
                                    If @cUpdateRowCnt = '1'
                                       Exec ('dbcc updateusage(' + @vcDBName + ') with COUNT_ROWS,NO_INFOMSGS ')
                                    Else
                                       Exec ('dbcc updateusage(' + @vcDBName + ') with NO_INFOMSGS ')
                                 End
                              Else
                                 Begin
                                    Print 'UpdateUsage cannot be run against ' +  @vcDBName  +
                                          ' because the it is in read only mode.'
                                 End
                           End
                        Insert into #names values(@vcTable,0)
                        Print 'Database ' + @vcDBName
                        Print ''
                        Goto GetTableInfo
                     End
               End
            Else -- I have a given database but no specific table so give all tables.
               Begin
                  Set @tiLoopRtn = 2
                  Insert into #names(name) values(@vcDBName)
                  Goto GetDatabaseInfo
                  RtnToDB:
                  If @vcTable = 'all'
                     Begin
                        Print ''
                        Truncate table #names
                        Exec ('Use ' + @vcDBName + ' Insert into #names ' +
                              'Select name, case when xtype = ''U'' then 0 else 1 end ' +
                              'from sysobjects where xtype = ''U'' or xtype = ''S''')
                        Goto GetTableInfo
                     End
                  If @vcTable = 'user'
                     Begin
                        Print ''
                        Truncate table #names
                        Exec ('Use ' + @vcDBName + ' Insert into #names ' +
                              'Select name, 0 ' +
                              'from sysobjects where xtype = ''U'' ')
                        Goto GetTableInfo
                     End
               End
         End
   End
Else
   Begin
      Set @tiLoopRtn = 1
      Insert into #names(name) 
      Select name from master.dbo.sysdatabases
       where (status&32)|(status&64)|(status&128)|(status&256)|
             (status&512)|(status&32768)|(status&1073741824) = 0
      Goto GetDatabaseInfo
      --RtnToAllDbs:
   End

Return

GetDatabaseInfo:
Select @vcName = min(name) from #names
While @vcName is not null
   Begin
      -- Check for single user mode and a connection
      If (Select status&4096 from master.dbo.sysdatabases where name = @vcName) <> 0
         Begin
            Print 'Database ' + @vcName + ' is in single user mode.'
            Print 'It cannot not be included in the report at this time.'
            Print ''
            Goto NextLoopdb
         End

      -- DB File Space = sum(size) sysfiles where 0x40&status <> 0x40
      -- Reserved Space = sum(reserved) sysindexes where indid in (0,1,255)
      -- Data Space = sum(dpages) sysindexes where indid < 2 + sum(used) where indid = 255
      -- Index Space = sum(used) sysindexes where indid in (0,1,255) - @dcDataSpace
      -- Unused Space = @dcReservedSpace - sum(used) sysindexes where indid in (0,1,255)
      -- Log File Space = sum(size) sysfiles where 0x40&status = 0x40
      -- Free DB Space = @dcDBFileSpace - @dcReservedSpace
      Exec ('use ' + @vcName + ' ' +
            'If ' + @cUpdateUsage + ' = 1 Begin If (Select status&1024 from master.dbo.sysdatabases ' +
            'where name = ''' + @vcName + ''') = 0 Begin If ' + @cUpdateRowCnt + ' = 1 ' +
            'Exec (''dbcc updateusage(' + @vcName + ') with COUNT_ROWS,NO_INFOMSGS '') ' +
            'Else Exec (''dbcc updateusage(' + @vcName + ') with NO_INFOMSGS '') ' +
            'End Else Begin Print ''UpdateUsage cannot be run against database ' + @vcName + 
            ' because it is in read only mode.'' End End ' +
            'declare @dcReservedSpace decimal(13), @dcDataSpace decimal(13), ' +
            '@dcIndexSpace decimal(13), @dcUnusedSpace decimal(13), ' +
            '@dcDBFileSpace decimal(13), @dcLogFileSpace decimal(13), ' +
            '@dcFreeDBspace decimal(13), @iTblCnt int,  @iRowCnt bigint ' +

            'Select @iTblCnt = count(*) from sysobjects where type = ''U'' ' +

            'Select @iRowCnt = isnull(sum(isnull(rowcnt,0)),0) from sysindexes i, sysobjects o ' +
            'where o.id = i.id and o.type = ''U'' and i.indid in (0,1,255) ' +

            'Select @dcDBFileSpace = sum(convert(dec(15),size))*' + @vcBytesperPage + ' '+
            'from sysfiles where 0x40&status <> 0x40 ' +

            'Select @dcReservedSpace = (select sum(convert(dec(15),reserved)) ' +
   'from sysindexes where indid in (0, 1, 255))*' + @vcBytesperPage + ' ' +

            'Select @dcDataSpace = ((select sum(convert(dec(15),dpages)) ' +
   'from sysindexes where indid < 2) +  ' +
         '(select isnull(sum(convert(dec(15),used)), 0) ' +
      'from sysindexes where indid = 255))*' + @vcBytesperPage + ' ' +

            'Select @dcIndexSpace = (select sum(convert(dec(15),used)) ' +
'from sysindexes where indid in (0, 1, 255))*' + @vcBytesperPage + ' - @dcDataSpace ' +

            'Select @dcUnusedSpace = @dcReservedSpace - (select sum(convert(dec(15),used)) ' +
'from sysindexes where indid in (0, 1, 255))*' + @vcBytesperPage + ' ' +

            'Select @dcLogFileSpace = sum(convert(dec(15),size))*' + @vcBytesperPage + ' '+
            'from sysfiles where 0x40&status = 0x40 ' +

            'Select @dcFreeDBspace = @dcDBFileSpace - @dcReservedSpace ' +

            'Insert into #dbData values(''' + @vcName + 
            ''', @iTblCnt, @iRowCnt, @dcDBFileSpace, @dcFreeDBspace, @dcReservedSpace, @dcDataSpace,' +
            '@dcIndexSpace, @dcUnusedSpace, @dcLogFileSpace, 0.0, 0.0, 3) '
            ) 

      NextLoopdb:
      Select @vcName = min(name) from #names where name > @vcName
         
   End

Insert into #logspaceinfo
Exec ('DBCC SQLPERF(LOGSPACE)')

Update #dbData 
   set usable_log_space = 1000000.*lspaceused,
       log_space_free = 100.000 - lspercent
  from #logspaceinfo b where #dbData.name = rtrim(b.name)

If (Select count(*) from #names) > 1
   Begin
      Select @iTblCnt = sum(db_tbl_cnt), 
             @dcRowCnt = sum(row_cnt), 
             @dcDBFileSpace = sum(db_file_size), 
             @dcFreeDBspace = sum(db_space_free),
             @dcReservedSpace = sum(total_reserved), 
             @dcDataSpace = sum(data), 
             @dcIndexSpace = sum(indexes),
             @dcUnusedSpace = sum(unused),
             @dcLogFileSpace = sum(log_file_size), 
             @dcActualLogSpace = sum(usable_log_space),
             @dcPercentFreeLogSpace = sum(log_space_free)/count(*)
        from #dbData
      Select @iTblCnt2 = sum(db_tbl_cnt), 
             @dcRowCnt2 = sum(row_cnt), 
             @dcDBFileSpace2 = sum(db_file_size), 
             @dcFreeDBspace2 = sum(db_space_free),
             @dcReservedSpace2 = sum(total_reserved), 
             @dcDataSpace2 = sum(data), 
             @dcIndexSpace2 = sum(indexes),
             @dcUnusedSpace2 = sum(unused),
             @dcLogFileSpace2 = sum(log_file_size), 
             @dcActualLogSpace2 = sum(usable_log_space),
             @dcPercentFreeLogSpace2 = sum(log_space_free)/count(*)
        from #dbData 
       where name not in ('master','msdb','model','tempdb','northwind','pubs')
      
      Insert into #dbData 
      Values (@@servername + '(all dbs)', 
              @iTblCnt,
              @dcRowCnt,
              @dcDBFileSpace, 
              @dcFreeDBspace,
              @dcReservedSpace, 
              @dcDataSpace, 
              @dcIndexSpace, 
              @dcUnusedSpace,
              @dcLogFileSpace,
              @dcActualLogSpace,
              @dcPercentFreeLogSpace,
              1) 
      Insert into #dbData 
      Values (@@servername + '(nonsystem dbs)', 
              @iTblCnt2,
              @dcRowCnt2,
              @dcDBFileSpace2, 
              @dcFreeDBspace2,
              @dcReservedSpace2, 
              @dcDataSpace2, 
              @dcIndexSpace2, 
              @dcUnusedSpace2,
              @dcLogFileSpace2,
              @dcActualLogSpace2,
              @dcPercentFreeLogSpace2,
              2)
   End

Print ''
-- Display results only if we have any
If (select Count(*) from #dbData) > 0
   Begin
      Select convert(varchar(40), name) [Server or db], 
             str(db_tbl_cnt,4) [ # of user tbls],
             str(row_cnt,13) [   total rows],
             str(db_file_size,13) [ db_file_size],
             str(db_space_free,13) [db_space_free],
             str(total_reserved,13) [total_reserved],
             str(data,13) [   data_space],
             str(indexes,13) [  index_space],
             str(unused,13) [unused_space],
             str(log_file_size,13) [log_file_size],
             str(usable_log_space,13) [usable_log_space],
             str(log_space_free,8,4) [% log_space_free]
       from #dbData
      order by sort_order, name
      
      If (select count(*) from #names) > 1
         Begin
            Select @vcObjCnt = str(count(*),4) from master.dbo.sysdatabases
            Select @vcNote = '(' + @vcObjCnt + ' databases; numbers in bytes)'
            Print @vcNote
            Print ''
            Exec master.dbo.xp_fixeddrives
         End
      Else
         Begin
            Select @vcNote = '(numbers in bytes)'
            Print @vcNote
            Print ''
         End
      
      -- Check to see if we should save the data
      If @tySaveDbInfo = 1 and datepart(weekday, GetDate()) = 7
         Exec ('Insert into msdb.dbo.DbSizeInfo ' +
               'Select @@servername, *, getdate() ' +
               'from #dbData order by sort_order, name ')
   End

If @tiLoopRtn = 1
   Return
If @tiLoopRtn = 2
   goto RtnToDB

GetTableInfo:
Select @vcName = min(name) from #names
While @vcName is not null
   Begin
      -- Reserved Space = sum(reserved) sysindexes where indid in (0,1,255)
      -- Data Space = sum(dpages) sysindexes where indid < 2 + sum(used) where indid = 255
      -- Index Space = sum(used) sysindexes where indid in (0,1,255) - @dcDataSpace
      -- Unused Space = @dcReservedSpace - sum(used) sysindexes where indid in (0,1,255)
      -- Row count = rowcnt sysindexes where indid < 2
      Exec ('use ' + @vcDBName + ' ' +
            'declare @dcReservedSpace decimal(13), @dcDataSpace decimal(13), ' +
            '@dcIndexSpace decimal(13), @dcUnusedSpace decimal(13) ' +
         
            'Select @dcReservedSpace = (select sum(convert(dec(13),reserved)) ' +
            'from sysindexes a join sysobjects b on a.id = b.id ' + 
            'where indid in (0, 1, 255) and a.id = object_id(''' + 
             @vcName + '''))*' + @vcBytesperPage + ' ' +
         
            'Select @dcDataSpace = ((select sum(convert(dec(13),dpages)) ' +
            'from sysindexes a join sysobjects b on a.id = b.id ' + 
            'where indid < 2 and a.id = object_id(''' + 
             @vcName + ''')) +  ' +
            '(select isnull(sum(convert(dec(15),used)), 0) ' +
            'from sysindexes a join sysobjects b on a.id = b.id ' + 
            'where indid = 255 and a.id = object_id(''' + 
             @vcName + ''')))*' + @vcBytesperPage + ' ' +
         
            'Select @dcIndexSpace = (select sum(convert(dec(13),used)) ' +
         'from sysindexes a join sysobjects b on a.id = b.id ' + 
            'where indid in (0, 1, 255) and a.id = object_id(''' + 
             @vcName + '''))*' + @vcBytesperPage + 
            ' - @dcDataSpace ' +
         
            'Select @dcUnusedSpace = @dcReservedSpace - (select sum(convert(dec(13),used)) ' +
         'from sysindexes a join sysobjects b on a.id = b.id ' + 
            'where indid in (0, 1, 255) and a.id = object_id(''' + 
             @vcName + '''))*' + @vcBytesperPage + ' ' +
            
            'Insert into #TableData ' +
            'Select ''' + @vcName + ''', rowcnt, ' +
            '@dcReservedSpace, @dcDataSpace, ' +
            '@dcIndexSpace, @dcUnusedSpace,  ' +
            '(select sort_order from #names where name = ''' + @vcName + ''')' +
            'from sysindexes a join sysobjects b on a.id = b.id ' + 
            'where indid < 2 and a.id = object_id(''' + 
             @vcName + ''')' 
            )
      Select @vcName = min(name) from #names where name > @vcName
   End

Select cast(name as varchar) [Table], 
       str(row_count,13) [    Row count],
       str(reserved,13)  [     Reserved], 
       str(data,13)      [         Data],
       str(indexes,13)   [      Indexes], 
       str(unused,13)    [       Unused]
  from #TableData
 order by sort_order, name

If (select count(*) from #names) > 1
   Begin
      Select @vcObjCnt = str(count(*),4) from #names
      Select @vcNote = '(' + @vcObjCnt + ' tables; numbers in bytes)'
      Print @vcNote
      Print ''
   End
Else
   Begin
      Select @vcNote = '(numbers in bytes)'
      Print @vcNote
      Print ''
   End

Return
go

grant exec on sp_spaceused2 to public
go


-- For SQL 7.0

if exists (select * 
             from dbo.sysobjects 
            where id = object_id('dbo.sp_spaceused2') 
              and sysstat & 0xf = 4)
   drop procedure dbo.sp_spaceused2
Go

Create procedure dbo.sp_spaceused2
       @vcDBName       sysname = null,
       @vcTable        sysname = null,
       @cUpdateUsage   char(1) = '0',
       @cUpdateRowCnt  char(1) = '0',
       @tySaveDbInfo   tinyint = 0
as

/***5***10***15***20***25***30***35***40***45***50***55***60***65***70***75**/--Name        : sp_spaceused2        for SQL 7.0
--
--Description : Similiar to sp_spaceused but returns more information. Use 
--              sp_spaceused2 '?' to return the syntax and a list of dbs.
--
--Parameters  : @vcDBName      - Specified database or 'all' or null or '?'.
--              @vcTable       - Specified table in current (null) or specified 
--                               database or 'all'.
--              @cUpdateUsage  - 0 or 1; 1 to run Update Usage.
--              @cUpdateRowCnt - 0 or 1; 1 to include 'Count Rows' in Update 
--                               Usage.
--              @tySaveDbInfo  - 0 or 1; 1 stores info in msdb.dbo.DbSizeInfo.
--
--Comments    : The data returned is in bytes. The old sp_spacedused
--              returns data in computer KB (data*1024 = bytes).
--
--Date        : 06/06/2001
--Author      : Clinton Herring
--
--History     : 06/27/2001 WCH Added @tySaveDbInfo and msdb.dbo.DbSizeInfo
--                             in order to save data permanently.
--
/***5***10***15***20***25***30***35***40***45***50***55***60***65***70***75**/
Set nocount on

-- If temp table exists drop them before attempting to create them. 
If (Select object_id('tempdb.dbo.#dbData')) > 0
   Exec ('Drop table #dbData')
If (Select object_id('tempdb.dbo.#TableData')) > 0
   Exec ('Drop table #TableData')
If (Select object_id('tempdb.dbo.#logspaceinfo')) > 0
   Exec ('Drop table #logspaceinfo')
If (Select object_id('tempdb.dbo.#names')) > 0
   Exec ('Drop table #names')

-- Create some holding tables for results.
Create table #dbData(
       name sysname null,
       db_tbl_cnt int null,
       row_cnt int null,
       db_file_size decimal(13) null,
       db_space_free decimal(13) null,
       total_reserved decimal(13) null,
       data decimal(13) null,
       indexes decimal(13) null,
       unused decimal(13) null,
       log_file_size decimal(13) null,
       usable_log_space decimal(13) null,
       log_space_free dec(10,6) null,
       sort_order tinyint
       )
-- Create some holding tables for results.
Create table #TableData(
       name sysname null,
       row_count varchar(11) null,
       reserved decimal(13) null,
       data decimal(13) null,
       indexes decimal(13) null,
       unused decimal(13) null,
       sort_order tinyint
       )
Create table #logspaceinfo(
       name sysname null,
       lspaceused dec(15,9) null,
       lspercent dec(10,6) null,
       status int null
       )
Create table #names(
       name sysname null,
       sort_order tinyint null
       )

-- Create a holding table in msdb to store db data
If @tySaveDbInfo = 1 and datepart(weekday, GetDate()) = 7
   Begin
      If not exists (select * from msdb.dbo.sysobjects 
                      where name = 'DbSizeInfo' and uid = 1)
         Exec('Use msdb ' +
              'Create table DbSizeInfo( ' +
              'ServerName sysname null, ' +
              'DbName sysname null, ' +
              'DbFileSize decimal(15) null, ' +
              'DbSpaceFree decimal(15) null, ' +
              'TotalReserved decimal(15) null, ' +
              'Data decimal(15) null, ' +
              'Indexes decimal(15) null, ' +
              'Unused decimal(15) null, ' +
              'LogFileSize decimal(15) null, ' +
              'UsableLogSpace decimal(15) null, ' +
              'LogSpaceFree dec(10,6) null, ' +
              'SortOrder tinyint, ' +
              'SaveDate datetime ' +
              ') ')
   End

-- Declare variables.
declare @iTblCnt int, 
        @dcRowCnt decimal(13), 
        @dcDBFileSpace decimal(13), 
        @dcFreeDBspace decimal(13),
        @dcReservedSpace decimal(13), 
        @dcDataSpace decimal(13), 
        @dcIndexSpace decimal(13), 
        @dcUnusedSpace decimal(13),
        @dcLogFileSpace decimal(13),
        @dcActualLogSpace decimal(13),
        @dcPercentFreeLogSpace dec(10,6),
        @iTblCnt2 int, 
        @dcRowCnt2 decimal(13), 
        @dcDBFileSpace2 decimal(13), 
        @dcFreeDBspace2 decimal(13),
        @dcReservedSpace2 decimal(13), 
        @dcDataSpace2 decimal(13), 
        @dcIndexSpace2 decimal(13), 
        @dcUnusedSpace2 decimal(13),
        @dcLogFileSpace2 decimal(13),
        @dcActualLogSpace2 decimal(13),
        @dcPercentFreeLogSpace2 dec(10,6),
        @vcBytesperPage varchar(5),
        @vcObjCnt varchar(5),
        @tiLoopRtn tinyint,
        @vcName sysname,
        @vcNote varchar(50),
        @vcCmd varchar(4000)

-- Retrieve the bytes per page value.
Select @vcBytesperPage = str(low,5) 
  from master.dbo.spt_values 
 where number = 1 and type = 'E'


-- Print help information.
If @vcDBName = '?'
   Begin
      Print 'Syntax: dbo.sp_spaceused2'
      Print '        @vcDBName       = <database name|[all]|[?]|null> -- Leave null to get info about the current'
      Print '                                                            database or use [all] to get info about'
      Print '                                                            all databases. '
      Print '        @vcTable        = <table name|[all]|[user]|null>  -- Any valid table within the specified database.'
      Print '                                                             Use [all] to get all tables for the specified database.'
      Print '                                                             Use [user] to get just user tables for the specified database.'
      Print '        @tiUpdateUsage  = <0|1>  -- Set to 1 to first run Update Usage. If you see'
      Print '                                    negative results run again with this option'
      Print '                                    set to one. The default is 0.'
      Print '        @tiUpdateRowCnt = <0|1>  -- Set to 1 to update row counts in sysindexes.'
      Print '        @tyCaptureInfo  = <0|1>  -- Set to 1 to capture db data in msdb.dbo.DbSizeInfo'
      Print ''
      Print 'The current database is ' + db_name() + '.'
      Print ''
      Select name [Other databases] from master.dbo.sysdatabases order by name
      Select name [Tables in the current database] from sysobjects where xtype = 'U' or xtype = 'S'
             order by xtype desc, name asc
      Return
   End

-- If @vcDBName is null grab the current db name
If @vcDBName is null 
   Begin
      Select @vcDBName = db_name()
   End

-- Check for a valid database name.
If @vcDBName <> 'all'
   Begin
      -- Is the databse name valid?
      If not exists (select * from master.dbo.sysdatabases where name = @vcDBName and
                     (status&32)|(status&64)|(status&128)|(status&256)|(status&512)|
                     (status&1024)|(status&32768)|(status&1073741824) = 0 )
         Begin
            Print '''' + @vcDBName + ''' is not a valid database on this server or is in a non-accessible mode such as offline.'
            Print ''
            Select name [Valid accessible databases are...] from master.dbo.sysdatabases 
             where (status&32)|(status&64)|(status&128)|(status&256)|(status&512)|
                   (status&1024)|(status&32768)|(status&1073741824) = 0 order by name 
            Return
         End
      Else -- Check for a valid table.
         Begin
            -- Check for single user mode.
            If (Select status&4096 from master.dbo.sysdatabases where name = @vcDBName) <> 0
               Begin
                  Print 'Database ' + @vcDBName + ' is in single user mode ' +
                        'and this process requires a subordinate connection.'
                  Print 'A report cannot not be generated while the database is in single user mode.'
                  Return
               End
            
            If @vcTable is not null and @vcTable <> 'all' and @vcTable <> 'user'
               Begin
                  -- Check for an owner otherwise assume dbo
                  If charindex('.',@vcTable) = 0 
                     Set @vcTable = 'dbo.' + @vcTable
                   
                  -- SQL functions are local to the db so use dynamic SQL to test existance
                  -- and capture the results in a temp table.
                  If (Select object_id('tempdb.dbo.#CaptureResults')) > 0 
                     Exec ('Drop table #CaptureResults')
                  Create table #CaptureResults(value tinyint null)
                  Exec ('Use ' + @vcDBName + ' If exists (select * from ' + 
                         @vcDBName + '.dbo.sysobjects ' +
                        'where id = object_id(''' + @vcTable + ''')) ' +
                        'Insert into #CaptureResults values (1) ' +
                        'Else Insert into #CaptureResults values (0) ')
               
                  -- Check the results table and print a message if 0.
                  If (select value from #CaptureResults) = 0
                     Begin
                        Print 'The table ''' + @vcDBName + '.' + @vcTable +
                              ''' does not exist.'
                        Print ''
                        Exec('Select name [Tables in ' + @vcDBName + '] from  ' + 
                              @vcDBName + '.dbo.sysobjects ' +
                             'where xtype = ''U'' or xtype = ''S'' ' +
                             'order by xtype desc, name asc')
                        Return
                     End
                  Else -- I have a single table in a given database.
                     Begin
                        -- Check to see if we should run UpdateUsage.
                        If @cUpdateUsage = '1'
                           Begin
                              -- Skip if the database is in read only mode or some other offline mode.
                              If (Select (status&32)|(status&64)|(status&128)|
                                         (status&256)|(status&512)|(status&1024)|
                                         (status&32768)|(status&1073741824) 
                                    from master.dbo.sysdatabases where name = @vcDBName) = 0
                                 Begin
                                    If @cUpdateRowCnt = '1'
                                       Exec ('dbcc updateusage(' + @vcDBName + ') with COUNT_ROWS,NO_INFOMSGS ')
                                    Else
                                       Exec ('dbcc updateusage(' + @vcDBName + ') with NO_INFOMSGS ')
                                 End
                              Else
                                 Begin
                                    Print 'Cannot run UpdateUsage against ' +  @vcDBName  +
                                          ' because the databse is read-only or offline, etc.'
                                 End
                           End
                        Insert into #names values(@vcTable,0)
                        Print 'Database ' + @vcDBName
                        Print ''
                        Goto GetTableInfo
                     End
               End
            Else -- I have a given database but no specific table so get all tables.
               Begin
                  Set @tiLoopRtn = 2
                  Insert into #names(name) values(@vcDBName)
                  Goto GetDatabaseInfo
                  RtnToDB:
                  If @vcTable = 'all'
                     Begin
                        Print ''
                        Truncate table #names
                        Exec ('Use ' + @vcDBName + ' Insert into #names ' +
                              'Select name, case when xtype = ''U'' then 0 else 1 end ' +
                              'from sysobjects where xtype = ''U'' or xtype = ''S''')
                        Goto GetTableInfo
                     End
                  If @vcTable = 'user'
                     Begin
                        Print ''
                        Truncate table #names
                        Exec ('Use ' + @vcDBName + ' Insert into #names ' +
                              'Select name, 0 ' +
                              'from sysobjects where xtype = ''U'' ')
                        Goto GetTableInfo
                     End
               End
         End
   End
Else
   -- Get all databasses
   Begin
      Set @tiLoopRtn = 1
      Insert into #names(name) 
      Select name from master.dbo.sysdatabases
       where (status&32)|(status&64)|(status&128)|(status&256)|
             (status&512)|(status&32768)|(status&1073741824) = 0
      Goto GetDatabaseInfo
      --RtnToAllDbs:
   End

Return

GetDatabaseInfo:
Select @vcName = min(name) from #names
While @vcName is not null
   Begin
      -- Check for single user mode and a connection
      If (Select status&4096 from master.dbo.sysdatabases where name = @vcName) <> 0
         Begin
            Print 'Database ' + @vcName + ' is in single user mode.'
            Print 'It cannot not be included in the report at this time.'
            Print ''
            Goto NextLoopdb
         End

      -- DB File Space = sum(size) sysfiles where 0x40&status <> 0x40
      -- Reserved Space = sum(reserved) sysindexes where indid in (0,1,255)
      -- Data Space = sum(dpages) sysindexes where indid < 2 + sum(used) where indid = 255
      -- Index Space = sum(used) sysindexes where indid in (0,1,255) - @dcDataSpace
      -- Unused Space = @dcReservedSpace - sum(used) sysindexes where indid in (0,1,255)
      -- Log File Space = sum(size) sysfiles where 0x40&status = 0x40
      -- Free DB Space = @dcDBFileSpace - @dcReservedSpace
      Exec ('use ' + @vcName + ' ' +
            'If ' + @cUpdateUsage + ' = 1 Begin If (Select status&1024 from master.dbo.sysdatabases ' +
            'where name = ''' + @vcName + ''') = 0 Begin If ' + @cUpdateRowCnt + ' = 1 ' +
            'Exec (''dbcc updateusage(' + @vcName + ') WITH COUNT_ROWS, NO_INFOMSGS  '') ' +
            'Else Exec (''dbcc updateusage(' + @vcName + ') with NO_INFOMSGS '') ' +
            'End Else Begin Print ''Updateusaage cannot be run against database ' + @vcName + 
            ' because it is in read only mode.'' End End ' +
            'declare @dcReservedSpace decimal(13), @dcDataSpace decimal(13), ' +
            '@dcIndexSpace decimal(13), @dcUnusedSpace decimal(13), ' +
            '@dcDBFileSpace decimal(13), @dcLogFileSpace decimal(13), ' +
            '@dcFreeDBspace decimal(13), @iTblCnt int,  @iRowCnt int ' +

            'Select @iTblCnt = count(*) from sysobjects where type = ''U'' ' +

            'Select @iRowCnt = isnull(sum(isnull(rows,0)),0) from sysindexes i, sysobjects o ' +
            'where o.id = i.id and o.type = ''U'' and i.indid in (0,1,255) ' +

            'Select @dcDBFileSpace = sum(convert(dec(15),size))*' + @vcBytesperPage + ' '+
            'from sysfiles where 0x40&status <> 0x40 ' +

            'Select @dcReservedSpace = (select sum(convert(dec(15),reserved)) ' +
   'from sysindexes where indid in (0, 1, 255))*' + @vcBytesperPage + ' ' +

            'Select @dcDataSpace = ((select sum(convert(dec(15),dpages)) ' +
   'from sysindexes where indid < 2) +  ' +
         '(select isnull(sum(convert(dec(15),used)), 0) ' +
      'from sysindexes where indid = 255))*' + @vcBytesperPage + ' ' +

            'Select @dcIndexSpace = (select sum(convert(dec(15),used)) ' +
'from sysindexes where indid in (0, 1, 255))*' + @vcBytesperPage + ' - @dcDataSpace ' +

            'Select @dcUnusedSpace = @dcReservedSpace - (select sum(convert(dec(15),used)) ' +
'from sysindexes where indid in (0, 1, 255))*' + @vcBytesperPage + ' ' +

            'Select @dcLogFileSpace = sum(convert(dec(15),size))*' + @vcBytesperPage + ' '+
            'from sysfiles where 0x40&status = 0x40 ' +

            'Select @dcFreeDBspace = @dcDBFileSpace - @dcReservedSpace ' +

            'Insert into #dbData values(''' + @vcName + 
            ''', @iTblCnt, @iRowCnt, @dcDBFileSpace, @dcFreeDBspace, @dcReservedSpace, @dcDataSpace,' +
            '@dcIndexSpace, @dcUnusedSpace, @dcLogFileSpace, 0.0, 0.0, 3) '
            ) 

      NextLoopdb:
      Select @vcName = min(name) from #names where name > @vcName
         
   End

Insert into #logspaceinfo
Exec ('DBCC SQLPERF(LOGSPACE)')

Update #dbData 
   set usable_log_space = 1000000.*lspaceused,
       log_space_free = 100.000 - lspercent
  from #logspaceinfo b where #dbData.name = rtrim(b.name)

If (Select count(*) from #names) > 1
   Begin
      Select @iTblCnt = sum(db_tbl_cnt), 
             @dcRowCnt = sum(row_cnt), 
             @dcDBFileSpace = sum(db_file_size), 
             @dcFreeDBspace = sum(db_space_free),
             @dcReservedSpace = sum(total_reserved), 
             @dcDataSpace = sum(data), 
             @dcIndexSpace = sum(indexes),
             @dcUnusedSpace = sum(unused),
             @dcLogFileSpace = sum(log_file_size), 
             @dcActualLogSpace = sum(usable_log_space),
             @dcPercentFreeLogSpace = sum(log_space_free)/count(*)
        from #dbData
      Select @iTblCnt2 = sum(db_tbl_cnt), 
             @dcRowCnt2 = sum(row_cnt), 
             @dcDBFileSpace2 = sum(db_file_size), 
             @dcFreeDBspace2 = sum(db_space_free),
             @dcReservedSpace2 = sum(total_reserved), 
             @dcDataSpace2 = sum(data), 
             @dcIndexSpace2 = sum(indexes),
             @dcUnusedSpace2 = sum(unused),
             @dcLogFileSpace2 = sum(log_file_size), 
             @dcActualLogSpace2 = sum(usable_log_space),
             @dcPercentFreeLogSpace2 = sum(log_space_free)/count(*)
        from #dbData 
       where name not in ('master','msdb','model','tempdb','northwind','pubs')
      
      Insert into #dbData 
      Values (@@servername + '(all dbs)', 
              @iTblCnt,
              @dcRowCnt,
              @dcDBFileSpace, 
              @dcFreeDBspace,
              @dcReservedSpace, 
              @dcDataSpace, 
              @dcIndexSpace, 
              @dcUnusedSpace,
              @dcLogFileSpace,
              @dcActualLogSpace,
              @dcPercentFreeLogSpace,
              1) 
      Insert into #dbData 
      Values (@@servername + '(nonsystem dbs)', 
              @iTblCnt2,
              @dcRowCnt2,
              @dcDBFileSpace2, 
              @dcFreeDBspace2,
              @dcReservedSpace2, 
              @dcDataSpace2, 
              @dcIndexSpace2, 
              @dcUnusedSpace2,
              @dcLogFileSpace2,
              @dcActualLogSpace2,
              @dcPercentFreeLogSpace2,
              2)

   End

Print ''
-- Display results only if we have any
If (select Count(*) from #dbData) > 0
   Begin
      Select convert(varchar(40), name) [Server or db], 
             str(db_tbl_cnt,4) [ # of user tbls],
             str(row_cnt,13) [   total rows],
             str(db_file_size,13) [ db_file_size],
             str(db_space_free,13) [db_space_free],
             str(total_reserved,13) [total_reserved],
             str(data,13) [   data_space],
             str(indexes,13) [  index_space],
             str(unused,13) [unused_space],
             str(log_file_size,13) [log_file_size],
             str(usable_log_space,13) [usable_log_space],
             str(log_space_free,8,4) [% log_space_free]
       from #dbData
      order by sort_order, name
      
      If (select count(*) from #names) > 1
         Begin
            Select @vcObjCnt = str(count(*),4) from master.dbo.sysdatabases
            Select @vcNote = '(' + @vcObjCnt + ' databases; size numbers in bytes)'
            Print @vcNote
            Print ''
            Exec master.dbo.xp_fixeddrives
         End
      Else
         Begin
            Select @vcNote = '(size numbers in bytes)'
            Print @vcNote
            Print ''
         End
      
      -- Check to see if we should save the data
      If @tySaveDbInfo = 1 and datepart(weekday, GetDate()) = 7
         Exec ('Insert into msdb.dbo.DbSizeInfo ' +
               'Select @@servername, *, getdate() ' +
               'from #dbData order by sort_order, name ')
   End

If @tiLoopRtn = 1
   Return
If @tiLoopRtn = 2
   goto RtnToDB

GetTableInfo:
Select @vcName = min(name) from #names
While @vcName is not null
   Begin
      -- Reserved Space = sum(reserved) sysindexes where indid in (0,1,255)
      -- Data Space = sum(dpages) sysindexes where indid < 2 + sum(used) where indid = 255
      -- Index Space = sum(used) sysindexes where indid in (0,1,255) - @dcDataSpace
      -- Unused Space = @dcReservedSpace - sum(used) sysindexes where indid in (0,1,255)
      -- Row count = rows sysindexes where indid < 2
      Exec ('use ' + @vcDBName + ' ' +
            'declare @dcReservedSpace decimal(13), @dcDataSpace decimal(13), ' +
            '@dcIndexSpace decimal(13), @dcUnusedSpace decimal(13) ' +
         
            'Select @dcReservedSpace = (select sum(convert(dec(13),reserved)) ' +
            'from sysindexes a join sysobjects b on a.id = b.id ' + 
            'where indid in (0, 1, 255) and a.id = object_id(''' + 
             @vcName + '''))*' + @vcBytesperPage + ' ' +
         
            'Select @dcDataSpace = ((select sum(convert(dec(13),dpages)) ' +
            'from sysindexes a join sysobjects b on a.id = b.id ' + 
            'where indid < 2 and a.id = object_id(''' + 
             @vcName + ''')) +  ' +
            '(select isnull(sum(convert(dec(15),used)), 0) ' +
            'from sysindexes a join sysobjects b on a.id = b.id ' + 
            'where indid = 255 and a.id = object_id(''' + 
             @vcName + ''')))*' + @vcBytesperPage + ' ' +
         
            'Select @dcIndexSpace = (select sum(convert(dec(13),used)) ' +
         'from sysindexes a join sysobjects b on a.id = b.id ' + 
            'where indid in (0, 1, 255) and a.id = object_id(''' + 
             @vcName + '''))*' + @vcBytesperPage + 
            ' - @dcDataSpace ' +
         
            'Select @dcUnusedSpace = @dcReservedSpace - (select sum(convert(dec(13),used)) ' +
         'from sysindexes a join sysobjects b on a.id = b.id ' + 
            'where indid in (0, 1, 255) and a.id = object_id(''' + 
             @vcName + '''))*' + @vcBytesperPage + ' ' +
            
            'Insert into #TableData ' +
            'Select ''' + @vcName + ''', convert(char(11),rows), ' +
            '@dcReservedSpace, @dcDataSpace, ' +
            '@dcIndexSpace, @dcUnusedSpace,  ' +
            '(select sort_order from #names where name = ''' + @vcName + ''')' +
            'from sysindexes a join sysobjects b on a.id = b.id ' + 
            'where indid < 2 and a.id = object_id(''' + 
             @vcName + ''')' 
            )
      Select @vcName = min(name) from #names where name > @vcName
   End

Select cast(name as varchar) [Table], 
       Convert(varchar(13),space(13 - len(row_count)) + row_count) [    Row count],
       str(reserved,13)  [     Reserved], 
       str(data,13)      [         Data],
       str(indexes,13)   [      Indexes], 
       str(unused,13)    [       Unused]
  from #TableData
 order by sort_order, name

If (select count(*) from #names) > 1
   Begin
      Select @vcObjCnt = str(count(*),4) from #names
      Select @vcNote = '(' + @vcObjCnt + ' tables; size numbers in bytes)'
      Print @vcNote
      Print ''
   End
Else
   Begin
      Select @vcNote = '(size numbers in bytes)'
      Print @vcNote
      Print ''
   End

Return
go

grant exec on sp_spaceused2 to public
go

Rate

You rated this post out of 5. Change rating

Share

Share

Rate

You rated this post out of 5. Change rating