Home
%3CLINGO-SUB%20id%3D%22lingo-sub-370331%22%20slang%3D%22en-US%22%3ELast%20Known%20Good%20Backup%2FDBCC%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-370331%22%20slang%3D%22en-US%22%3E%3CP%3E%3CSTRONG%3E%20First%20published%20on%20MSDN%20on%20Feb%2009%2C%202010%20%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CP%3EBased%20on%20Paul%20Randal%E2%80%99s%20%3CA%20href%3D%22http%3A%2F%2Fwww.sqlskills.com%2FBLOGS%2FPAUL%2Fpost%2FCHECKDB-From-Every-Angle-When-did-DBCC-CHECKDB-last-run-successfully.aspx%22%20target%3D%22_blank%22%20rel%3D%22noopener%20nofollow%20noopener%20noreferrer%20noopener%20noreferrer%22%3E%20post%20%3C%2FA%3E%20telling%20how%20to%20get%20the%20last%20known%20good%20DBCC%20ran%20against%20a%20database%2C%20based%20on%20my%20job%20requirements%2C%20I%20decided%20to%20write%20a%20T-SQL%20script%20which%20has%20nothing%20new%20but%20returns%20last%20known%20good%20DBCC%20execution%20time%20and%20last%20backup%20time%20for%20all%20databases%20of%20a%20SQL%20Server%20instance.%20The%20point%20is%20that%20this%20runs%20on%20all%20three%20supported%20versions%20of%20SQL%20Server%3A%202000%2C%202005%20and%202008%20but%20returns%20the%20last%20successful%20DBCC%20execution%20date%20for%202005%20and%202008%20only.%20By%20the%20way%2C%20for%20supported%20versions%20have%20a%20look%20at%20this%20%3CA%20href%3D%22http%3A%2F%2Fblogs.msdn.com%2Fsqlreleaseservices%2Farchive%2F2009%2F10%2F08%2Fend-of-service-pack-support-for-sql-server-2005-sp2-and-sql-server-2008-rtm.aspx%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%3E%20blog%20entry%20%3C%2FA%3E%20.%3C%2FP%3E%0A%3CP%3EIn%20the%20case%20you%20have%20a%202008%20Management%20Studio%20installed%20somewhere%20on%20your%20network%2C%20preferably%20on%20your%20monitoring%20laptop%2C%20PC%20or%20server%2C%20you%20can%20register%20all%20your%20SQL%20Server%20instances%20and%20run%20this%20script%20in%20a%20multi-server%20query.%3C%2FP%3E%0A%3CP%3EI%20have%20seen%20a%20similar%20%3CA%20href%3D%22http%3A%2F%2Fsqlserverpedia.com%2Fblog%2Fsql-server-bloggers%2Felegant-solution-finding-last-clean-dbcc-checkdb-ran-date-quickly-for-all-databases%2F%22%20target%3D%22_blank%22%20rel%3D%22noopener%20nofollow%20noopener%20noreferrer%20noopener%20noreferrer%22%3E%20batch%20%3C%2FA%3E%20more%20compact%20and%20elegant%20using%20CTE%20which%20is%20just%20great%20but%20cannot%20work%20on%20SQL%20Server%202000%20unfortunately.%3C%2FP%3E%0A%3CP%3EThis%20script%20takes%20care%20of%20SQL%20Server%202008%20doubling%20the%20dbi_dbccLastKnownGood%20field.%3C%2FP%3E%0A%3CP%3EHere%20it%20is%3A%3C%2FP%3E%0A%3CPRE%3E%0ASET%20NOCOUNT%20ON%0AGO%0A%0AUSE%20master%0AGO%0A%0A--%20Trace%20flag%20to%20make%20DBCC%20Page%20command%20results%20available%20in%20the%20current%20connection%0ADBCC%20TRACEON%20(3604)%0AGO%0A%0ACREATE%20TABLE%20%23DBCC_table%20(%0A%20ParentObject%20NVARCHAR(4000)%20NULL%0A%20%2COBJECT%20NVARCHAR(4000)%20NULL%0A%20%2CField%20NVARCHAR(4000)%20NULL%0A%20%2CVALUE%20NVARCHAR(4000)%20NULL%0A%20)%0A%0ACREATE%20TABLE%20%23LastDBCC_table%20(%0A%20%5BDatabase%20Name%5D%20NVARCHAR(4000)%20NULL%0A%20%2C%5BLast%20Known%20Good%20DBCC%5D%20NVARCHAR(4000)%20NULL%0A%20)%0A%0ADECLARE%20%40cmd%20VARCHAR(4000)%0ADECLARE%20%40DB_NAME%20NVARCHAR(500)%0ADECLARE%20%40DB_ID%20INT%0A%0ADECLARE%20LastDBCC_cursor%20CURSOR%0AFOR%0ASELECT%20name%0A%20%2C%5Bdbid%5D%0AFROM%20sysdatabases%0AORDER%20BY%20dbid%0A%0AOPEN%20LastDBCC_cursor%0A%0A--%20Perform%20the%20first%20fetch.%0AFETCH%20NEXT%0AFROM%20LastDBCC_cursor%0AINTO%20%40DB_NAME%0A%20%2C%40DB_ID%0A%0A--%20Check%20%40%40FETCH_STATUS%20to%20see%20if%20there%20are%20any%20more%20rows%20to%20fetch.%0AWHILE%20%40%40FETCH_STATUS%20%3D%200%0ABEGIN%0A%20--%20This%20is%20executed%20as%20long%20as%20the%20previous%20fetch%20succeeds.%0A%20SET%20%40cmd%20%3D%20'dbcc%20page('%20%2B%20convert(VARCHAR%2C%20%40DB_ID)%20%2B%20'%2C1%2C9%2C3)%20with%20tableresults'%0A%0A%20INSERT%20INTO%20%23DBCC_table%0A%20EXECUTE%20(%40cmd)%0A%0A%20INSERT%20INTO%20%23LastDBCC_table%0A%20SELECT%20DISTINCT%20%40DB_NAME%0A%20%20%2CVALUE%0A%20FROM%20%23DBCC_table%0A%20WHERE%20Field%20%3D%20'dbi_dbccLastKnownGood'%0A%0A%20IF%20%40%40ROWCOUNT%20%3D%200%0A%20%20INSERT%20INTO%20%5B%23LastDBCC_table%5D%0A%20%20SELECT%20%40DB_NAME%0A%20%20%20%2C'Not%20implemented'%0A%0A%20FETCH%20NEXT%0A%20FROM%20LastDBCC_cursor%0A%20INTO%20%40DB_NAME%0A%20%20%2C%40DB_ID%0A%0A%20DELETE%20%23DBCC_table%0AEND%0A%0ACLOSE%20LastDBCC_cursor%0A%0ADEALLOCATE%20LastDBCC_cursor%0A%0ASELECT%20T1.%5BDatabase%20Name%5D%0A%20%2CCASE%20%0A%20%20WHEN%20(max(T1.%5BLast%20Known%20Good%20DBCC%5D)%20%3D%20'1900-01-01%2000%3A00%3A00.000')%0A%20%20%20THEN%20'Not%20Yet%20Ran'%0A%20%20ELSE%20max(T1.%5BLast%20Known%20Good%20DBCC%5D)%0A%20%20END%20AS%20%5BLast%20Known%20Good%20DBCC%5D%0A%20%2C%0A%20--max(T1.%5BLast%20Known%20Good%20DBCC%5D)%20as%20%5BLast%20Known%20Good%20DBCC%5D%2C%0A%20COALESCE(convert(VARCHAR(50)%2C%20MAX(T2.backup_finish_date)%2C%2021)%2C%20'Not%20Yet%20Taken')%20AS%20%5BLast%20BackUp%20Taken%5D%0AFROM%20%23LastDBCC_table%20T1%0ALEFT%20OUTER%20JOIN%20msdb.dbo.backupset%20T2%20ON%20T2.database_name%20%3D%20T1.%5BDatabase%20Name%5D%0AGROUP%20BY%20T1.%5BDatabase%20Name%5D%0AORDER%20BY%20T1.%5BDatabase%20Name%5D%0A%0ADROP%20TABLE%20%23LastDBCC_table%0A%0ADROP%20TABLE%20%23DBCC_table%0A%0ADBCC%20TRACEOFF%20(3604)%0AGO%0A%3C%2FPRE%3E%0A%3CP%3E%3CSTRONG%3E%20Lionel%20P%C3%A9nuchot%20%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CP%3ESenior%20Premier%20Field%20Engineer%2C%20Microsoft%20France%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-TEASER%20id%3D%22lingo-teaser-370331%22%20slang%3D%22en-US%22%3E%3CP%3EFirst%20published%20on%20MSDN%20on%20Feb%2009%2C%202010%20%26nbsp%3B%20Based%20on%20Paul%20Randal%E2%80%99s%20post%20telling%20how%20to%20get%20the%20last%20known%20good%20DBCC%20ran%20against%20a%20database%2C%20based%20on%20my%20job%20requirements%2C%20I%20decided%20to%20write%20a%20T-SQL%20script%20which%20has%20nothing%20new%20but%20returns%20last%20known%20good%20DBCC%20execution%20time%20and%20last%20backup%20time%20for%20all%20databases%20of%20a%20SQL%20Server%20instance.%3C%2FP%3E%3C%2FLINGO-TEASER%3E
Occasional Visitor

