在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,將有更多精彩。