Database
 sql >> Datenbank >  >> RDS >> Database

Gespeicherte Prozedur zum Abrufen von Datenbanktabelleninformationen

Als SQL Server DBAs kümmern wir uns immer um eines der wichtigsten Dinge für das Unternehmen, die Daten. In einigen Fällen können Anwendungen ziemlich komplex werden, und Sie haben am Ende eine Menge Datenbanktabellen, die über Ihre SQL Server-Instanz(en) verstreut sind. Dies kann zu einigen Unannehmlichkeiten führen, wie zum Beispiel:

  • Zu wissen, wie sich Ihre Daten jeden Tag in Bezug auf Wachstumstrends (Platz und/oder Anzahl der Zeilen) verhalten.
  • Zu wissen, welche Datenbanktabellen eine bestimmte/andere Strategie zum Speichern der Daten erfordern (oder erfordern werden), weil sie zu schnell wachsen.
  • Zu wissen, welche Ihrer Datenbanktabellen zu viel Speicherplatz beanspruchen, was möglicherweise zu Speicherbeschränkungen führt.

Aufgrund der Bedeutung dieser Details habe ich einige gespeicherte Prozeduren erstellt, die für jeden SQL Server-DBA, der Informationen zu Datenbanktabellen in seiner Umgebung verfolgen möchte, eine große Hilfe sein können. Vertrauen Sie mir, einer von ihnen ist sehr cool.

Erste Überlegungen

  • Stellen Sie sicher, dass das Konto, das diese gespeicherte Prozedur ausführt, über ausreichende Berechtigungen verfügt. Sie könnten wahrscheinlich mit sysadmin beginnen und dann so granular wie möglich vorgehen, um sicherzustellen, dass der Benutzer über die Mindestberechtigungen verfügt, die erforderlich sind, damit der SP ordnungsgemäß funktioniert.
  • Die Datenbankobjekte (Datenbanktabelle und gespeicherte Prozedur) werden innerhalb der Datenbank erstellt, die zum Zeitpunkt der Ausführung des Skripts ausgewählt wurde, wählen Sie also sorgfältig aus.
  • Das Skript ist so aufgebaut, dass es mehrmals ausgeführt werden kann, ohne dass Ihnen eine Fehlermeldung angezeigt wird. Für die gespeicherte Prozedur habe ich die Anweisung „CREATE OR ALTER PROCEDURE“ verwendet, die seit SQL Server 2016 SP1 verfügbar ist. Wundern Sie sich deshalb nicht, wenn es in einer früheren Version nicht reibungslos funktioniert.
  • Ändern Sie gerne die Namen der erstellten Datenbankobjekte.
  • Achten Sie auf die Parameter der gespeicherten Prozedur, die die Rohdaten sammelt. Sie können in einer leistungsstarken Datenerfassungsstrategie zur Visualisierung von Trends von entscheidender Bedeutung sein.

Wie verwende ich die gespeicherten Prozeduren?

  1. Kopieren Sie den T-SQL-Code und fügen Sie ihn ein (verfügbar in diesem Artikel).
  2. Der erste SP erwartet 2 Parameter:
    1. @persistData:„Y“, wenn ein DBA die Ausgabe in einer Zieltabelle speichern möchte, und „N“, wenn der DBA die Ausgabe direkt sehen möchte.
    2. @truncateTable:„Y“, um die Tabelle zuerst zu kürzen, bevor die erfassten Daten gespeichert werden, und „N“, wenn die aktuellen Daten in der Tabelle verbleiben. Denken Sie daran, dass der Wert dieses Parameters irrelevant ist, wenn der Wert des @persistData-Parameters „N“ ist.
  3. Der zweite SP erwartet 1 Parameter:
    1. @targetParameter:Der Name der Spalte, die zum Transponieren der gesammelten Informationen verwendet werden soll.

Dargestellte Felder und ihre Bedeutung

  • Datenbankname: der Name der Datenbank, in der sich die Tabelle befindet.
  • Schema: der Name des Schemas, in dem sich die Tabelle befindet.
  • Tabellenname: der Platzhalter für den Namen der Tabelle.
  • row_count: die Anzahl der Zeilen, die die Tabelle derzeit hat.
  • total_space_mb: die Anzahl der für die Tabelle zugewiesenen MegaBytes.
  • used_space_mb: die Anzahl der Megabytes, die tatsächlich von der Tabelle verwendet werden.
  • unused_space_mb: die Anzahl der Megabytes, die die Tabelle nicht verwendet.
  • Erstellungsdatum: das Datum/die Uhrzeit, wann die Tabelle erstellt wurde.
  • data_collection_timestamp: nur sichtbar, wenn „Y“ an den Parameter @persistData übergeben wird. Es wird verwendet, um zu wissen, wann der SP ausgeführt und die Informationen erfolgreich in der Tabelle DBA_Tables gespeichert wurden.

Ausführungstests

Ich werde einige Ausführungen der Stored Procedures demonstrieren:

/* Tabelleninformationen für alle Benutzerdatenbanken anzeigen */

EXEC GetTablesData @persistData = 'N',@truncateTable = 'N'