First published on MSDN on Feb 09, 2010

Based on Paul Randal’s post telling how to get the last known good DBCC ran against a database, based on my job requirements, I decided to write a T-SQL script which has nothing new but returns last known good DBCC execution time and last backup time for all databases of a SQL Server instance. The point is that this runs on all three supported versions of SQL Server: 2000, 2005 and 2008 but returns the last successful DBCC execution date for 2005 and 2008 only. By the way, for supported versions have a look at this blog entry .

In the case you have a 2008 Management Studio installed somewhere on your network, preferably on your monitoring laptop, PC or server, you can register all your SQL Server instances and run this script in a multi-server query.

I have seen a similar batch more compact and elegant using CTE which is just great but cannot work on SQL Server 2000 unfortunately.

This script takes care of SQL Server 2008 doubling the dbi_dbccLastKnownGood field.

Here it is:

SET NOCOUNT ON
GO

USE master
GO

-- Trace flag to make DBCC Page command results available in the current connection
DBCC TRACEON (3604)
GO

CREATE TABLE #DBCC_table (
	ParentObject NVARCHAR(4000) NULL
	,OBJECT NVARCHAR(4000) NULL
	,Field NVARCHAR(4000) NULL
	,VALUE NVARCHAR(4000) NULL
	)

CREATE TABLE #LastDBCC_table (
	[Database Name] NVARCHAR(4000) NULL
	,[Last Known Good DBCC] NVARCHAR(4000) NULL
	)

