一.本文所涉及的內容(Contents)
二.背景(Contexts)
在性能調優或者需要了解某數據庫表信息的時候,最直觀的方式就是羅列出這個數據所有表的信息,這些信息包括:表的記錄數、數據記錄占用空間、索引占用空間、未使用的空間等(如Figure1所示),有了這些信息你可以簡單的判斷這個數據庫來自數據上的壓力可能是某個表造成的。因為表數據越大,對數據庫性能的影響越大。
要實現某個數據庫所有表的信息,可以通過游標的形式獲取相應的數據,下圖Figure1返回某數據庫中所有表的信息:
(Figure1:某數據庫所有表信息)
也許你並不滿足於Figure1的信息,你希望獲取整個數據庫實例中所有數據庫所有表的信息(如Figure2所示),如果想了解里面的實現可以參考:SQL Server 查看所有數據庫所有表大小信息(Sizes of All Tables in All Database)
(Figure2:所有數據庫所有表信息)
三.實現代碼(SQL Codes)
為了實現Figure1的效果,有三種方式可以實現:
1. 運用游標循環每個表,調用系統存儲過程sp_spaceused把結果保存到臨時表,可以過濾表;
2. 運用系統存儲過程sp_MSforeachtable把結果保存到臨時表,可以過濾表;
3. 運用系統表INFORMATION_SCHEMA.TABLES與系統存儲過程sp_spaceused結合起來,拼接出每個表的INSERT語句,把結果保存到臨時表,可以過濾表;
首先定義一個臨時表變量@tablespaceinfo用於保存表的信息,使用游標讀取sys.tables中的表名稱,再通過sp_spaceused獲取這個表的相關數據插入到臨時表變量@tablespaceinfo。下面是SQL腳本的實現,效果就如Figure1所示:
--Script1: --查看某數據庫所有表的信息 DECLARE @tablespaceinfo TABLE ( [name] SYSNAME, [rows] BIGINT, [reserved] VARCHAR(100), [data] VARCHAR(100), [index_size] VARCHAR(100), [unused] VARCHAR(100) ) DECLARE @tablename VARCHAR(255); DECLARE Info_cursor CURSOR FOR SELECT '['+[name]+']' FROM sys.tables WHERE TYPE='U'; OPEN Info_cursor FETCH NEXT FROM Info_cursor INTO @tablename WHILE @@FETCH_STATUS = 0 BEGIN INSERT INTO @tablespaceinfo EXEC sp_spaceused @tablename FETCH NEXT FROM Info_cursor INTO @tablename END CLOSE Info_cursor DEALLOCATE Info_cursor SELECT * FROM @tablespaceinfo ORDER BY Cast(Replace(reserved,'KB','') AS INT) DESC
下面Script2腳本是通過使用系統存儲過程sp_MSforeachtable,返回的結果如Figure1所示:
--Script2: --查看某數據庫所有表的信息 DECLARE @tablespaceinfo TABLE ( [name] SYSNAME, [rows] BIGINT, [reserved] VARCHAR(100), [data] VARCHAR(100), [index_size] VARCHAR(100), [unused] VARCHAR(100) ) INSERT INTO @tablespaceinfo EXEC sp_MSforeachtable @command1="sp_spaceused '?'" SELECT * FROM @tablespaceinfo ORDER BY Cast(Replace(reserved,'KB','') AS INT) DESC
1) 如果你想使用sp_MSforeachtable但是又想過濾掉一些表,你可以在查詢@tablespaceinfo的時候加入Where條件(這種方式就不演示了);或者直接在開始使用sp_MSforeachtable的時候就進行過濾:
--獲取所有表信息 EXEC sp_MSforeachtable @command1="sp_spaceused '?'"
如果我們加入@whereand參數進行過濾,但是會出現下圖的錯誤信息:
--過濾某些表 EXEC sp_MSforeachtable @whereand="and [name] like 't%'", @command1="sp_spaceused '?'"
(Figure3:錯誤信息)
經過查看sp_MSforeachtable的源代碼,發現是有多個[name]字段的,所以指明字段,上面的錯誤就可以解決了,效果如下圖所示。把下面的SQL代碼替換Script2的部分代碼就可以了。
--過濾某些表 EXEC sp_MSforeachtable @whereand="and o.[name] like 't%'", @command1="sp_spaceused '?'" --或者 EXEC sp_MSforeachtable @whereand="and syso.[name] like 't%'", @command1="sp_spaceused '?'"
(Figure4:過濾返回的結果)
2) 在sp_MSforeachtable系統存儲過程,有@whereand的參數可以對表進行過濾,sp_MSForEachDB系統存儲是否也有同樣的功能呢?查看了sp_MSForEachDB的源碼,我們發現是沒有這個功能的,為實現這個功能,我對sp_MSForEachDB進行修改,重新命名為:sp_MSForEachDB_Filter
--擴展sp_MSforeachdb create proc dbo.[sp_MSforeachdb_Filter] @command1 nvarchar(2000), @replacechar nchar(1) = N'?', @command2 nvarchar(2000) = null, @command3 nvarchar(2000) = null, @whereand nvarchar(2000) = null,@precommand nvarchar(2000) = null, @postcommand nvarchar(2000) = null as set deadlock_priority low declare @inaccessible nvarchar(12), @invalidlogin nvarchar(12), @dbinaccessible nvarchar(12) select @inaccessible = ltrim(str(convert(int, 0x03e0), 11)) select @invalidlogin = ltrim(str(convert(int, 0x40000000), 11)) select @dbinaccessible = N'0x80000000' if (@precommand is not null) exec(@precommand) declare @origdb nvarchar(128) select @origdb = db_name() exec(N'declare hCForEachDatabase cursor global for select name from master.dbo.sysdatabases d ' + N' where (d.status & ' + @inaccessible + N' = 0)' + N' and (DATABASEPROPERTY(d.name, ''issingleuser'') = 0 and (has_dbaccess(d.name) = 1))' + @whereand) declare @retval int select @retval = @@error if (@retval = 0) exec @retval = sys.sp_MSforeach_worker @command1, @replacechar, @command2, @command3, 1 if (@retval = 0 and @postcommand is not null) exec(@postcommand) declare @tempdb nvarchar(258) SELECT @tempdb = REPLACE(@origdb, N']', N']]') exec (N'use ' + N'[' + @tempdb + N']') return @retval
上面的存儲過程sp_MSforeachdb_Filter是依照sp_MSforeachtable進行簡單修改的,創建之后就可以使用下面的SQL腳本進行過濾數據庫了,效果如下圖所示:
--過濾數據庫 EXEC [sp_MSforeachdb_Filter] @command1="print '?'", @whereand=" and [name] not in('tempdb','master','model','msdb') "
(Figure5:過濾數據庫)
(三) 運用系統表INFORMATION_SCHEMA.TABLES與系統存儲過程sp_spaceused結合起來,拼接出每個表的INSERT語句,把結果保存到臨時表,可以過濾表;
一次偶然的機會看到一個同樣能實現Figure1效果的SQL腳本,它使用了系統表INFORMATION_SCHEMA.TABLES,下面是我修改過的SQL腳本,區別就在於可以滿足對不同架構表的查詢。原文詳情可以參考:How to get information about all databases without a loop
--Script3:Sizes of All Tables in a Database --exec sp_MSforeachtable 'print ''?'' exec sp_spaceused ''?''' --在它的基礎上做了些修改,適合不同的框架dbo等 IF OBJECT_ID('tempdb..#TablesSizes') IS NOT NULL DROP TABLE #TablesSizes CREATE TABLE #TablesSizes (TableName sysname, Rows bigint, reserved varchar(100), data varchar(100), index_size varchar(100), unused varchar(100)) DECLARE @sql VARCHAR(MAX) SELECT @sql = COALESCE(@sql,'') + ' INSERT INTO #TablesSizes execute sp_spaceused ''' + QUOTENAME(TABLE_SCHEMA,'[]') + '.' + QUOTENAME(Table_Name,'[]') + '''' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' PRINT (@SQL) EXECUTE (@SQL) SELECT * FROM #TablesSizes ORDER BY TableName
上面PRINT出來的腳本類似下圖Figure6所示:
(Figure6:拼接的SQL代碼)
四.參考文獻(References)
與存儲過程sp_MSforeachdb類似的存儲過程sp_MSforeachdb