Sql-Server
在呼叫數據庫上下文中執行的中央儲存過程
我正在使用該
sys.dm_db_index_physical_stats
視圖開發定制的維護解決方案。我目前從儲存過程中引用它。現在,當該儲存過程在我的一個數據庫上執行時,它會執行我希望它執行的操作並拉下有關任何數據庫的所有記錄的列表。當我將它放在不同的數據庫上時,它會拉下僅與該數據庫相關的所有記錄的列表。例如(底部的程式碼):
- 針對數據庫 6 執行的查詢顯示$$ requested $$數據庫 1-10 的資訊。
- 針對數據庫 3 執行的查詢顯示$$ requested $$僅數據庫 3 的資訊。
我希望這個過程專門用於數據庫 3 的原因是因為我更願意將所有維護對象保存在同一個數據庫中。我想讓這份工作放在維護數據庫中,就像在那個應用程序數據庫中一樣工作。
程式碼:
ALTER PROCEDURE [dbo].[GetFragStats] @databaseName NVARCHAR(64) = NULL ,@tableName NVARCHAR(64) = NULL ,@indexID INT = NULL ,@partNumber INT = NULL ,@Mode NVARCHAR(64) = 'DETAILED' AS BEGIN SET NOCOUNT ON; DECLARE @databaseID INT, @tableID INT IF @databaseName IS NOT NULL AND @databaseName NOT IN ('tempdb','ReportServerTempDB') BEGIN SET @databaseID = DB_ID(@databaseName) END IF @tableName IS NOT NULL BEGIN SET @tableID = OBJECT_ID(@tableName) END SELECT D.name AS DatabaseName, T.name AS TableName, I.name AS IndexName, S.index_id AS IndexID, S.avg_fragmentation_in_percent AS PercentFragment, S.fragment_count AS TotalFrags, S.avg_fragment_size_in_pages AS PagesPerFrag, S.page_count AS NumPages, S.index_type_desc AS IndexType FROM sys.dm_db_index_physical_stats(@databaseID, @tableID, @indexID, @partNumber, @Mode) AS S JOIN sys.databases AS D ON S.database_id = D.database_id JOIN sys.tables AS T ON S.object_id = T.object_id JOIN sys.indexes AS I ON S.object_id = I.object_id AND S.index_id = I.index_id WHERE S.avg_fragmentation_in_percent > 10 ORDER BY DatabaseName, TableName, IndexName, PercentFragment DESC END GO
一種方法是在其中創建一個系統過程,
master
然後在您的維護數據庫中創建一個包裝器。請注意,這一次僅適用於一個數據庫。首先,在主人:
USE [master]; GO CREATE PROCEDURE dbo.sp_GetFragStats -- sp_prefix required @tableName NVARCHAR(128) = NULL, @indexID INT = NULL, @partNumber INT = NULL, @Mode NVARCHAR(20) = N'DETAILED' AS BEGIN SET NOCOUNT ON; SELECT DatabaseName = DB_NAME(), TableName = t.name, IndexName = i.name, IndexID = s.index_id, PercentFragment = s.avg_fragmentation_in_percent, TotalFrags = s.fragment_count, PagesPerFrag = s.avg_fragment_size_in_pages, NumPages = s.page_count, IndexType = s.index_type_desc -- shouldn't s.partition_number be part of the output as well? FROM sys.tables AS t INNER JOIN sys.indexes AS i ON t.[object_id] = i.[object_id] AND i.index_id = COALESCE(@indexID, i.index_id) AND t.name = COALESCE(@tableName, t.name) CROSS APPLY sys.dm_db_index_physical_stats(DB_ID(), t.[object_id], i.index_id, @partNumber, @Mode) AS s WHERE s.avg_fragmentation_in_percent > 10 -- probably also want to filter on minimum page count too -- do you really care about a table that has 100 pages? ORDER BY DatabaseName, TableName, IndexName, PercentFragment DESC; END GO -- needs to be marked as a system object: EXEC sp_MS_MarkSystemObject N'dbo.sp_GetFragStats'; GO
現在,在您的維護數據庫中,創建一個使用動態 SQL 正確設置上下文的包裝器:
USE YourMaintenanceDatabase; GO CREATE PROCEDURE dbo.GetFragStats @DatabaseName SYSNAME, -- can't really be NULL, right? @tableName NVARCHAR(128) = NULL, @indexID INT = NULL, @partNumber INT = NULL, @Mode NVARCHAR(20) = N'DETAILED' AS BEGIN DECLARE @sql NVARCHAR(MAX); SET @sql = N'USE ' + QUOTENAME(@DatabaseName) + '; EXEC dbo.sp_GetFragStats @tableName, @indexID, @partNumber, @Mode;'; EXEC sp_executesql @sql, N'@tableName NVARCHAR(128),@indexID INT,@partNumber INT,@Mode NVARCHAR(20)', @tableName, @indexID, @partNumber, @Mode; END GO
(數據庫名稱不能真正成為
NULL
的原因是因為你不能加入類似的東西,sys.objects
並且sys.indexes
它們獨立存在於每個數據庫中。所以如果你想要實例範圍的資訊,可能會有不同的過程。)現在您可以為任何其他數據庫呼叫它,例如
EXEC YourMaintenanceDatabase.dbo.GetFragStats @DatabaseName = N'AdventureWorks2012', @TableName = N'SalesOrderHeader';
而且您始終可以
synonym
在每個數據庫中創建一個,因此您甚至不必引用維護數據庫的名稱:USE SomeOtherDatabase;`enter code here` GO CREATE SYNONYM dbo.GetFragStats FOR YourMaintenanceDatabase.dbo.GetFragStats;
另一種方法是使用動態 SQL,但是這也一次只適用於一個數據庫:
USE YourMaintenanceDatabase; GO CREATE PROCEDURE dbo.GetFragStats @DatabaseName SYSNAME, @tableName NVARCHAR(128) = NULL, @indexID INT = NULL, @partNumber INT = NULL, @Mode NVARCHAR(20) = N'DETAILED' AS BEGIN SET NOCOUNT ON; DECLARE @sql NVARCHAR(MAX) = N'SELECT DatabaseName = @DatabaseName, TableName = t.name, IndexName = i.name, IndexID = s.index_id, PercentFragment = s.avg_fragmentation_in_percent, TotalFrags = s.fragment_count, PagesPerFrag = s.avg_fragment_size_in_pages, NumPages = s.page_count, IndexType = s.index_type_desc FROM ' + QUOTENAME(@DatabaseName) + '.sys.tables AS t INNER JOIN ' + QUOTENAME(@DatabaseName) + '.sys.indexes AS i ON t.[object_id] = i.[object_id] AND i.index_id = COALESCE(@indexID, i.index_id) AND t.name = COALESCE(@tableName, t.name) CROSS APPLY ' + QUOTENAME(@DatabaseName) + '.sys.dm_db_index_physical_stats( DB_ID(@DatabaseName), t.[object_id], i.index_id, @partNumber, @Mode) AS s WHERE s.avg_fragmentation_in_percent > 10 ORDER BY DatabaseName, TableName, IndexName, PercentFragment DESC;'; EXEC sp_executesql @sql, N'@DatabaseName SYSNAME, @tableName NVARCHAR(128), @indexID INT, @partNumber INT, @Mode NVARCHAR(20)', @DatabaseName, @tableName, @indexID, @partNumber, @Mode; END GO
另一種方法是創建一個視圖(或表值函式)來合併所有數據庫的表和索引名稱,但是您必須將數據庫名稱硬編碼到視圖中,並在添加時維護它們/刪除您希望允許包含在此查詢中的數據庫。與其他方法不同,這將允許您一次檢索多個數據庫的統計資訊。
一、觀點:
CREATE VIEW dbo.CertainTablesAndIndexes AS SELECT db = N'AdventureWorks2012', t.[object_id], [table] = t.name, i.index_id, [index] = i.name FROM AdventureWorks2012.sys.tables AS t INNER JOIN AdventureWorks2012.sys.indexes AS i ON t.[object_id] = i.[object_id] UNION ALL SELECT db = N'database2', t.[object_id], [table] = t.name, i.index_id, [index] = i.name FROM database2.sys.tables AS t INNER JOIN database2.sys.indexes AS i ON t.[object_id] = i.[object_id] -- ... UNION ALL ... ; GO
然後程序:
CREATE PROCEDURE dbo.GetFragStats @DatabaseName NVARCHAR(128) = NULL, @tableName NVARCHAR(128) = NULL, @indexID INT = NULL, @partNumber INT = NULL, @Mode NVARCHAR(20) = N'DETAILED' AS BEGIN SET NOCOUNT ON; SELECT DatabaseName = DB_NAME(s.database_id), TableName = v.[table], IndexName = v.[index], IndexID = s.index_id, PercentFragment = s.avg_fragmentation_in_percent, TotalFrags = s.fragment_count, PagesPerFrag = s.avg_fragment_size_in_pages, NumPages = s.page_count, IndexType = s.index_type_desc FROM dbo.CertainTablesAndIndexes AS v CROSS APPLY sys.dm_db_index_physical_stats (DB_ID(v.db), v.[object_id], v.index_id, @partNumber, @Mode) AS s WHERE s.avg_fragmentation_in_percent > 10 AND v.index_id = COALESCE(@indexID, v.index_id) AND v.[table] = COALESCE(@tableName, v.[table]) AND v.db = COALESCE(@DatabaseName, v.db) ORDER BY DatabaseName, TableName, IndexName, PercentFragment DESC; END GO