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?
- Kopieren Sie den T-SQL-Code und fügen Sie ihn ein (verfügbar in diesem Artikel).
- Der erste SP erwartet 2 Parameter:
- @persistData:„Y“, wenn ein DBA die Ausgabe in einer Zieltabelle speichern möchte, und „N“, wenn der DBA die Ausgabe direkt sehen möchte.
- @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.
- Der zweite SP erwartet 1 Parameter:
- @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.