web-dev-qa-db-fra.com

Vérifiez quand la dernière sauvegarde a été prise

Brent Ozar avait une lettre d'information interesive sur 2015-06-22; Comment gérez-vous des sauvegardes à mesurer DBAS , où il pense qu'un bon DBA devrait vérifier quand

  • la dernière sauvegarde a été faite
  • c'était la dernière restaurée
  • le dernier checkdb a été fait

Il s'est avéré être un bon exercice pour moi. Non seulement j'ai trouvé deux bases de données qui n'ont pas été sauvegardées, j'ai également découvert que nous avons des bases de données où nous n'avons jamais vérifié que nous pouvons restaurer la sauvegarde.

Alors; J'ai fourni une solution à la tâche dans la section de réponse. Nous avons la convention que les noms de base de données sont uniques et mon script répertorie le serveur de production et les serveurs de test, afin que je puisse les comparer.
[.____] Avez-vous une solution meilleure?

Meilleures salutations,

Henrik

6
USE mydb

SET NOCOUNT on
BEGIN TRY
    DROP table #S 
END TRY
BEGIN CATCH
END CATCH

BEGIN TRY
    DROP TABLE  #Mytable
END TRY
BEGIN CATCH
END CATCH

BEGIN TRY
    DROP TABLE #DBInfoResults
END TRY
BEGIN CATCH
END CATCH

go

CREATE table #S ( server_name sysname, purpose VARCHAR(255), servertype VARCHAR(30) )
insert into #s
VALUES 
('myserver',  'Test', 'Smallserver'),
('mybigserver\dsa', 'Production', 'Data ware house'),
('myservertoo',  'Test', 'Data ware house')

declare  @Server_name sysname, @Purpose varchar(255)
declare @sql nvarchar(max) = N'',  @s varchar(21)='', @loopCounter int=0, @debug TINYINT=1

DECLARE Server_Cursor CURSOR
FOR
SELECT   s.server_name, s.purpose
FROM    #S s
order by 1

CREATE TABLE #Mytable (server_name sysname, database_name sysname, LastFullBackup DATE, LastIncrementalBackup DATE, comment VARCHAR(255), SizeInGB BIGINT, LastRestoreDate DATE, LastKnownGoodDBCCCheck DATE)
CREATE TABLE #DBInfoResults ([ParentObject] VARCHAR(512),[Object] VARCHAR(512),[Field] VARCHAR(512),[VALUE] VARCHAR(512))

OPEN Server_Cursor
FETCH NEXT FROM Server_Cursor INTO @Server_name, @Purpose
WHILE @@FETCH_STATUS = 0 BEGIN
    set @loopCounter +=1
    RAISERROR ('%i server: "%s"  ', 10 ,1, @Loopcounter, @Server_name) WITH NOWAIT

    select @sql = N'     
