SQL Server sp_helpdb 改写

在SQL Server 数据库管理中,我们常使用sp_helpdb 来查看数据库的基本信息,如我们要查看test数据库的信息,可以执行下面脚本:

EXEC sp_helpdb test

查询结果如下:

但结果中无论是db_size 还是文件的size,都是分配的空间,我们不能看出数据库使用的空间有多少,未使用空间有多少。这个数据库的使用空间对前文中数据库收缩有很大的用处。在最开始我是通过SSMS对象资源管理器,右击目标数据库,在任务→收缩→文件中查看数据库或者数据库文件的空间使用情况的,如下图:

这样操作对于高逼格的管理员来说,显得就比较low了。当然最主要的是重复性操作性低。为此查看系统视图 sysfiles、database_files,系统过程 sp_helpfile 均只记录数据库的分配空间大小。使用前文中提到的 dbcc showfilestats,发现其只有数据文件的空间分配使用情况。最终找到了fileProperty内置函数,使用其 usedSpace 参数,结合系统视图、获取到了所有数据库文件空间分配使用情况。为满足高效管理的需要,我将sp_helpdb 改写为 sp_DBA_helpdb,具体脚本如下:

USE [master]
GO
/*
      数据库基本信息查看
      完善系统过程sp_helpdb
      --exec sp_DBA_helpdb 'master'
      create by Jack
*/
CREATE PROCEDURE [dbo].[sp_DBA_helpdb] @dbname SYSNAME = NULL   -- 数据库名
AS
BEGIN
DECLARE @exec_stmt NVARCHAR(625) 
DECLARE @showdev BIT 
DECLARE @name SYSNAME 
DECLARE @cmd NVARCHAR(285) -- (26 + 258) + 1 extra 
DECLARE @dbdesc VARCHAR(600) /* 数据库的属性信息*/ 
DECLARE @propdesc VARCHAR(40) 
 
SET nocount ON 
CREATE TABLE #spdbdesc
    (
      dbName SYSNAME
      ,owner SYSNAME NULL
      ,created NVARCHAR(11)
      ,dbid SMALLINT
      ,dbdesc NVARCHAR(600) NULL
      ,dbTotalSize NVARCHAR(13) NULL  --数据库分配空间(总空间)
      ,dbUsedSize NVARCHAR(13) NULL       --数据库已经使用的空间
      ,dbFreeSizeRatio NVARCHAR(15) NULL  --数据库可用空间比例
      ,cmptLevel TINYINT  --兼容级别
    ) 
/* 
**  如果指定数据库名称为null,返回实例上所有数据库信息,
**  否则返回对应数据库信息及数据库的文件信息
*/ 
IF @dbname IS NULL
    SELECT  @showdev = 0 
ELSE
    SELECT  @showdev = 1 
 
/* 
**  检验数据库是否存在
*/ 
IF NOT EXISTS ( SELECT  *
                FROM    master.dbo.sysdatabases
                WHERE   ( @dbname IS NULL
                          OR name = @dbname
                        ) )
    BEGIN 
        RAISERROR(15010,-1,-1,@dbname) 
        RETURN (1) 
    END 
 
 
/* 
**  从系统视图sysdatabases 初始化#spdbdesc 表
*/ 
INSERT  INTO #spdbdesc ( dbname, owner, created, dbid, cmptlevel )
        SELECT  name, ISNULL(SUSER_SNAME(sid), '~~UNKNOWN~~')
                  , CONVERT(NVARCHAR(11), crdate), dbid, cmptlevel
        FROM    master.dbo.sysdatabases
        WHERE   ( @dbname IS NULL
                  OR name = @dbname
                )  --如果指定数据库名称为null,则返回所有实例上所有数据库信息
 
/*
      **fileProperty 只能获取当前会话的文件属性,否则返回NULL
      **下面将循环每个库的每个文件,获取已经使用的文件大小
*/
IF OBJECT_ID('tempdb..#fileSize') IS NOT NULL
    DROP TABLE #fileSize