/* Die Informationen der Datenbanktabellen beibehalten und die Zieltabelle abfragen, wobei die Zieltabelle zuerst abgeschnitten wird */

EXEC GetTablesData @persistData = 'Y',@truncateTable = 'Y'
SELECT * FROM DBA_Tables

Nebenabfragen

*Abfrage, um die Datenbanktabellen von der größten Anzahl an Zeilen zur niedrigsten sortiert anzuzeigen.

SELECT * FROM DBA_Tables ORDER BY row_count DESC;

*Abfrage, um die Datenbanktabellen sortiert vom größten zum niedrigsten Gesamtspeicherplatz anzuzeigen.

SELECT * FROM DBA_Tables ORDER BY total_space_mb DESC;

*Abfrage, um die Datenbanktabellen sortiert vom größten belegten Speicherplatz zum niedrigsten anzuzeigen.

SELECT * FROM DBA_Tables ORDER BY unused_space_mb DESC;

*Abfrage, um die Datenbanktabellen sortiert vom größten ungenutzten Speicherplatz zum niedrigsten anzuzeigen.

SELECT * FROM DBA_Tables ORDER BY unused_space_mb DESC;

*Abfrage, um die Datenbanktabellen nach Erstellungsdatum sortiert anzuzeigen, von der neuesten bis zur ältesten.

SELECT * FROM DBA_Tables ORDER BY created_date DESC;

Hier ist ein vollständiger Code der gespeicherten Prozedur, die die Informationen der Datenbanktabellen erfasst:

*Ganz am Anfang des Skripts sehen Sie den Standardwert, den die gespeicherte Prozedur annimmt, wenn für jeden Parameter kein Wert übergeben wird.

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

Bis zu diesem Punkt scheinen die Informationen etwas trocken zu sein, aber lassen Sie mich diese Wahrnehmung mit der Präsentation einer ergänzenden gespeicherten Prozedur ändern. Sein Hauptzweck besteht darin, die gesammelten Informationen in die Zieltabelle zu übertragen, die als Quelle für Trendberichte dient.

So können Sie die Stored Procedure ausführen:

*Zu Demonstrationszwecken habe ich manuelle Datensätze in die Zieltabelle mit dem Namen t1 eingefügt, um meine übliche Ausführung einer gespeicherten Prozedur zu simulieren.

*Die Ergebnismenge ist etwas breit, daher werde ich ein paar Screenshots machen, um die vollständige Ausgabe zu zeigen.

EXEC TransposeTablesInformation @targetParmeter = 'row_count' 

Schlüsselmitnahmen

  • Wenn Sie die Ausführung des Skripts automatisieren, das die Zieltabelle füllt, können Sie sofort feststellen, ob etwas damit oder mit Ihren Daten schief gelaufen ist. Sehen Sie sich die Daten für die Tabelle „t1“ und die Spalte „15“ an. Sie können dort NULL sehen, was absichtlich gemacht wurde, um Ihnen etwas zu zeigen, das passieren könnte.
  • Bei dieser Art von Ansicht können Sie ein eigenartiges Verhalten für die wichtigsten/kritischsten Datenbanktabellen erkennen.
  • Im gegebenen Beispiel habe ich das Feld „row_count“ der Zieltabelle ausgewählt, aber Sie können jedes andere numerische Feld als Parameter auswählen und erhalten das gleiche Tabellenformat, aber mit anderen Daten.
  • Keine Sorge, wenn Sie einen ungültigen Parameter angeben, wird die gespeicherte Prozedur Sie warnen und ihre Ausführung stoppen.

Hier ist ein vollständiger Code der gespeicherten Prozedur, die die Informationen der Zieltabelle transponiert:

*Ganz am Anfang des Skripts sehen Sie den Standardwert, den die gespeicherte Prozedur annimmt, wenn für jeden Parameter kein Wert übergeben wird.

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

Schlussfolgerung

  • Sie können den Datenerfassungs-SP in jeder von Ihnen unterstützten SQL Server-Instanz bereitstellen und einen Warnmechanismus für Ihren gesamten Stapel unterstützter Instanzen implementieren.
  • Wenn Sie einen Agentenjob implementieren, der diese Informationen relativ häufig abfragt, können Sie in Bezug auf das Verhalten Ihrer Daten im Laufe des Monats auf dem Laufenden bleiben. Natürlich können Sie noch weiter gehen und die monatlich gesammelten Daten speichern, um ein noch größeres Bild zu erhalten; Sie müssten einige Änderungen am Code vornehmen, aber es würde sich absolut lohnen.
  • Stellen Sie sicher, dass Sie diesen Mechanismus ordnungsgemäß in einer Sandbox-Umgebung testen, und stellen Sie sicher, dass Sie Zeiträume mit geringer Aktivität wählen, wenn Sie eine Produktionsbereitstellung planen.
  • Das Sammeln von Informationen dieser Art kann helfen, DBAs voneinander zu unterscheiden. Es gibt wahrscheinlich Tools von Drittanbietern, die das Gleiche können und sogar noch mehr, aber nicht jeder hat das Budget, um es sich zu leisten. Ich hoffe, dies kann jedem helfen, der sich entscheidet, es in seiner Umgebung zu verwenden.