Jako SQL Server DBA se vždy staráme o jednu z nejdůležitějších věcí pro podnikání, o data. V některých případech mohou být aplikace poměrně složité a vy skončíte s tunou databázových tabulek roztroušených po instancích SQL Serveru. To může vést k několika nepříjemnostem, jako například:
- Vědět, jak se vaše data chovají každý den, pokud jde o trendy růstu (prostor a/nebo počet řádků).
- Vědět, jaké databázové tabulky vyžadují (nebo budou vyžadovat) konkrétní/odlišnou strategii pro ukládání dat, protože se příliš rychle rozrůstají.
- Vědět, které z vašich databázových tabulek zabírají příliš mnoho místa, což může vést k omezením úložiště.
Vzhledem k důležitosti těchto podrobností jsem vytvořil několik uložených procedur, které mohou být velkou pomocí pro každého SQL Server DBA, který by chtěl sledovat informace týkající se databázových tabulek ve svém prostředí. Věřte mi, jeden z nich je velmi cool.
Počáteční úvahy
- Ujistěte se, že účet spouštějící tuto uloženou proceduru má dostatečná oprávnění. Pravděpodobně byste mohli začít se sysadminem a pak postupovat co nejpodrobněji, abyste se ujistili, že uživatel má minimální oprávnění potřebná pro správné fungování SP.
- Databázové objekty (databázová tabulka a uložená procedura) budou vytvořeny uvnitř databáze vybrané v době spuštění skriptu, takže vybírejte pečlivě.
- Skript je vytvořen tak, aby jej bylo možné provést několikrát, aniž by se vám zobrazila chyba. Pro uloženou proceduru jsem použil příkaz „CREATE OR ALTER PROCEDURE“, dostupný od SQL Server 2016 SP1. Proto se nedivte, že to v dřívější verzi nebude fungovat hladce.
- Neváhejte změnit názvy vytvořených databázových objektů.
- Věnujte pozornost parametrům uložené procedury, která shromažďuje nezpracovaná data. Mohou být zásadní ve výkonné strategii sběru dat pro vizualizaci trendů.
Jak používat uložené procedury?
- Zkopírujte a vložte kód T-SQL (k dispozici v tomto článku).
- První SP očekává 2 parametry:
- @persistData:„Y“, pokud chce správce databází uložit výstup do cílové tabulky, a ‚N‘, pokud chce správce vidět výstup přímo.
- @truncateTable:„Y“ pro zkrácení tabulky před uložením zachycených dat a „N“, pokud jsou v tabulce zachována aktuální data. Mějte na paměti, že hodnota tohoto parametru je irelevantní, pokud je hodnota parametru @persistData ‚N‘.
- Druhý SP očekává 1 parametr:
- @targetParameter:Název sloupce, který má být použit k transpozici shromážděných informací.
Prezentovaná pole a jejich význam
- název_databáze: název databáze, kde se tabulka nachází.
- schéma: název schématu, kde se tabulka nachází.
- název_tabulky: zástupný symbol pro název tabulky.
- row_count: počet řádků, které tabulka aktuálně obsahuje.
- total_space_mb: počet megabajtů přidělených pro tabulku.
- used_space_mb: počet megabajtů skutečně používaných tabulkou.
- unused_space_mb: počet megabajtů, které tabulka nepoužívá.
- created_date: datum/čas vytvoření tabulky.
- data_collection_timestamp: viditelné pouze v případě, že je parametru @persistData předáno „Y“. Používá se ke zjištění, kdy byl SP proveden a informace byly úspěšně uloženy do tabulky DBA_Tables.
Testy provedení
Předvedu několik provedení uložených procedur:
/* Zobrazí informace o tabulkách pro všechny uživatelské databáze */
EXEC GetTablesData @persistData = 'N',@truncateTable = 'N'
/* Zachovat informace databázových tabulek a dotazovat se na cílovou tabulku, přičemž nejprve zkrátíte cílovou tabulku */
EXEC GetTablesData @persistData = 'Y',@truncateTable = 'Y'
SELECT * FROM DBA_Tables
Postranní dotazy
*Dotaz pro zobrazení databázových tabulek seřazených od největšího počtu řádků po nejnižší.
SELECT * FROM DBA_Tables ORDER BY row_count DESC;
*Dotaz pro zobrazení databázových tabulek seřazených od největšího celkového prostoru po nejnižší.
SELECT * FROM DBA_Tables ORDER BY total_space_mb DESC;
*Dotaz pro zobrazení databázových tabulek seřazených od největšího využitého prostoru po nejnižší.
SELECT * FROM DBA_Tables ORDER BY used_space_mb DESC;
*Dotaz pro zobrazení databázových tabulek seřazených od největšího nevyužitého prostoru po nejnižší.
SELECT * FROM DBA_Tables ORDER BY unused_space_mb DESC;
*Dotaz pro zobrazení databázových tabulek seřazených podle data vytvoření, od nejnovější po nejstarší.
SELECT * FROM DBA_Tables ORDER BY created_date DESC;
Zde je úplný kód uložené procedury, který zachycuje informace databázových tabulek:
*Na úplném začátku skriptu uvidíte výchozí hodnotu, kterou uložená procedura předpokládá, pokud není pro každý parametr předána žádná hodnota.
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
Až do tohoto bodu se informace zdají trochu suché, ale dovolte mi změnit toto vnímání prezentací doplňkové uložené procedury. Jeho hlavním účelem je transponovat informace shromážděné v cílové tabulce, která slouží jako zdroj pro zprávy trendů.
Takto můžete spustit uloženou proceduru:
*Pro demonstrační účely jsem do cílové tabulky s názvem t1 vložil manuální záznamy, abych simuloval mé obvyklé provádění uložené procedury.
*Výsledková sada je trochu široká, takže pořídím několik snímků obrazovky, abych ukázal celý výstup.
EXEC TransposeTablesInformation @targetParmeter = 'row_count'
Klíčové poznatky
- Pokud zautomatizujete provádění skriptu, který vyplňuje cílovou tabulku, můžete si okamžitě všimnout, že se s ním nebo s vašimi daty něco pokazilo. Podívejte se na údaje pro tabulku ‚t1‘ a sloupec ‚15‘. Můžete tam vidět NULL, což bylo provedeno záměrně, aby vám ukázalo něco, co se může stát.
- V tomto druhu zobrazení můžete vidět zvláštní chování pro nejdůležitější/kritické databázové tabulky.
- V uvedeném příkladu jsem zvolil pole „row_count“ cílové tabulky, ale jako parametr můžete zvolit jakékoli jiné číselné pole a získat stejný formát tabulky, ale s jinými údaji.
- Nemějte obavy, pokud zadáte neplatný parametr, uložená procedura vás upozorní a zastaví její provádění.
Zde je úplný kód uložené procedury, který transponuje informace cílové tabulky:
*Na úplném začátku skriptu uvidíte výchozí hodnotu, kterou uložená procedura předpokládá, pokud není pro každý parametr předána žádná hodnota.
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
Závěr
- Sběr dat SP můžete nasadit v každé instanci serveru SQL Server pod vaší podporou a implementovat mechanismus upozornění v rámci celého zásobníku podporovaných instancí.
- Pokud implementujete úlohu agenta, která se dotazuje na tyto informace relativně často, můžete zůstat na vrcholu hry, pokud jde o to, jak se vaše data během měsíce chovají. Samozřejmě můžete jít ještě dále a ukládat měsíční shromážděná data, abyste měli ještě větší obrázek; museli byste v kódu provést nějaké úpravy, ale rozhodně by to stálo za to.
- Ujistěte se, že jste tento mechanismus řádně otestovali v prostředí izolovaného prostoru, a když plánujete produkční nasazení, vyberte období s nízkou aktivitou.
- Shromažďování informací tohoto typu může pomoci odlišit DBA od sebe navzájem. Pravděpodobně existují nástroje 3 stran, které dokážou totéž, a dokonce i více, ale ne každý má rozpočet, aby si to mohl dovolit. Doufám, že to může pomoci každému, kdo se rozhodne jej použít ve svém prostředí.