DECLARE @cmd VARCHAR(4000)
DECLARE @DB_NAME NVARCHAR(500)
DECLARE @DB_ID INT

DECLARE LastDBCC_cursor CURSOR
FOR
SELECT name
	,[dbid]
FROM sysdatabases
ORDER BY dbid

OPEN LastDBCC_cursor

-- Perform the first fetch.
FETCH NEXT
FROM LastDBCC_cursor
INTO @DB_NAME
	,@DB_ID

-- Check @@FETCH_STATUS to see if there are any more rows to fetch.
WHILE @@FETCH_STATUS = 0
BEGIN
	-- This is executed as long as the previous fetch succeeds.
	SET @cmd = 'dbcc page(' + convert(VARCHAR, @DB_ID) + ',1,9,3) with tableresults'

	INSERT INTO #DBCC_table
	EXECUTE (@cmd)

	INSERT INTO #LastDBCC_table
	SELECT DISTINCT @DB_NAME
		,VALUE
	FROM #DBCC_table
	WHERE Field = 'dbi_dbccLastKnownGood'

	IF @@ROWCOUNT = 0
		INSERT INTO [#LastDBCC_table]
		SELECT @DB_NAME
			,'Not implemented'

	FETCH NEXT
	FROM LastDBCC_cursor
	INTO @DB_NAME
		,@DB_ID

	DELETE #DBCC_table
END

CLOSE LastDBCC_cursor

DEALLOCATE LastDBCC_cursor

SELECT T1.[Database Name]
	,CASE 
		WHEN (max(T1.[Last Known Good DBCC]) = '1900-01-01 00:00:00.000')
			THEN 'Not Yet Ran'
		ELSE max(T1.[Last Known Good DBCC])
		END AS [Last Known Good DBCC]
	,
	--max(T1.[Last Known Good DBCC]) as [Last Known Good DBCC],
	COALESCE(convert(VARCHAR(50), MAX(T2.backup_finish_date), 21), 'Not Yet Taken') AS [Last BackUp Taken]
FROM #LastDBCC_table T1
LEFT OUTER JOIN msdb.dbo.backupset T2 ON T2.database_name = T1.[Database Name]
GROUP BY T1.[Database Name]
ORDER BY T1.[Database Name]

DROP TABLE #LastDBCC_table

DROP TABLE #DBCC_table

DBCC TRACEOFF (3604)
GO

Lionel Pénuchot

Senior Premier Field Engineer, Microsoft France