AutoRestoreBackups-Prozedur 

Die Prozedur liest alle Datenbank Backups eines Verzeichnisses und liest dies dann in den SQL-Server ein:

Parameter

 
 
Parameter Typ Default Beschreibung
@SourceDirBackupFiles NVARCHAR(255) F:\Daten\... Pfad zu den .bak/.sav Dateien
@Whatif BIT 0 1 = Befehle nur anzeigen, 0 = ausführen
@Single BIT 0 1 = Datenbank vor Restore in SINGLE_USER setzen und danach MULTI_USER
@DestDirDbFiles NVARCHAR(255) NULL Zielverzeichnis für Data-Dateien (.mdf.ndf). Bei NULL wird der Standarddatenpfad der Instanz verwendet.
@DestDirLogFiles NVARCHAR(255) NULL Zielverzeichnis für Log-Dateien (.ldf). Bei NULL wird der Standardlogpfad der Instanz verwendet.
@VerifyOnly BIT 0 1 = Vor dem Restore wird RESTORE VERIFYONLY ausgeführt.
@ReplaceExisting BIT 1 1 = WITH REPLACE (überschreibt existierende DB), 0 = Fehler, falls DB bereits existiert.
@Stats INT 10 Gibt STATS = n an (Fortschrittsmeldungen alle n Prozent).
@RecoveryState NVARCHAR(10) 'RECOVERY' 'RECOVERY' (DB online) oder 'NORECOVERY' (für weitere Backups).
       

 

Ausgabe

  • Im @Whatif = 1-Modus werden die generierten RESTORE-Befehle auf der Konsole ausgegeben.

  • Im Ausführungsmodus werden Erfolgs‑/Fehlermeldungen pro Datenbank ausgegeben.

/*
=====================================================================
Autor:       Janke
Datum:     2025-11  
Beschreibung: 
    Stellt alle Datenbank-Backups (.bak, .sav) aus einem Quellverzeichnis
    automatisch wieder her. Für jede Backup-Datei wird die enthaltene
    Datenbank ermittelt und ein RESTORE Befehl mit MOVE-Klauseln generiert.
    Die Zielverzeichnisse für Daten- und Log-Dateien können einzeln
    angegeben werden. Optional kann nur ein Whatif-Modus durchgeführt,
    eine Datenbank vorher in den SINGLE_USER-Modus versetzt und nachher
    zurückgesetzt werden, sowie eine Verifikation des Backups durchgeführt
    werden.

Parameter:
    @SourceDirBackupFiles  - Pfad, in dem die .bak/.sav Dateien liegen
    @Whatif                - 1 = Nur Befehle anzeigen, 0 = Ausführen
    @Single                - 1 = Vor Restore SINGLE_USER setzen (und zurück)
    @DestDirDbFiles        - Zielpfad für Data-Dateien (NULL = Standarddatenpfad)
    @DestDirLogFiles       - Zielpfad für Log-Dateien  (NULL = Standardlogpfad)
    @VerifyOnly            - 1 = Vor Restore VERIFYONLY ausführen
    @ReplaceExisting       - 1 = WITH REPLACE verwenden, 0 = Fehler bei existierender DB
    @Stats                 - Statistikanzeige (z.B. 10 = STATS = 10)
    @RecoveryState         - 'RECOVERY' oder 'NORECOVERY' (Standard = RECOVERY)

Abhängigkeiten:
    - SQL Server ab 2012 (SERVERPROPERTY mit 'InstanceDefaultDataPath' ab 2012? Tatsächlich ab 2012 verfügbar)
    - xp_dirtree (erfordert sysadmin, alternativ könnte man ein Directory-Listing per xp_cmdshell oder CLR verwenden)
=====================================================================
*/

CREATE OR ALTER PROCEDURE dbo.AutoRestoreBackups
    @SourceDirBackupFiles NVARCHAR(255) = 'F:\Daten\SQL\Backup\Usr-DB\',
    @Whatif BIT = 0,                         -- 0 = ausführen, 1 = nur anzeigen
    @Single BIT = 0,                         -- 1 = SINGLE_USER vorher/nachher
    @DestDirDbFiles NVARCHAR(255) = NULL,    -- NULL = Standarddatenpfad
    @DestDirLogFiles NVARCHAR(255) = NULL,   -- NULL = Standardlogpfad
    @VerifyOnly BIT = 0,                     -- 1 = RESTORE VERIFYONLY vorher
    @ReplaceExisting BIT = 1,                -- 1 = WITH REPLACE
    @Stats INT = 10,                         -- STATS = n, 0 = keine Ausgabe
    @RecoveryState NVARCHAR(10) = 'RECOVERY'  -- 'RECOVERY' oder 'NORECOVERY'
