SQL Server DBAとして、私たちは常にビジネスにとって最も重要なことの1つであるデータを処理します。場合によっては、アプリケーションが非常に複雑になり、SQLServerインスタンスに大量のデータベーステーブルが散在することになります。これにより、次のようないくつかの不便が生じる可能性があります。
- 成長傾向(行のスペースや量)の観点から、データが毎日どのように動作するかを知る。
- データの増大が速すぎるため、データを格納するために特定の/異なる戦略が必要な(または必要になる)データベーステーブルを把握する。
- どのデータベーステーブルがスペースを取りすぎているかを知ると、ストレージの制約につながる可能性があります。
これらの詳細が重要であるため、環境内のデータベーステーブルに関する情報を追跡したいSQLServerDBAに非常に役立つストアドプロシージャをいくつか作成しました。私を信じてください、そのうちの1つはとてもクールです。
最初の考慮事項
- このストアドプロシージャを実行するアカウントに十分な権限があることを確認してください。おそらく、sysadminから始めて、SPが正しく機能するために必要な最小限の特権をユーザーが持っていることを確認するために、可能な限り細かく調整することができます。
- データベースオブジェクト(データベーステーブルとストアドプロシージャ)は、スクリプトの実行時に選択されたデータベース内に作成されるため、慎重に選択してください。
- スクリプトは、エラーが発生することなく数回実行できるように作成されています。ストアドプロシージャには、SQL Server2016SP1以降で使用可能な「CREATEORALTERPROCEDURE」ステートメントを使用しました。そのため、以前のバージョンでスムーズに機能しなくても驚かないでください。
- 作成したデータベースオブジェクトの名前は自由に変更してください。
- 生データを収集するストアドプロシージャのパラメータに注意してください。これらは、傾向を視覚化するための強力なデータ収集戦略において重要になる可能性があります。
ストアドプロシージャの使用方法
- T-SQLコード(この記事内で利用可能)をコピーして貼り付けます。
- 最初のSPは2つのパラメーターを想定しています。
- @ persistData:DBAが出力をターゲットテーブルに保存する場合は「Y」、DBAが出力を直接表示する場合は「N」。
- @truncateTable:「Y」はキャプチャされたデータを保存する前に最初にテーブルを切り捨てます。「N」は現在のデータがテーブルに保持されている場合です。 @persistDataパラメータの値が「N」の場合、このパラメータの値は関係ないことに注意してください。
- 2番目のSPは1つのパラメーターを想定しています。
- @targetParameter:収集された情報を転置するために使用される列の名前。
提示されたフィールドとその意味
- database_name: テーブルが存在するデータベースの名前。
- スキーマ: テーブルが存在するスキーマの名前。
- table_name: テーブル名のプレースホルダー。
- row_count: テーブルに現在ある行数。
- total_space_mb: テーブルに割り当てられたメガバイト数。
- used_space_mb: テーブルで実際に使用されているメガバイト数。
- unused_space_mb: テーブルが使用していないメガバイト数。
- created_date: テーブルが作成された日時。
- data_collection_timestamp: 「Y」が@persistDataパラメータに渡された場合にのみ表示されます。これは、SPがいつ実行され、情報がDBA_Tablesテーブルに正常に保存されたかを知るために使用されます。
実行テスト
ストアドプロシージャのいくつかの実行を示します:
/*すべてのユーザーデータベースのテーブル情報を表示します*/
EXEC GetTablesData @persistData = 'N',@truncateTable = 'N'
/ *データベーステーブルの情報を永続化し、ターゲットテーブルをクエリして、最初にターゲットテーブルを切り捨てます* /
EXEC GetTablesData @persistData = 'Y',@truncateTable = 'Y'
SELECT * FROM DBA_Tables
サイドクエリ
*最大行数から最小行数に並べ替えられたデータベーステーブルを表示するためのクエリ。
SELECT * FROM DBA_Tables ORDER BY row_count DESC;
*最大の合計スペースから最小の合計スペースにソートされたデータベーステーブルを表示するためのクエリ。
SELECT * FROM DBA_Tables ORDER BY total_space_mb DESC;
*最大の使用済みスペースから最小のスペースにソートされたデータベーステーブルを表示するためのクエリ。
SELECT * FROM DBA_Tables ORDER BY unused_space_mb DESC;
*最大の未使用スペースから最小のスペースにソートされたデータベーステーブルを表示するためのクエリ。
SELECT * FROM DBA_Tables ORDER BY unused_space_mb DESC;
*作成日から新しいものから古いものへと並べ替えられたデータベーステーブルを表示するためのクエリ。
SELECT * FROM DBA_Tables ORDER BY created_date DESC;
データベーステーブルの情報をキャプチャするストアドプロシージャの完全なコードは次のとおりです。
*スクリプトの最初に、各パラメーターに値が渡されない場合にストアドプロシージャが想定するデフォルト値が表示されます。
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE OR ALTER PROCEDURE [dbo].[GetTablesData]
@persistData CHAR(1) = 'Y',
@truncateTable CHAR(1) = 'Y'
AS
BEGIN
SET NOCOUNT ON
DECLARE @command NVARCHAR(MAX)
DECLARE @Tmp_TablesInformation TABLE(
[database] [VARCHAR](255) NOT NULL,
[schema] [VARCHAR](64) NOT NULL,
[table] [VARCHAR](255) NOT NULL,
[row_count] [BIGINT]NOT NULL,
[total_space_mb] [DECIMAL](15,2) NOT NULL,
[used_space_mb] [DECIMAL](15,2) NOT NULL,
[unused_space_mb] [DECIMAL](15,2) NOT NULL,
[created_date] [DATETIME] NOT NULL
)
SELECT @command = '
USE [?]
IF DB_ID(''?'') > 4
BEGIN
SELECT
''?'',
s.Name AS [schema],
t.NAME AS [table],
p.rows AS row_count,
CAST(ROUND(((SUM(a.total_pages) * 8) / 1024.00), 2) AS DECIMAL(15, 2)) AS total_space_mb,
CAST(ROUND(((SUM(a.used_pages) * 8) / 1024.00), 2) AS DECIMAL(15, 2)) AS used_space_mb,
CAST(ROUND(((SUM(a.total_pages) - SUM(a.used_pages)) * 8) / 1024.00, 2) AS DECIMAL(15, 2)) AS unused_space_mb,
t.create_date as created_date
FROM sys.tables t
INNER JOIN sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN sys.allocation_units a ON p.partition_id = a.container_id
LEFT OUTER JOIN sys.schemas s ON t.schema_id = s.schema_id
WHERE t.NAME NOT LIKE ''dt%''
AND t.is_ms_shipped = 0
AND i.OBJECT_ID > 255
GROUP BY t.Name, s.Name, p.Rows,t.create_date
ORDER BY total_space_mb DESC, t.Name
END'
INSERT INTO @Tmp_TablesInformation
EXEC sp_MSForEachDB @command
IF @persistData = 'N'
SELECT * FROM @Tmp_TablesInformation
ELSE
BEGIN
IF(@truncateTable = 'Y')
TRUNCATE TABLE DBA_Tables
INSERT INTO DBA_Tables
SELECT *,GETDATE() FROM @Tmp_TablesInformation ORDER BY [database],[schema],[table]
END
END
GO
これまでのところ、情報は少し乾燥しているように見えますが、補足的なストアドプロシージャの提示でその認識を変えさせてください。その主な目的は、トレンドレポートのソースとして機能するターゲットテーブルで収集された情報を転置することです。
ストアドプロシージャを実行する方法は次のとおりです。
*デモンストレーションの目的で、通常のストアドプロシージャの実行をシミュレートするために、t1という名前のターゲットテーブルに手動レコードを挿入しました。
*結果セットは少し広いので、完全な出力を表示するためにスクリーンショットをいくつか撮ります。
EXEC TransposeTablesInformation @targetParmeter = 'row_count'
重要なポイント
- ターゲットテーブルにデータを入力するスクリプトの実行を自動化すると、スクリプトまたはデータに問題が発生したかどうかをすぐに確認できます。テーブル「t1」と列「15」のデータを見てください。何かが起こる可能性があることを示すために意図的に行われたNULLをそこに見ることができます。
- この種のビューを使用すると、最も重要な/重要なデータベーステーブルの固有の動作を確認できます。
- この例では、ターゲットテーブルの「row_count」フィールドを選択しましたが、パラメータとして他の数値フィールドを選択して、同じテーブル形式を取得できますが、データは異なります。
- 心配しないでください。無効なパラメータを指定すると、ストアドプロシージャが警告を発し、実行を停止します。
ターゲットテーブルの情報を置き換えるストアドプロシージャの完全なコードは次のとおりです。
*スクリプトの最初に、各パラメーターに値が渡されない場合にストアドプロシージャが想定するデフォルト値が表示されます。
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE OR ALTER PROCEDURE [dbo].[TransposeTablesInformation]
@targetParameter NVARCHAR(15) = 'row_count'
AS
BEGIN
SET NOCOUNT ON;
IF (@targetParameter <> 'row_count' AND @targetParameter <> 'total_space_mb' AND @targetParameter <> 'used_space_mb' AND @targetParameter <> 'unused_space_mb')
BEGIN
PRINT 'Please specify a valid parameter!'
PRINT 'i.e. row_count | total_space_mb | used_space_mb | unused_space_mb'
RETURN
END
ELSE
BEGIN
CREATE TABLE #TablesInformation(
[database] [VARCHAR](255) NOT NULL,
[schema] [VARCHAR](64) NOT NULL,
[table] [VARCHAR](255) NOT NULL,
[1] [DECIMAL](10,2) NULL,
[2] [DECIMAL](10,2) NULL,
[3] [DECIMAL](10,2) NULL,
[4] [DECIMAL](10,2) NULL,
[5] [DECIMAL](10,2) NULL,
[6] [DECIMAL](10,2) NULL,
[7] [DECIMAL](10,2) NULL,
[8] [DECIMAL](10,2) NULL,
[9] [DECIMAL](10,2) NULL,
[10] [DECIMAL](10,2) NULL,
[11] [DECIMAL](10,2) NULL,
[12] [DECIMAL](10,2) NULL,
[13] [DECIMAL](10,2) NULL,
[14] [DECIMAL](10,2) NULL,
[15] [DECIMAL](10,2) NULL,
[16] [DECIMAL](10,2) NULL,
[17] [DECIMAL](10,2) NULL,
[18] [DECIMAL](10,2) NULL,
[19] [DECIMAL](10,2) NULL,
[20] [DECIMAL](10,2) NULL,
[21] [DECIMAL](10,2) NULL,
[22] [DECIMAL](10,2) NULL,
[23] [DECIMAL](10,2) NULL,
[24] [DECIMAL](10,2) NULL,
[25] [DECIMAL](10,2) NULL,
[26] [DECIMAL](10,2) NULL,
[27] [DECIMAL](10,2) NULL,
[28] [DECIMAL](10,2) NULL,
[29] [DECIMAL](10,2) NULL,
[30] [DECIMAL](10,2) NULL,
[31] [DECIMAL](10,2) NULL
)
INSERT INTO #TablesInformation([database],[schema],[table])
SELECT DISTINCT [database_name],[schema],[table_name]
FROM DBA_Tables
ORDER BY [database_name],[schema],table_name
DECLARE @databaseName NVARCHAR(255)
DECLARE @schemaName NVARCHAR(64)
DECLARE @tableName NVARCHAR(255)
DECLARE @value DECIMAL(10,2)
DECLARE @dataTimestamp DATETIME
DECLARE @sqlCommand NVARCHAR(MAX)
IF(@targetParameter = 'row_count')
BEGIN
DECLARE TablesCursor CURSOR FOR
SELECT
[database_name],
[schema],
[table_name],
[row_count],
[data_collection_timestamp]
FROM DBA_Tables
ORDER BY [database_name],[schema],table_name
END
IF(@targetParameter = 'total_space_mb')
BEGIN
DECLARE TablesCursor CURSOR FOR
SELECT
[database_name],
[schema],
[table_name],
[total_space_mb],
[data_collection_timestamp]
FROM DBA_Tables
ORDER BY [database_name],[schema],table_name
END
IF(@targetParameter = 'used_space_mb')
BEGIN
DECLARE TablesCursor CURSOR FOR
SELECT
[database_name],
[schema],
[table_name],
[used_space_mb],
[data_collection_timestamp]
FROM DBA_Tables
ORDER BY [database_name],[schema],table_name
END
IF(@targetParameter = 'unused_space_mb')
BEGIN
DECLARE TablesCursor CURSOR FOR
SELECT
[database_name],
[schema],
[table_name],
[unused_space_mb],
[data_collection_timestamp]
FROM DBA_Tables
ORDER BY [database_name],[schema],table_name
END
OPEN TablesCursor
FETCH NEXT FROM TablesCursor INTO @databaseName,@schemaName,@tableName,@value,@dataTimestamp
WHILE(@@FETCH_STATUS = 0)
BEGIN
SET @sqlCommand = CONCAT('
UPDATE #TablesInformation
SET [',DAY(@dataTimestamp),'] = ',@value,'
WHERE [database] = ',CHAR(39),@databaseName,CHAR(39),'
AND [schema] = ',CHAR(39),@schemaName+CHAR(39),'
AND [table] = ',CHAR(39),@tableName+CHAR(39),'
')
EXEC(@sqlCommand)
FETCH NEXT FROM TablesCursor INTO @databaseName,@schemaName,@tableName,@value,@dataTimestamp
END
CLOSE TablesCursor
DEALLOCATE TablesCursor
IF(@targetParameter = 'row_count')
SELECT [database],
[schema],
[table],
CONVERT(INT,[1]) AS [1],
CONVERT(INT,[2]) AS [2],
CONVERT(INT,[3]) AS [3],
CONVERT(INT,[4]) AS [4],
CONVERT(INT,[5]) AS [5],
CONVERT(INT,[6]) AS [6],
CONVERT(INT,[7]) AS [7],
CONVERT(INT,[8]) AS [8],
CONVERT(INT,[9]) AS [9],
CONVERT(INT,[10]) AS [10],
CONVERT(INT,[11]) AS [11],
CONVERT(INT,[12]) AS [12],
CONVERT(INT,[13]) AS [13],
CONVERT(INT,[14]) AS [14],
CONVERT(INT,[15]) AS [15],
CONVERT(INT,[16]) AS [16],
CONVERT(INT,[17]) AS [17],
CONVERT(INT,[18]) AS [18],
CONVERT(INT,[19]) AS [19],
CONVERT(INT,[20]) AS [20],
CONVERT(INT,[21]) AS [21],
CONVERT(INT,[22]) AS [22],
CONVERT(INT,[23]) AS [23],
CONVERT(INT,[24]) AS [24],
CONVERT(INT,[25]) AS [25],
CONVERT(INT,[26]) AS [26],
CONVERT(INT,[27]) AS [27],
CONVERT(INT,[28]) AS [28],
CONVERT(INT,[29]) AS [29],
CONVERT(INT,[30]) AS [30],
CONVERT(INT,[31]) AS [31]
FROM #TablesInformation
ELSE
SELECT * FROM #TablesInformation
END
END
GO
結論
- サポート下のすべてのSQLServerインスタンスにデータ収集SPを展開し、サポートされているインスタンスのスタック全体にアラートメカニズムを実装できます。
- この情報を比較的頻繁に照会するエージェントジョブを実装する場合、その月のデータの動作を把握するという点で、ゲームのトップを維持できます。もちろん、さらに進んで、毎月収集されたデータを保存して、さらに全体像を把握することもできます。コードを微調整する必要がありますが、それだけの価値はあります。
- サンドボックス環境でこのメカニズムを適切にテストし、本番環境への展開を計画している場合は、アクティビティの少ない期間を選択してください。
- このタイプの情報を収集すると、DBAを互いに区別するのに役立ちます。同じことを実行できるサードパーティのツールはおそらく3つあり、それ以上のツールがありますが、誰もがそれを購入できる予算があるわけではありません。これが、自分の環境で使用することを決定した人の助けになることを願っています。