T-SQL query to get the latest available backup chain

This query shall return the latest available database backup chain (Full, Differential and Log) of individual databases along with their backup size and compressed size on the SQL Server instance. Output shall also include the total backup size, total compressed size of all the Full backups, Differential backups, Log backups and Total Full + Differential + Log backups.

This query can be helpful for knowing the latest backup chain and size of the backups for the capacity planning and also to estimate the space requirement for database restore.

Below image shows the T-SQL query output format with sample data.

backup_chain

/*
	Author	:	Brahmanand Shukla
	Date	:	28-Oct-2019
	Purpose	:	T-SQL query to get the latest available backup chain
*/

DECLARE @server_name	VARCHAR(50)	=	'ALL'
	, @database_name	VARCHAR(50)	=	'ALL'

-- Uncomment the below section and supply your  if you want to fetch the report for specific server
--SET @server_name		=	'sql_server_1'

-- Uncomment the below section and supply your  if you want to fetch the report for specific database
--SET @database_name	=	'sql_database_1'

; WITH cte_Backup
AS
(
	SELECT backupset.server_name
		, backupset.database_name
		, backupset.backup_start_date
		, backupset.backup_finish_date
		,
			CASE backupset.type
				WHEN 'D' THEN 'Full'
				WHEN 'I' THEN 'Differential'
				WHEN 'L' THEN 'Log'
			END AS [backup_type]
		, CAST(((backupset.backup_size / 1024) / 1024) AS NUMERIC(18, 2)) AS [Backup_size_MB]
		, CAST(((backupset.compressed_backup_size / 1024) / 1024) AS NUMERIC(18, 2)) AS [Compressed_Backup_size_MB]
		, DATEDIFF(SECOND, backupset.backup_start_date, backupset.backup_finish_date) AS [Duration_Seconds]
		, backupset.database_creation_date
		, backupset.recovery_model
		, backupmediafamily.physical_device_name
		, backupset.[user_name]
		, backupset.[backup_set_uuid]
		, backupset.[database_backup_lsn]
		, backupset.[differential_base_guid]
		, ROW_NUMBER() OVER(PARTITION BY backupset.server_name, backupset.database_name, backupset.type
							ORDER BY backupset.backup_finish_date DESC) AS [RowID]
		,
			CASE backupset.type
				WHEN 'D' THEN 1
				WHEN 'I' THEN 2
				WHEN 'L' THEN 3
			END AS [backup_type_sort_order]
	FROM msdb.dbo.backupmediafamily backupmediafamily
		INNER JOIN msdb.dbo.backupset backupset
			ON backupmediafamily.media_set_id = backupset.media_set_id
	WHERE (@server_name = 'ALL' OR backupset.server_name = @server_name)
		AND (@database_name = 'ALL' OR backupset.database_name = @database_name)
)
, cte_Backup_Full
AS
(
	SELECT *
	FROM cte_Backup
	WHERE [backup_type] = 'Full'
		AND [RowID] = 1
)
, cte_Backup_Differential
AS
(
	SELECT DIF.[server_name]
		, DIF.[database_name]
		, DIF.[backup_start_date]
		, DIF.[backup_finish_date]
		, DIF.[backup_type]
		, DIF.[Backup_size_MB]
		, DIF.[Compressed_Backup_size_MB]
		, DIF.[Duration_Seconds]
		, DIF.[database_creation_date]
		, DIF.[recovery_model]
		, DIF.[physical_device_name]
		, DIF.[user_name]
		, DIF.[backup_type_sort_order]
		, DIF.[database_backup_lsn]
		, ROW_NUMBER() OVER(PARTITION BY DIF.[server_name], DIF.[database_name], DIF.[differential_base_guid]
							ORDER BY DIF.[backup_finish_date] DESC) AS [RowID]
	FROM cte_Backup DIF
		INNER JOIN cte_Backup_Full FUL
			ON FUL.[server_name] = DIF.[server_name]
			AND FUL.[database_name] = DIF.[database_name]
			AND FUL.[backup_set_uuid] = DIF.[differential_base_guid]
	WHERE DIF.[backup_type] = 'Differential'
		AND DIF.[backup_finish_date] > FUL.[backup_finish_date]
)
, cte_Backup_Log
AS
(
	SELECT AL.*
	FROM cte_Backup AL
		INNER JOIN cte_Backup_Differential DIF
			ON DIF.[server_name] = AL.[server_name]
			AND DIF.[database_name] = AL.[database_name]
			AND DIF.[database_backup_lsn] = AL.[database_backup_lsn]
	WHERE DIF.[RowID] = 1
		AND AL.[backup_type] = 'Log'
		AND AL.[backup_finish_date] > DIF.[backup_finish_date]
)
, cte_Backup_Chain
AS
(
	SELECT [server_name]
		, [database_name]
		, [backup_start_date]
		, [backup_finish_date]
		, [backup_type]
		, [Backup_size_MB]
		, [Compressed_Backup_size_MB]
		, [Duration_Seconds]
		, [database_creation_date]
		, [recovery_model]
		, [physical_device_name]
		, [user_name]
		, [backup_type_sort_order]
	FROM cte_Backup_Full
	UNION ALL
	SELECT [server_name]
		, [database_name]
		, [backup_start_date]
		, [backup_finish_date]
		, [backup_type]
		, [Backup_size_MB]
		, [Compressed_Backup_size_MB]
		, [Duration_Seconds]
		, [database_creation_date]
		, [recovery_model]
		, [physical_device_name]
		, [user_name]
		, [backup_type_sort_order]
	FROM cte_Backup_Differential
	WHERE [RowID] = 1
	UNION ALL
	SELECT [server_name]
		, [database_name]
		, [backup_start_date]
		, [backup_finish_date]
		, [backup_type]
		, [Backup_size_MB]
		, [Compressed_Backup_size_MB]
		, [Duration_Seconds]
		, [database_creation_date]
		, [recovery_model]
		, [physical_device_name]
		, [user_name]
		, [backup_type_sort_order]
	FROM cte_Backup_Log
)
, cte_Backup_Full_All_DB
AS
(
	SELECT [server_name]
		, 'ALL_FULL' AS [database_name]
		, NULL AS [backup_start_date]
		, NULL AS [backup_finish_date]
		, NULL AS [backup_type]
		, SUM([Backup_size_MB]) AS [Backup_size_MB]
		, SUM([Compressed_Backup_size_MB]) AS [Compressed_Backup_size_MB]
		, SUM([Duration_Seconds]) AS [Duration_Seconds]
		, NULL AS [database_creation_date]
		, NULL AS [recovery_model]
		, NULL AS [physical_device_name]
		, NULL AS [user_name]
		, 0 AS [backup_type_sort_order]
	FROM cte_Backup_Chain
	WHERE [backup_type] = 'Full'
	GROUP BY [server_name]
)
, cte_Backup_Differential_All_DB
AS
(
	SELECT [server_name]
		, 'ALL_DIFF' AS [database_name]
		, NULL AS [backup_start_date]
		, NULL AS [backup_finish_date]
		, NULL AS [backup_type]
		, SUM([Backup_size_MB]) AS [Backup_size_MB]
		, SUM([Compressed_Backup_size_MB]) AS [Compressed_Backup_size_MB]
		, SUM([Duration_Seconds]) AS [Duration_Seconds]
		, NULL AS [database_creation_date]
		, NULL AS [recovery_model]
		, NULL AS [physical_device_name]
		, NULL AS [user_name]
		, 0 AS [backup_type_sort_order]
	FROM cte_Backup_Chain
	WHERE [backup_type] = 'Differential'
	GROUP BY [server_name]
)
, cte_Backup_Log_All_DB
AS
(
	SELECT [server_name]
		, 'ALL_LOG' AS [database_name]
		, NULL AS [backup_start_date]
		, NULL AS [backup_finish_date]
		, NULL AS [backup_type]
		, SUM([Backup_size_MB]) AS [Backup_size_MB]
		, SUM([Compressed_Backup_size_MB]) AS [Compressed_Backup_size_MB]
		, SUM([Duration_Seconds]) AS [Duration_Seconds]
		, NULL AS [database_creation_date]
		, NULL AS [recovery_model]
		, NULL AS [physical_device_name]
		, NULL AS [user_name]
		, 0 AS [backup_type_sort_order]
	FROM cte_Backup_Chain
	WHERE [backup_type] = 'Log'
	GROUP BY [server_name]
)
, cte_Backup_Full_Differential_Log_All_DB
AS
(
	SELECT [server_name]
		, 'FULL+DIFF+LOG' AS [database_name]
		, NULL AS [backup_start_date]
		, NULL AS [backup_finish_date]
		, NULL AS [backup_type]
		, SUM([Backup_size_MB]) AS [Backup_size_MB]
		, SUM([Compressed_Backup_size_MB]) AS [Compressed_Backup_size_MB]
		, SUM([Duration_Seconds]) AS [Duration_Seconds]
		, NULL AS [database_creation_date]
		, NULL AS [recovery_model]
		, NULL AS [physical_device_name]
		, NULL AS [user_name]
		, 0 AS [backup_type_sort_order]
	FROM cte_Backup_Chain
	GROUP BY [server_name]
)
, cte_Final_Output_Staging
AS
(
	SELECT [server_name]
		, [database_name]
		, [backup_start_date]
		, [backup_finish_date]
		, [backup_type]
		, [Backup_size_MB]
		, [Compressed_Backup_size_MB]
		, [Duration_Seconds]
		, [database_creation_date]
		, [recovery_model]
		, [physical_device_name]
		, [user_name]
		, [backup_type_sort_order]
		, 1 AS [sort_priority]
	FROM cte_Backup_Full_All_DB
	UNION ALL
	SELECT [server_name]
		, [database_name]
		, [backup_start_date]
		, [backup_finish_date]
		, [backup_type]
		, [Backup_size_MB]
		, [Compressed_Backup_size_MB]
		, [Duration_Seconds]
		, [database_creation_date]
		, [recovery_model]
		, [physical_device_name]
		, [user_name]
		, [backup_type_sort_order]
		, 2 AS [sort_priority]
	FROM cte_Backup_Differential_All_DB
	UNION ALL
	SELECT [server_name]
		, [database_name]
		, [backup_start_date]
		, [backup_finish_date]
		, [backup_type]
		, [Backup_size_MB]
		, [Compressed_Backup_size_MB]
		, [Duration_Seconds]
		, [database_creation_date]
		, [recovery_model]
		, [physical_device_name]
		, [user_name]
		, [backup_type_sort_order]
		, 3 AS [sort_priority]
	FROM cte_Backup_Log_All_DB
	UNION ALL
	SELECT [server_name]
		, [database_name]
		, [backup_start_date]
		, [backup_finish_date]
		, [backup_type]
		, [Backup_size_MB]
		, [Compressed_Backup_size_MB]
		, [Duration_Seconds]
		, [database_creation_date]
		, [recovery_model]
		, [physical_device_name]
		, [user_name]
		, [backup_type_sort_order]
		, 4 AS [sort_priority]
	FROM cte_Backup_Full_Differential_Log_All_DB
	UNION ALL
	SELECT [server_name]
		, [database_name]
		, [backup_start_date]
		, [backup_finish_date]
		, [backup_type]
		, [Backup_size_MB]
		, [Compressed_Backup_size_MB]
		, [Duration_Seconds]
		, [database_creation_date]
		, [recovery_model]
		, [physical_device_name]
		, [user_name]
		, [backup_type_sort_order]
		, (4 + [backup_type_sort_order]) AS [sort_priority]
	FROM cte_Backup_Chain
)

SELECT [server_name]
	, [database_name]
	, [backup_start_date]
	, [backup_finish_date]
	, [backup_type]
	, [Backup_size_MB]
	, [Compressed_Backup_size_MB]
	, [Duration_Seconds]
	, [database_creation_date]
	, [recovery_model]
	, [physical_device_name]
	, [user_name]
FROM cte_Final_Output_Staging
ORDER BY ROW_NUMBER() OVER(ORDER BY [sort_priority] ASC, [database_name] ASC, [backup_type_sort_order] ASC, [backup_finish_date] ASC)

 

3 thoughts on “T-SQL query to get the latest available backup chain

Leave a comment