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 generiertenRESTORE-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_dirtreeerfordert diesysadmin-Rolle. Alternativ könnte man ein Directory Listing überxp_cmdshelloder 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;