sql >> Database teknologi >  >> RDS >> Database

Lagret procedure for at få oplysninger om databasetabeller

Som SQL Server DBA'er tager vi os altid af en af ​​de vigtigste ting for virksomheden, nemlig dataene. I nogle tilfælde kan applikationer blive ret komplekse, og du ender med et væld af databasetabeller spredt rundt om dine SQL Server-forekomster. Dette kan medføre nogle få gener, såsom:

  • Ved, hvordan dine data opfører sig hver dag med hensyn til væksttendenser (plads og/eller antal rækker).
  • Vide, hvad databasetabeller kræver (eller vil kræve) en bestemt/anden strategi for at gemme dataene, fordi de vokser for hurtigt.
  • At vide, hvilke af dine databasetabeller der tager for meget plads, hvilket muligvis fører til lagerbegrænsninger.

På grund af vigtigheden af ​​disse detaljer, har jeg lavet et par Stored Procedures, som kan være til stor hjælp for enhver SQL Server DBA, der gerne vil holde styr på information vedrørende databasetabeller i sit miljø. Tro mig, en af ​​dem er meget sej.

Indledende overvejelser

  • Sørg for, at kontoen, der udfører denne lagrede procedure, har nok privilegier. Du kunne sandsynligvis starte med sysadmin og derefter gå så detaljeret som muligt for at sikre, at brugeren har det minimum af privilegier, der kræves for at SP'en fungerer korrekt.
  • Databaseobjekterne (databasetabel og lagret procedure) vil blive oprettet i den database, der er valgt på det tidspunkt, hvor scriptet udføres, så vælg med omhu.
  • Scriptet er lavet på en måde, det kan udføres flere gange uden at få en fejlmeddelelse til dig. Til den lagrede procedure brugte jeg "CREATE OR ALTER PROCEDURE"-sætning, tilgængelig siden SQL Server 2016 SP1. Derfor skal du ikke blive overrasket, hvis det ikke fungerer problemfrit i en tidligere version.
  • Du er velkommen til at ændre navnene på de oprettede databaseobjekter.
  • Vær opmærksom på parametrene for den lagrede procedure, der indsamler de rå data. De kan være afgørende i en kraftfuld dataindsamlingsstrategi for at visualisere tendenser.

Hvordan bruges de lagrede procedurer?

  1. Kopiér og indsæt T-SQL-koden (tilgængelig i denne artikel).
  2. Den første SP forventer 2 parametre:
    1. @persistData:'Y', hvis en DBA ønsker at gemme outputtet i en måltabel, og 'N', hvis DBA'en ønsker at se outputtet direkte.
    2. @truncateTable:'Y' for at afkorte tabellen først, før de registrerede data gemmes, og 'N', hvis de aktuelle data bevares i tabellen. Husk, at værdien af ​​denne parameter er irrelevant, hvis værdien af ​​@persistData-parameteren er 'N'.
  3. Den anden SP forventer 1 parameter:
    1. @targetParameter:Navnet på den kolonne, der skal bruges til at transponere de indsamlede oplysninger.

Felter præsenteret og deres betydning

  • databasenavn: navnet på den database, hvor tabellen ligger.
  • skema: navnet på det skema, hvor tabellen ligger.
  • tabelnavn: pladsholderen for tabellens navn.
  • row_count: antallet af rækker, som tabellen har i øjeblikket.
  • total_space_mb: antallet af MegaBytes tildelt til tabellen.
  • used_space_mb: antallet af MegaBytes, der faktisk bruges af tabellen.
  • unused_space_mb: antallet af MegaBytes, som tabellen ikke bruger.
  • created_date: dato/klokkeslæt, hvor tabellen blev oprettet.
  • data_indsamling_tidsstempel: kun synlig, hvis 'Y' sendes til parameteren @persistData. Det bruges til at vide, hvornår SP'en blev udført, og oplysningerne blev gemt i DBA_Tables-tabellen.

Udførelsestest

Jeg vil demonstrere et par udførelser af de lagrede procedurer:

/* Vis tabeloplysningerne for alle brugerdatabaser */

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

/* Bevar informationen fra databasetabellerne og forespørg måltabellen, afkort måltabellen først */

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

Sideforespørgsler

*Forespørgsel for at se databasetabellerne sorteret fra det største antal rækker til det laveste.

