sql >> Base de Datos >  >> RDS >> Database

Procedimiento almacenado para obtener información de las tablas de la base de datos

Como DBA de SQL Server, siempre nos ocupamos de una de las cosas más importantes para el negocio, los datos. En algunos casos, las aplicaciones pueden volverse bastante complejas y termina con un montón de tablas de base de datos dispersas alrededor de su(s) instancia(s) de SQL Server. Esto podría ocasionar algunos inconvenientes, como:

  • Saber cómo se comportan sus datos todos los días, en términos de tendencias de crecimiento (espacio y/o cantidad de filas).
  • Saber qué tablas de base de datos requieren (o requerirán) una estrategia particular/diferente para almacenar los datos porque está creciendo demasiado rápido.
  • Saber cuáles de las tablas de su base de datos ocupan demasiado espacio, lo que posiblemente genere restricciones de almacenamiento.

Debido a la importancia de estos detalles, he creado un par de procedimientos almacenados que pueden ser de gran ayuda para cualquier DBA de SQL Server que desee realizar un seguimiento de la información sobre las tablas de la base de datos en su entorno. Confía en mí, uno de ellos es genial.

Consideraciones iniciales

  • Asegúrese de que la cuenta que ejecuta este procedimiento almacenado tenga suficientes privilegios. Probablemente podría comenzar con sysadmin y luego ir lo más granular posible para asegurarse de que el usuario tenga el mínimo de privilegios necesarios para que el SP funcione correctamente.
  • Los objetos de la base de datos (tabla de la base de datos y procedimiento almacenado) se crearán dentro de la base de datos seleccionada en el momento en que se ejecute el script, así que elija con cuidado.
  • La secuencia de comandos está diseñada de manera que se puede ejecutar varias veces sin que se produzca un error. Para el procedimiento almacenado, utilicé la declaración "CREATE OR ALTER PROCEDURE", disponible desde SQL Server 2016 SP1. Por eso, no se sorprenda si no funciona sin problemas en una versión anterior.
  • Siéntase libre de cambiar los nombres de los objetos de base de datos creados.
  • Preste atención a los parámetros del procedimiento almacenado que recopila los datos sin procesar. Pueden ser cruciales en una poderosa estrategia de recopilación de datos para visualizar tendencias.

¿Cómo utilizar los procedimientos almacenados?

  1. Copie y pegue el código T-SQL (disponible en este artículo).
  2. El primer SP espera 2 parámetros:
    1. @persistData:'Y' si un DBA quiere guardar el resultado en una tabla de destino y 'N' si el DBA quiere ver el resultado directamente.
    2. @truncateTable:'Y' para truncar la tabla primero antes de almacenar los datos capturados y 'N' si los datos actuales se mantienen en la tabla. Tenga en cuenta que el valor de este parámetro es irrelevante si el valor del parámetro @persistData es 'N'.
  3. El segundo SP espera 1 parámetro:
    1. @targetParameter:el nombre de la columna que se utilizará para transponer la información recopilada.

Campos presentados y su significado

  • nombre_de_la_base_de_datos: el nombre de la base de datos donde reside la tabla.
  • esquema: el nombre del esquema donde reside la tabla.
  • nombre_tabla: el marcador de posición para el nombre de la tabla.
  • recuento_de_filas: el número de filas que tiene actualmente la tabla.
  • espacio_total_mb: el número de MegaBytes asignados para la tabla.
  • used_space_mb: la cantidad de megabytes que la tabla usa actualmente.
  • unused_space_mb: la cantidad de MegaBytes que la tabla no está usando.
  • fecha_de_creación: la fecha/hora en que se creó la tabla.
  • datos_coleccion_marca de tiempo: visible solo si se pasa 'Y' al parámetro @persistData. Se utiliza para saber cuándo se ejecutó el SP y se guardó correctamente la información en la tabla DBA_Tables.

Pruebas de ejecución

Demostraré algunas ejecuciones de los procedimientos almacenados:

/* Mostrar la información de las tablas para todas las bases de datos de usuarios */

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