CREATE TABLE #fileSize
    (
      dbName VARCHAR(256) ,
      fName VARCHAR(256) ,
      totalSize BIGINT ,
      usedSize BIGINT
    )
 
DECLARE ms_crs_c1 CURSOR global
FOR
SELECT  DB_NAME(dbid)
FROM    #spdbdesc 
OPEN ms_crs_c1 
FETCH ms_crs_c1 INTO @name 
WHILE @@fetch_status >= 0
    BEGIN 
        IF ( HAS_DBACCESS(@name) <> 1 )  --判断是否有权限访问数据库,不等于无权限访问
            BEGIN 
                DELETE  #spdbdesc
                WHERE CURRENT OF ms_crs_c1 
                RAISERROR(15622,-1,-1, @name) 
            END 
        ELSE
            BEGIN 
                IF OBJECT_ID('tempdb..#fileName') IS NOT NULL
                    DROP TABLE #fileName
                CREATE TABLE #fileName
                    (
                      name VARCHAR(256) ,
                      TotalSize BIGINT
                    )
                SET @exec_stmt = 'select name,size from ' + QUOTENAME(@name) + '.sys.sysfiles'
                INSERT  INTO #fileName ( name, TotalSize )
                        EXEC ( @exec_stmt
                            )
                DECLARE @fname VARCHAR(256) ,
                    @TotalSize BIGINT
                DECLARE fName CURSOR
                FOR
                SELECT  name, TotalSize
                FROM    #fileName
                OPEN fName
                FETCH NEXT FROM fName INTO @fName, @TotalSize
                WHILE @@FETCH_STATUS >= 0
                    BEGIN
                        SET @exec_stmt = 'use ' + QUOTENAME(@name) + ' select fileProperty(''' + @fname
                            + ''',''SpaceUsed'')'
                        INSERT  INTO #fileSize ( usedSize )
                                EXEC ( @exec_stmt)
                        UPDATE  #fileSize
                        SET     dbName = @name, fName = @fname, totalSize = @TotalSize
                        WHERE   dbName IS NULL
                        FETCH NEXT FROM fName INTO @fName, @TotalSize
                    END
                CLOSE fName
                DEALLOCATE fName
       
            END 
        FETCH ms_crs_c1 INTO @name 
    END 
DEALLOCATE ms_crs_c1;
WITH    cte
          AS (
               SELECT   dbName, SUM(totalSize) totalSize, SUM(usedSize) usedSize
               FROM     #fileSize
               GROUP BY dbName
             )
UPDATE  #spdbdesc
SET     dbTotalSize = cte.totalSize, dbUsedSize = cte.usedSize
FROM    #spdbdesc s
LEFT JOIN cte ON cte.dbName = s.dbName
UPDATE  #spdbdesc
SET     dbFreeSizeRatio = STR(( CONVERT(dec(17, 2), dbTotalSize) - CONVERT(dec(17, 2), dbUsedSize) ) * 100.0
                              / CONVERT(dec(17, 2), dbTotalSize), 5, 2) + '%'
UPDATE  #spdbdesc
SET     dbTotalSize = STR(CONVERT(DECIMAL(17,2), dbTotalSize)/128.0,10,2) + ' MB'
      , dbUsedSize = STR(CONVERT(DECIMAL(17,2), dbUsedSize)/128.0,10,2) + ' MB'
/* 
**  Now for each dbid in #spdbdesc, build the database status 
**  description. 
*/ 
DECLARE @curdbid SMALLINT /* the one we're currently working on */ 
/* 
**  Set @curdbid to the first dbid. 
*/ 
SELECT  @curdbid = MIN(dbid)
FROM    #spdbdesc 
 
 
WHILE @curdbid IS NOT NULL
    BEGIN 
        SET @name = DB_NAME(@curdbid) 
 
 -- These properties always available 
        SELECT  @dbdesc = 'Status=' + CONVERT(SYSNAME, DATABASEPROPERTYEX(@name, 'Status')) 
        SELECT  @dbdesc = @dbdesc + ', Updateability=' + CONVERT(SYSNAME, DATABASEPROPERTYEX(@name, 'Updateability')) 
        SELECT  @dbdesc = @dbdesc + ', UserAccess=' + CONVERT(SYSNAME, DATABASEPROPERTYEX(@name, 'UserAccess')) 
        SELECT  @dbdesc = @dbdesc + ', Recovery=' + CONVERT(SYSNAME, DATABASEPROPERTYEX(@name, 'Recovery')) 
        SELECT  @dbdesc = @dbdesc + ', Version=' + CONVERT(SYSNAME, DATABASEPROPERTYEX(@name, 'Version')) 
 
 -- These props only available if db not shutdown 
        IF DATABASEPROPERTY(@name, 'IsShutdown') = 0
            BEGIN 
                SELECT  @dbdesc = @dbdesc + ', Collation=' + CONVERT(SYSNAME, DATABASEPROPERTYEX(@name, 'Collation')) 
                SELECT  @dbdesc = @dbdesc + ', SQLSortOrder=' + CONVERT(SYSNAME, DATABASEPROPERTYEX(@name,
                                                                                                    'SQLSortOrder')) 
            END 
 
 -- These are the boolean properties 
        IF DATABASEPROPERTYEX(@name, 'IsAutoClose') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsAutoClose' 
        IF DATABASEPROPERTYEX(@name, 'IsAutoShrink') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsAutoShrink' 
        IF DATABASEPROPERTYEX(@name, 'IsInStandby') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsInStandby' 
        IF DATABASEPROPERTYEX(@name, 'IsTornPageDetectionEnabled') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsTornPageDetectionEnabled' 
        IF DATABASEPROPERTYEX(@name, 'IsAnsiNullDefault') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsAnsiNullDefault' 
        IF DATABASEPROPERTYEX(@name, 'IsAnsiNullsEnabled') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsAnsiNullsEnabled' 
        IF DATABASEPROPERTYEX(@name, 'IsAnsiPaddingEnabled') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsAnsiPaddingEnabled' 
        IF DATABASEPROPERTYEX(@name, 'IsAnsiWarningsEnabled') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsAnsiWarningsEnabled' 
        IF DATABASEPROPERTYEX(@name, 'IsArithmeticAbortEnabled') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsArithmeticAbortEnabled' 
        IF DATABASEPROPERTYEX(@name, 'IsAutoCreateStatistics') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsAutoCreateStatistics' 
        IF DATABASEPROPERTYEX(@name, 'IsAutoUpdateStatistics') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsAutoUpdateStatistics' 
        IF DATABASEPROPERTYEX(@name, 'IsCloseCursorsOnCommitEnabled') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsCloseCursorsOnCommitEnabled' 
        IF DATABASEPROPERTYEX(@name, 'IsFullTextEnabled') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsFullTextEnabled' 
        IF DATABASEPROPERTYEX(@name, 'IsLocalCursorsDefault') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsLocalCursorsDefault' 
        IF DATABASEPROPERTYEX(@name, 'IsNullConcat') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsNullConcat' 
        IF DATABASEPROPERTYEX(@name, 'IsNumericRoundAbortEnabled') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsNumericRoundAbortEnabled' 
        IF DATABASEPROPERTYEX(@name, 'IsQuotedIdentifiersEnabled') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsQuotedIdentifiersEnabled' 
        IF DATABASEPROPERTYEX(@name, 'IsRecursiveTriggersEnabled') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsRecursiveTriggersEnabled' 
        IF DATABASEPROPERTYEX(@name, 'IsMergePublished') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsMergePublished' 
        IF DATABASEPROPERTYEX(@name, 'IsPublished') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsPublished' 
        IF DATABASEPROPERTYEX(@name, 'IsSubscribed') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsSubscribed' 
        IF DATABASEPROPERTYEX(@name, 'IsSyncWithBackup') = 1
            SELECT  @dbdesc = @dbdesc + ', ' + 'IsSyncWithBackup' 
 
        UPDATE  #spdbdesc
        SET     dbdesc = @dbdesc
        WHERE   dbid = @curdbid 
 
 /* 
 **  Now get the next, if any dbid. 
 */ 
        SELECT  @curdbid = MIN(dbid)
        FROM    #spdbdesc
        WHERE   dbid > @curdbid 
    END 
 
/* 
**  Now #spdbdesc is complete so we can print out the db info 
*/ 
SELECT  name = dbname, dbTotalsize, dbUsedSize, dbFreeSizeRatio, owner = owner, dbid = dbid, created = created,
        status = dbdesc, compatibility_level = cmptlevel
FROM    #spdbdesc
ORDER BY dbname 
 
/* 
**  If we are looking at one database, show its file allocation. 
*/ 
IF @showdev = 1
    AND HAS_DBACCESS(@dbname) = 1
    BEGIN 
        PRINT N' ' 
        IF OBJECT_ID('tempdb..#fileInfo') IS NOT NULL
            DROP TABLE #fileInfo
        CREATE TABLE #fileInfo
            (
              Name VARCHAR(256) ,
              Fileid SMALLINT ,
              filename VARCHAR(256) ,
              FileGroup VARCHAR(520) ,
              TotalSize NVARCHAR(13) ,
              UsedSize NVARCHAR(13) ,
              FreeSizeRatio NVARCHAR(15) ,
              maxsize VARCHAR(20) ,
              growth VARCHAR(15) ,
              usage VARCHAR(20)
            )
        SELECT  @cmd = N'use ' + QUOTENAME(@dbname) + N' exec sys.sp_helpfile' 
        INSERT  INTO #fileInfo ( Name, Fileid, filename, FileGroup, TotalSize, maxsize, growth, usage )
                EXEC ( @cmd
                    )
       
        UPDATE  #fileInfo
        SET     TotalSize = STR(CONVERT(dec(17, 2), LEFT(TotalSize, LEN(TotalSize) - 2)) / 1024.0, 10, 2)
        UPDATE  #fileInfo
        SET     TotalSize = STR(f.TotalSize, 10, 2) + ' MB'
                        , UsedSize = STR(CONVERT(dec(17, 2),s.usedSize)/128.0, 10, 2) + ' MB',
                FreeSizeRatio = STR(( CONVERT(dec(17, 2), s.TotalSize) - CONVERT(dec(17, 2), s.usedSize) ) * 100.0
                                    / CONVERT(dec(17, 2), s.TotalSize), 5, 2) + '%'
        FROM    #fileInfo f
        LEFT JOIN #fileSize s ON f.Name = s.fName
                                 AND s.dbName = @dbname
        SELECT  Name, Fileid, filename, FileGroup, TotalSize, UsedSize, FreeSizeRatio, maxsize, growth, usage
        FROM    #fileInfo
 
    END 
RETURN (0) -- sp_helpdb 
END
EXEC sys.sp_MS_marksystemobject 'sp_DBA_helpdb';

对改写后的sp_DBA_helpdb进行测试,首先传入参数NULL,如下:

EXEC sp_DBA_helpdb NULL

然后在将参数换为指定的数据库,这里使用的是test库:

EXEC sp_DBA_helpdb test

从测试的结果来看,完全符合我们的预期。

这样当我们使用前文中的sp_DBA_shrikfile对单个数据库文件进行收缩时,sp_DBA_helpdb能够快速为我们确定各参数的值。同时sp_DBA_helpdb亦能帮助我们快速确定哪些文件需要收缩、收缩哪些文件可以快速释放大量空间。

后记

这里我直接改了sp_helpdb来实现目标,当然也可以先修改sp_helpfile,实现目标后再调用修改后的sp_helpfile 修改sp_DBA_helpdb。 

​如果想更为及时的获取最新文章,可以搜索注公众 MSQLServer,将有更多精彩。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值