SELECT * FROM DBA_Tables ORDER BY row_count DESC;

*Forespørgsel for at se databasetabellerne sorteret fra den største samlede plads til den laveste.

SELECT * FROM DBA_Tables ORDER BY total_space_mb DESC;

*Forespørgsel for at se databasetabellerne sorteret fra den største brugte plads til den laveste.

SELECT * FROM DBA_Tables ORDER BY used_space_mb DESC;

*Forespørgsel for at se databasetabellerne sorteret fra den største ubrugte plads til den laveste.

SELECT * FROM DBA_Tables ORDER BY unused_space_mb DESC;

*Forespørgsel for at se databasetabellerne sorteret efter oprettelsesdato, fra den nyeste til den ældste.

SELECT * FROM DBA_Tables ORDER BY created_date DESC;

Her er en komplet kode for den lagrede procedure, der fanger oplysningerne fra databasetabellerne:

*I begyndelsen af ​​scriptet vil du se standardværdien, som den lagrede procedure antager, hvis der ikke sendes nogen værdi for hver parameter.

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

Indtil nu virker informationen en smule tør, men lad mig ændre denne opfattelse med præsentationen af ​​en supplerende lagret procedure. Dens hovedformål er at transponere de oplysninger, der er indsamlet i måltabellen, der tjener som kilde til trendrapporter.

Sådan kan du udføre den lagrede procedure:

*Til demonstrationsformål har jeg indsat manuelle poster i måltabellen ved navn t1 for at simulere min sædvanlige udførelse af Stored Procedure.

*Resultatsættet er lidt bredt, så jeg vil tage et par skærmbilleder for at vise det fulde output.

EXEC TransposeTablesInformation @targetParmeter = 'row_count' 

Vigtige ting

  • Hvis du automatiserer udførelsen af ​​scriptet, der udfylder måltabellen, kan du straks bemærke, om noget gik galt med den eller dine data. Tag et kig på dataene for tabel 't1' og kolonnen '15'. Du kan se NULL der, hvilket blev gjort med vilje for at vise dig noget, der kunne ske.
  • Med denne form for visning kan du se en ejendommelig adfærd for de vigtigste/kritiske databasetabeller.
  • I det givne eksempel har jeg valgt feltet 'row_count' i måltabellen, men du kan vælge et hvilket som helst andet numerisk felt som parameter og få det samme tabelformat, men med andre data.
  • Bare rolig, hvis du angiver en ugyldig parameter, vil den lagrede procedure advare dig og stoppe dens udførelse.

Her er en komplet kode for den lagrede procedure, der transponerer informationen fra måltabellen:

*I begyndelsen af ​​scriptet vil du se standardværdien, som den lagrede procedure antager, hvis der ikke sendes nogen værdi for hver parameter.

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

Konklusion

  • Du kan implementere dataindsamlings-SP'en i hver SQL Server-instans under din support og implementere en advarselsmekanisme på tværs af hele din stak af understøttede instanser.
  • Hvis du implementerer et agentjob, der forespørger om disse oplysninger relativt ofte, kan du være på forkant med spillet med hensyn til at vide, hvordan dine data opfører sig i løbet af måneden. Selvfølgelig kan du gå endnu længere og gemme de månedlige indsamlede data for at få et endnu større billede; du bliver nødt til at lave nogle justeringer af koden, men det ville være det hele værd.
  • Sørg for at teste denne mekanisme korrekt i et sandkassemiljø, og når du planlægger en produktionsimplementering, skal du sørge for at vælge perioder med lav aktivitet.
  • Indsamling af oplysninger af denne type kan hjælpe med at differentiere en DBA fra hinanden. Der er sikkert 3 parts værktøjer, der kan det samme, og endnu mere, men det er ikke alle, der har råd til det. Jeg håber, at dette kan hjælpe alle, der beslutter sig for at bruge det i deres miljø.

  1. Hvordan vælger jeg effektivt den tidligere ikke-nul værdi?

  2. Hvordan taler Access med ODBC-datakilder? Del 1

  3. SQL-forespørgselsoptimering - Sådan bestemmes hvornår og om det er nødvendigt

  4. Med sqlalchemy hvordan man dynamisk binder til databasemotoren på en per-anmodningsbasis