insert into #Mytable ( server_name, database_name, LastFullBackup, LastIncrementalBackup)
SELECT server_name, name, LastFullBackup, LastIncrementalBackup 
from OPENROWSET(''SQLNCLI10'', ''Server='+@Server_name+';Trusted_Connection=yes;'',
    ''SELECT server.server_name, d.name, FullBackup.LastFullBackup, IncBackup.LastIncrementalBackup 
    FROM sys.databases d
    OUTER APPLY (SELECT @@SERVERNAME AS server_name) AS server
    OUTER APPLY (
        SELECT TOP 1 B.backup_finish_date AS LastFullBackup
        FROM msdb.dbo.backupset B 
        WHERE TYPE=''''d''''
        AND server.server_name=B.server_name
        AND d.name=b.database_name
        ORDER BY B.backup_finish_date DESC 
    ) AS FullBackup
    OUTER APPLY (
        SELECT TOP 1 B.backup_finish_date AS LastIncrementalBackup
        FROM msdb.dbo.backupset B 
        WHERE TYPE=''''I''''
        AND server.server_name=B.server_name
        AND d.name=b.database_name
        ORDER BY B.backup_finish_date DESC 
    ) AS IncBackup
    WHERE STATE_DESC = ''''ONLINE''''
    AND name <> ''''tempdb'''' /* no backups, checkdbs needed */
    ORDER BY 1,2
    ''
    ) as a
    '
    if @loopCounter <= 1 IF @debug <> 0 select @sql
    begin try
        exec sp_executesql @sql
    end try
    begin CATCH
      print error_number()
      print ERROR_MESSAGE()
    end catch


    /* loop the databases found on this server */
    DECLARE @database_name sysname
    DECLARE db_Cursor CURSOR
    FOR
    SELECT database_name
    FROM  #Mytable H
    WHERE H.server_name=@Server_name
    order by 1

    OPEN db_Cursor
    FETCH NEXT FROM db_Cursor INTO @database_name
    WHILE @@FETCH_STATUS = 0 BEGIN
        select @sql = N'     
        UPDATE #Mytable 
        SET SizeInGB=(
            SELECT SizeInGB
            from OPENROWSET(''SQLNCLI10'', ''Server='+@Server_name+';Trusted_Connection=yes;'',
            ''
            SELECT SUM(CAST(size AS BIGINT))*8/1024/1024 as SizeInGB FROM ' + @database_name + '.sys.database_files DF
            ''
            ) as a
        )
        from #Mytable h
        where h.server_name=''' + @server_name + ''' and h.DataBase_name=''' + @database_name + '''

        '
        RAISERROR ('%i server: "%s" db: %s get Size', 10 ,1, @Loopcounter, @Server_name, @database_name) WITH NOWAIT
        if @loopCounter <= 1 IF @debug <> 0 select @sql
        begin try
            exec sp_executesql @sql
        end try
        begin CATCH

          print error_number()
          print ERROR_MESSAGE()
        end CATCH

        /* last restore date */
        select @sql = N'     
        UPDATE #Mytable 
        SET LastRestoreDate=(
            SELECT restore_date 
            FROM OPENROWSET(''SQLNCLI10'', ''Server='+@Server_name+';Trusted_Connection=yes;'',
            ''
            SELECT max(restore_date) as restore_date FROM msdb.dbo.restorehistory where destination_database_name=''''' + @database_name + '''''
            ''
            ) as a
        )
        from #Mytable h
        where h.server_name=''' + @server_name + ''' and h.DataBase_name=''' + @database_name + '''
        '

        RAISERROR ('%i server: "%s" db: %s get Restore date ', 10 ,1, @Loopcounter, @Server_name, @database_name) WITH NOWAIT
        if @loopCounter <= 1 IF @debug <> 0 select @sql
        begin try
            exec sp_executesql @sql
        end try
        begin CATCH

          print error_number()
          print ERROR_MESSAGE()
        end CATCH


        /* last DBCC */
        TRUNCATE TABLE #DBInfoResults
        select @sql = N'     
        Begin Try
          EXEC sys.sp_dropserver @server = ''myLinkedServer''
        End try
        begin catch
        end catch

        begin try
        EXEC sp_addlinkedserver @server=''myLinkedServer'', @srvproduct='''', @provider=''sqlncli'', @datasrc='''+@Server_name+''',  @location='''', @provstr='''', @catalog=''' + @database_name + '''  
        EXEC sp_addlinkedsrvlogin @rmtsrvname = ''myLinkedServer'', @useself = ''true''
        EXEC sp_serveroption ''myLinkedServer'', ''rpc out'', true;

        INSERT INTO #DBInfoResults
        EXEC (''DBCC DBINFO() WITH TABLERESULTS, NO_INFOMSGS'') at myLinkedServer

        UPDATE #Mytable 
        SET LastKnownGoodDBCCCheck=(SELECT value FROM #DBInfoResults where Field = ''dbi_dbccLastKnownGood'')
        from #Mytable h
        where h.server_name=''' + @server_name + ''' and h.DataBase_name=''' + @database_name + '''
        end try
        begin catch
          print error_number()
          print ERROR_MESSAGE()
        end catch
        EXEC sys.sp_dropserver @server = ''myLinkedServer''
        '

        RAISERROR ('%i server: "%s" dbcc: %s ', 10 ,1, @Loopcounter, @Server_name, @database_name) WITH NOWAIT
        if @loopCounter <= 1 select @sql
        begin try
            exec sp_executesql @sql
        end try
        begin CATCH

          print error_number()
          print ERROR_MESSAGE()
        end CATCH

        SET @loopCounter+=1
        FETCH NEXT FROM db_Cursor INTO @database_name
    END
    CLOSE db_Cursor ;
    DEALLOCATE db_Cursor ;

    FETCH NEXT FROM Server_Cursor INTO @Server_name, @Purpose
END
CLOSE Server_Cursor ;
DEALLOCATE Server_Cursor ;

UPDATE #Mytable SET Comment = 'Problem! '  FROM #Mytable H WHERE DATEDIFF(DAY, CASE WHEN h.LastIncrementalBackup>LastFullBackup THEN h.LastIncrementalBackup ELSE LastFullBackup END, GETDATE()) > 1
UPDATE #Mytable SET Comment = 'No backup required; structure in TFS.' WHERE database_name IN ('vdcasdw', 'mydbTemp', 'VTMChart', 'VTMFileStream', 'VTRArchive')
UPDATE #Mytable SET Comment = 'No backup required;' WHERE database_name LIKE '%ToBeDeleted'
UPDATE #Mytable SET Comment = 'No backup required; data in DWH.' WHERE database_name IN ('OperationalData')
UPDATE #Mytable SET Comment = 'No backup required; test server.'  FROM #Mytable H INNER JOIN #S S ON S.server_name = H.server_name WHERE Purpose IN ('test', 'Development')

/* list all checks */
SELECT top 10000 h.*, s.purpose, s.servertype FROM #Mytable H
INNER JOIN #S S ON S.server_name = H.server_name 
ORDER BY 1 DESC


/* run report on production servers */
SELECT H.server_name, H.database_name, COALESCE(CAST(H.LastFullBackup AS VARCHAR(30)), 'no backup exists!') AS LastFullBackup
, COALESCE(CAST(H.LastIncrementalBackup AS VARCHAR(30)), '') AS LastIncrementalBackup
, COALESCE(CAST(DATEDIFF(DAY, CASE WHEN h.LastIncrementalBackup>LastFullBackup THEN h.LastIncrementalBackup ELSE LastFullBackup END, GETDATE()) AS VARCHAR(30)), '') AS DaysSinceLastBackup
, COALESCE(comment, '') AS Comment 
, COALESCE(CAST(sizeinGB AS VARCHAR(30)), '') AS SizeinGB
, COALESCE(CAST(H2.LastRestoreDate AS VARCHAR(30)), 'Backup never tested!') AS LastRestoreDate
, COALESCE(CAST(DATEDIFF(DAY, h2.LastRestoreDate, GETDATE()) AS VARCHAR(30)), '') AS DaysSinceRestore
, CASE WHEN H2.LastKnownGoodDBCCCheck <> '1900-01-01' THEN (CAST(H2.LastKnownGoodDBCCCheck AS VARCHAR(30))) else 'A Database without DBCC CheckDB' END AS LastKnownGoodDBCCCheck
, CASE WHEN H2.LastKnownGoodDBCCCheck <> '1900-01-01' THEN (CAST(DATEDIFF(DAY, h2.LastKnownGoodDBCCCheck, GETDATE()) AS VARCHAR(30))) else '' END AS DaysSinceLastKnownGoodDBCCCheck
, h2.Purpose AS SystemThatExists
FROM #Mytable H
INNER JOIN #S S ON S.server_name = H.server_name
OUTER APPLY (
    SELECT MAX(LastKnownGoodDBCCCheck) AS LastKnownGoodDBCCCheck, MAX(LastRestoreDate ) AS LastRestoreDate,  utl.CommaListConcatenate(s3.Purpose) AS Purpose FROM #Mytable H3 
    INNER JOIN #S S3 ON S3.server_name = H3.server_name
    WHERE h.database_name=h3.database_name AND s3.servertype=s.servertype 
) AS h2
WHERE s.purpose='production'
ORDER BY 1,2
2

J'utiliserais réellement dbatools ( https://dbatools.io ) ces jours-ci

Get-DbalastBackup -SqlinStance Instance

Qui ressemble à ceci

enter image description here Ancienne réponse ci-dessous

Je suis un mec PowerShell (sonne comme si j'étant assis à une réunion anonyme de PowerShell !!)

c'est donc le script que j'utiliserais bien que j'utilise les objets retournés pour écrire dans une base de données pour un rapport ou écrire à Excel ou créer un email HTML pour mon manager ou tout ce qui était nécessaire. N'oubliez pas que les serveurs $ doivent contenir ServerName\InstanceName, PortNumber si l'un de ceux-ci n'est pas standard

# Load SMO extension
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null;
$Servers = 
## A list 'Servername1','Servername2' a text file Get-Content 'PATHTOSERVERFILE' or query a database Invoke-SQLCmd -Server SERVERNAME -Database ALLMyInstances -Query "Select Name FROM Instances"
foreach($Server in $Servers)    
{
$srv = New-Object ('Microsoft.SqlServer.Management.Smo.Server') $Server
$lastDBCC_CHECKDB = @{Name="Last DBCC Check";Expression={$_.ExecuteWithResults("DBCC DBINFO () WITH TABLERESULTS").Tables[0] | where {$_.Field.ToString() -eq "dbi_dbccLastKnownGood"} | Select Value -ExpandProperty Value}}
foreach($db in $srv.databases)
{
$db|Select Parent,Name,LastBackupDate,LastDifferentialBackupDate,LastLogBackupDate,$lastDBCC_CHECKDB
 }
}
4
SQLDBAWithABeard

peut-être que je suis un peu trop "vieille école", mais j'ai utilisé une requête et une script pendant environ 10 ans maintenant depuis le serveur SQL 2005 jours:

SELECT LEFT(d.name,20) AS database_name,
    CONVERT(VARCHAR(16), MAX(CASE b.[type] WHEN 'D' THEN b.backup_finish_date END), 120) AS LastFullBackup,
    CONVERT(VARCHAR(16), MAX(CASE b.[type] WHEN 'I' THEN b.backup_finish_date END), 120) AS LastDiffBackup,
    CONVERT(VARCHAR(16), MAX(CASE b.[type] WHEN 'L' THEN b.backup_finish_date END), 120) AS LastLogBackup,
    CONVERT(VARCHAR(16), MAX(CASE WHEN b.[type] NOT IN ('D','I','L') THEN b.backup_finish_date END), 120) AS LastOtherBackup
  FROM sys.databases d
    LEFT OUTER JOIN msdb.dbo.backupset b ON d.name = b.database_name
  WHERE d.name <> 'tempdb'
  GROUP BY d.database_id, d.name
  ORDER BY CASE WHEN d.database_id <= 4 THEN 0 ELSE 1 END, d.name

Ceci est appelé par un script CMD i Calendrier qui appelle SQLCMD pour exécuter cette fois la fois par instance et conduite la sortie à un fichier texte que je électronique à la fin.

2
Chris Harshman

Voici comment je l'ai fait. Je ne vérifie pas tous les serveurs, mais cela pourrait être adapté pour le faire sans beaucoup de tracas, je suppose.

-- I used the same method as written by Brent Ozar Unlimited in sp_Blitz to get the DBCC date.
-- All credit for that goes to them fof that.
-- I wrote the rest of it, so similarities to code, living or dead, is unintentional.

declare @databasesize table (dbname nvarchar(128), dbsize decimal(20, 6))
create table #dbcc (ParentObject varchar(255), [Object] varchar(255), Field varchar(255), Value varchar(255), DbName nvarchar(128) NULL)

insert into @databasesize exec sp_MSforeachdb '
    select
        ''?''
        ,((sum(size) * 1.0) / 128) as DatabaseSize
    from
        ?.sys.database_files df'

exec sp_MSforeachdb 'use [?]; insert into #dbcc (ParentObject,
            Object,
            Field,
            Value)
        EXEC (''DBCC DBInfo() With TableResults, NO_INFOMSGS'');
        UPDATE #dbcc SET DbName = N''?'' WHERE DbName IS NULL;'

select
    @@SERVERNAME as ServerName
    ,sd.[name] as DatabaseName
    ,ds.dbsize
    ,max(bsd.backup_finish_date) as LastFullBackupDate
    ,max(bsl.backup_finish_date) as LastLogBackupDate
    ,rh.restore_date as BackupFileRestoreDate
    ,nullif(dbc.Value, '1900-01-01 00:00:00.000') as LastDBCCDate
from
    sys.databases sd
    inner join @databasesize ds on sd.[name] = ds.dbname
    left outer join msdb..backupset bsd on sd.[name] = bsd.database_name
    and bsd.[type] = 'D'
    left outer join msdb..backupset bsl on sd.[name] = bsl.database_name
    and bsl.[type] = 'L'
    left outer join #dbcc dbc on sd.[name] = dbc.DbName
    and dbc.Field = 'dbi_dbccLastKnownGood'
    left outer join msdb..restorehistory rh on bsd.backup_set_id = rh.backup_set_id
group by
    sd.[name]
    ,ds.dbsize
    ,dbc.Value
    ,rh.restore_date

drop table #dbcc
1
user2235