AS
BEGIN
    SET NOCOUNT ON;

    -- Standardpfade ermitteln (falls nicht angegeben)
    IF @DestDirDbFiles IS NULL
        SET @DestDirDbFiles = CAST(SERVERPROPERTY('InstanceDefaultDataPath') AS NVARCHAR(255));
    IF @DestDirLogFiles IS NULL
        SET @DestDirLogFiles = CAST(SERVERPROPERTY('InstanceDefaultLogPath') AS NVARCHAR(255));

    -- Pfade mit abschließendem Backslash versehen
    IF RIGHT(@DestDirDbFiles, 1) <> '\' SET @DestDirDbFiles = @DestDirDbFiles + '\';
    IF RIGHT(@DestDirLogFiles, 1) <> '\' SET @DestDirLogFiles = @DestDirLogFiles + '\';
    IF RIGHT(@SourceDirBackupFiles, 1) <> '\' SET @SourceDirBackupFiles = @SourceDirBackupFiles + '\';

    -- Temporäre Tabellen für Dateilisten und Backup-Metadaten
    CREATE TABLE #BackupFiles (FileName NVARCHAR(255), Depth INT, IsFile INT);
    CREATE TABLE #HeaderResult (BackupName NVARCHAR(128), BackupDescription NVARCHAR(255), BackupType SMALLINT, ...); -- alle Spalten
    CREATE TABLE #FileList (LogicalName NVARCHAR(128), PhysicalName NVARCHAR(260), Type CHAR(1), ...); -- alle Spalten

    -- 1. Dateiliste aus Verzeichnis holen
    INSERT INTO #BackupFiles
    EXEC master.dbo.xp_dirtree @SourceDirBackupFiles, 1, 1;

    -- 2. Alle .bak und .sav Dateien ermitteln (ohne Unterordner)
    DECLARE @BackupFiles TABLE (FileName NVARCHAR(255), FullPath NVARCHAR(500));
    INSERT INTO @BackupFiles (FileName, FullPath)
    SELECT bf.FileName, @SourceDirBackupFiles + bf.FileName
    FROM #BackupFiles bf
    WHERE bf.IsFile = 1
      AND (bf.FileName LIKE '%.bak' OR bf.FileName LIKE '%.sav');

    IF NOT EXISTS (SELECT 1 FROM @BackupFiles)
    BEGIN
        PRINT 'Keine .bak- oder .sav-Dateien im Verzeichnis gefunden.';
        RETURN;
    END;

    -- 3. Für jede Datei: Datenbanknamen, Backup-Typ, Dateiliste ermitteln
    DECLARE @FullPath NVARCHAR(500), @DbName SYSNAME, @BackupType SMALLINT;
    DECLARE @RestoreCmd NVARCHAR(MAX), @MoveCmd NVARCHAR(MAX);
    DECLARE @Index INT = 1, @MaxIndex INT;
    DECLARE @crlf CHAR(2) = CHAR(13) + CHAR(10);

    SELECT @MaxIndex = COUNT(*) FROM @BackupFiles;
    
    -- Temporäre Tabelle für Restore-Befehle (optional zum Protokollieren)
    CREATE TABLE #RestoreCommands (Seq INT, Cmd NVARCHAR(MAX));

    WHILE @Index <= @MaxIndex
    BEGIN
        SELECT @FullPath = FullPath, @FileName = FileName
        FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY FileName) AS rn FROM @BackupFiles) t
        WHERE rn = @Index;

        -- Header auslesen
        TRUNCATE TABLE #HeaderResult;
        BEGIN TRY
            INSERT INTO #HeaderResult
            EXEC ('RESTORE HEADERONLY FROM DISK = ''' + REPLACE(@FullPath, '''', '''''') + '''');
        END TRY
        BEGIN CATCH
            PRINT 'Fehler beim Lesen der Header von ' + @FullPath + ': ' + ERROR_MESSAGE();
            SET @Index = @Index + 1;
            CONTINUE;
        END CATCH

        -- Nur die erste Zeile verwenden (für einfache Backup-Dateien mit einem Set)
        SELECT TOP 1 @DbName = DatabaseName, @BackupType = BackupType
        FROM #HeaderResult;

        IF @DbName IS NULL
        BEGIN
            PRINT 'Keine gültige Datenbank in ' + @FullPath + ' gefunden.';
            SET @Index = @Index + 1;
            CONTINUE;
        END

        -- Prüfen, ob es sich um ein vollständiges Datenbank-Backup handelt (BackupType = 1)
        IF @BackupType <> 1
        BEGIN
            PRINT 'Überspringe ' + @FullPath + ' (BackupTyp = ' + CAST(@BackupType AS VARCHAR) + ' - nur vollständige DB-Backups werden unterstützt).';
            SET @Index = @Index + 1;
            CONTINUE;
        END

        -- Filelist auslesen
        TRUNCATE TABLE #FileList;
        BEGIN TRY
            INSERT INTO #FileList
            EXEC ('RESTORE FILELISTONLY FROM DISK = ''' + REPLACE(@FullPath, '''', '''''') + '''');
        END TRY
        BEGIN CATCH
            PRINT 'Fehler beim Lesen der Filelist von ' + @FullPath + ': ' + ERROR_MESSAGE();
            SET @Index = @Index + 1;
            CONTINUE;
        END CATCH

        -- MOVE-Klauseln generieren
        SET @MoveCmd = '';
        SELECT @MoveCmd = @MoveCmd + 
            ', MOVE ' + QUOTENAME(LogicalName, '''') + ' TO ' + 
            QUOTENAME(
                CASE WHEN Type = 'D' THEN @DestDirDbFiles ELSE @DestDirLogFiles END + 
                REVERSE(SUBSTRING(REVERSE(PhysicalName), 1, CHARINDEX('\', REVERSE(PhysicalName)) - 1)),
                ''''
            ) + @crlf
        FROM #FileList;

        -- Basis-Restore-Befehl
        SET @RestoreCmd = 
            CASE WHEN @Single = 1 THEN 'ALTER DATABASE ' + QUOTENAME(@DbName) + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE;' + @crlf ELSE '' END +
            'RESTORE DATABASE ' + QUOTENAME(@DbName) + @crlf +
            'FROM DISK = ' + QUOTENAME(@FullPath, '''') + @crlf +
            'WITH ' + @crlf +
            CASE WHEN @ReplaceExisting = 1 THEN '  REPLACE,' + @crlf ELSE '' END +
            '  STATS = ' + CAST(@Stats AS VARCHAR) + ',' + @crlf +
            STUFF(@MoveCmd, 1, 2, '') +   -- erstes Komma/Leerzeichen entfernen
            '  ' + @RecoveryState + @crlf +
            CASE WHEN @Single = 1 THEN 'ALTER DATABASE ' + QUOTENAME(@DbName) + ' SET MULTI_USER;' + @crlf ELSE '' END;

        -- Optional: VERIFYONLY
        IF @VerifyOnly = 1
        BEGIN
            DECLARE @VerifyCmd NVARCHAR(MAX) = 'RESTORE VERIFYONLY FROM DISK = ' + QUOTENAME(@FullPath, '''') + ';';
            IF @Whatif = 1
                PRINT '-- Verifiziere: ' + @VerifyCmd;
            ELSE
            BEGIN
                BEGIN TRY
                    EXEC sp_executesql @VerifyCmd;
                END TRY
                BEGIN CATCH
                    PRINT 'VERIFYONLY fehlgeschlagen für ' + @FullPath + ': ' + ERROR_MESSAGE();
                    SET @Index = @Index + 1;
                    CONTINUE;
                END CATCH
            END
        END

        -- Befehl speichern/ausführen
        IF @Whatif = 1
            PRINT @RestoreCmd;
        ELSE
        BEGIN
            BEGIN TRY
                EXEC sp_executesql @RestoreCmd;
                PRINT 'Restore von ' + @DbName + ' erfolgreich.';
            END TRY
            BEGIN CATCH
                PRINT 'Fehler beim Restore von ' + @DbName + ': ' + ERROR_MESSAGE();
            END CATCH
        END

        SET @Index = @Index + 1;
    END

    -- Aufräumen
    DROP TABLE #BackupFiles;
    DROP TABLE #HeaderResult;
    DROP TABLE #FileList;
    DROP TABLE #RestoreCommands;
END;
GO

 

Einschränkungen

  • Es werden nur vollständige Datenbank-Backups (BackupType = 1) unterstützt. Differential- oder Transaktionslog-Backups werden ignoriert.

  • Eine Backup-Datei sollte nur ein Backup-Set enthalten. Bei mehreren Sets wird nur das erste verwendet.

  • xp_dirtree erfordert die sysadmin-Rolle. Alternativ könnte man ein Directory Listing über xp_cmdshell oder eine CLR-Funktion realisieren.

  • Das Skript ist nicht für Azure SQL Database geeignet (dort gibt es keine xp_dirtree).

 

-- Nur anzeigen, was passieren würde
EXEC dbo.AutoRestoreBackups 
    @SourceDirBackupFiles = 'D:\Backups\',
    @Whatif = 1,
    @DestDirDbFiles = 'E:\Data\',
    @DestDirLogFiles = 'F:\Logs\';

-- Tatsächlich wiederherstellen, bestehende DBs ersetzen, mit SINGLE_USER
EXEC dbo.AutoRestoreBackups 
    @SourceDirBackupFiles = 'D:\Backups\',
    @Whatif = 0,
    @Single = 1,
    @ReplaceExisting = 1;

 

Cookies user preferences
We use cookies to ensure you to get the best experience on our website. If you decline the use of cookies, this website may not function as expected.
Accept all
Decline all
Analytics
Tools used to analyze the data to measure the effectiveness of a website and to understand how it works.
Google Analytics
Advertisement
If you accept, the ads on the page will be adapted to your preferences.
Google Ad
Save