/* Conservar la información de las tablas de la base de datos y consultar la tabla de destino, truncando primero la tabla de destino */

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

Consultas secundarias

*Consulta para ver las tablas de la base de datos ordenadas desde la mayor cantidad de filas hasta la menor.

SELECT * FROM DBA_Tables ORDER BY row_count DESC;

*Consulta para ver las tablas de la base de datos ordenadas desde el espacio total más grande hasta el más bajo.

SELECT * FROM DBA_Tables ORDER BY total_space_mb DESC;

* Consulta para ver las tablas de la base de datos ordenadas desde el espacio utilizado más grande hasta el más bajo.

SELECT * FROM DBA_Tables ORDER BY used_space_mb DESC;

* Consulta para ver las tablas de la base de datos ordenadas desde el espacio sin usar más grande hasta el más bajo.

SELECT * FROM DBA_Tables ORDER BY unused_space_mb DESC;

*Consulta para ver las tablas de la base de datos ordenadas por fecha de creación, de la más nueva a la más antigua.

SELECT * FROM DBA_Tables ORDER BY created_date DESC;

Aquí hay un código completo del Procedimiento almacenado que captura la información de las tablas de la base de datos:

*Al principio de la secuencia de comandos, verá el valor predeterminado que asume el procedimiento almacenado si no se pasa ningún valor para cada parámetro.

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

Hasta este punto, la información parece un poco seca, pero permítanme cambiar esa percepción con la presentación de un procedimiento almacenado complementario. Su objetivo principal es transponer la información recopilada en la tabla de destino que sirve como fuente para los informes de tendencias.

Así es como puede ejecutar el procedimiento almacenado:

*Para fines de demostración, inserté registros manuales en la tabla de destino denominada t1 para simular mi ejecución habitual de procedimiento almacenado.

*El conjunto de resultados es un poco amplio, así que tomaré un par de capturas de pantalla para mostrar el resultado completo.

EXEC TransposeTablesInformation @targetParmeter = 'row_count' 

Conclusiones clave

  • Si automatiza la ejecución de la secuencia de comandos que completa la tabla de destino, puede notar inmediatamente si algo salió mal con ella o con sus datos. Eche un vistazo a los datos de la tabla 't1' y la columna '15'. Puede ver NULL allí, que se hizo a propósito para mostrarle algo que podría suceder.
  • Con este tipo de vista, puede ver un comportamiento peculiar para las tablas de base de datos más importantes/críticas.
  • En el ejemplo dado, elegí el campo 'row_count' de la tabla de destino, pero puede elegir cualquier otro campo numérico como parámetro y obtener el mismo formato de tabla, pero con datos diferentes.
  • No se preocupe, si especifica un parámetro no válido, el procedimiento almacenado le avisará y detendrá su ejecución.

Aquí hay un código completo del procedimiento almacenado que transpone la información de la tabla de destino:

*Al principio de la secuencia de comandos, verá el valor predeterminado que asume el procedimiento almacenado si no se pasa ningún valor para cada parámetro.

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

Conclusión

  • Puede implementar el SP de recopilación de datos en cada instancia de SQL Server bajo su soporte e implementar un mecanismo de alerta en toda su pila de instancias compatibles.
  • Si implementa un trabajo de agente que consulta esta información con relativa frecuencia, puede estar al tanto del juego en términos de saber cómo se comportan sus datos durante el mes. Por supuesto, puede ir aún más lejos y almacenar los datos recopilados mensualmente para tener una imagen aún más amplia; tendrías que hacer algunos ajustes al código, pero valdría la pena.
  • Asegúrese de probar este mecanismo correctamente en un entorno de espacio aislado y, cuando planee una implementación de producción, asegúrese de elegir períodos de baja actividad.
  • La recopilación de información de este tipo puede ayudar a diferenciar un DBA de otro. Probablemente existen herramientas de terceros que pueden hacer lo mismo, e incluso más, pero no todos tienen el presupuesto para permitírselo. Espero que esto pueda ayudar a cualquiera que decida usarlo en su entorno.