Zweck

Fügt alle Datenbanken in einem Verzeichnis dem SQL Server hinzu. 
Dies wird oftmals für schnelle Server Umzüge genutzt.

Hinweise zur Verwendung

  • Ausführungsumgebung: Das Skript muss auf einem Windows-basierten SQL Server mit aktivierten xp_cmdshell-Berechtigungen (sysadmin) ausgeführt werden.

  • Test vor Produktion: Führen Sie das Skript zuerst mit einer Kopie der MDF-Dateien aus, um unerwartete Seiteneffekte zu vermeiden.

  • Transaktionslogs: Nach einem CREATE DATABASE ... FOR ATTACH wird das zugehörige Logfile (.ldf) erwartet. Wenn es fehlt, versucht SQL Server, ein neues Log zu erstellen – dies kann fehlschlagen, wenn die Datenbank nicht ordnungsgemäß heruntergefahren wurde.

  • Berechtigungen: Der SQL Server-Dienst muss Lese- und Schreibzugriff auf das Verzeichnis @AttachFromDir haben.

  • Keine Unterstützung für Dateigruppen: Das Skript ignoriert sekundäre Dateien (.ndf). Datenbanken mit mehreren Dateigruppen können nicht korrekt angehängt werden.

-- =====================================================================
-- Skript: Automatisches Anhängen von Datenbanken aus einem Verzeichnis
-- Autor:   (Ihr Name)
-- Datum:   (aktuelles Datum)
-- 
-- Zweck:
--   Durchsucht ein angegebenes Verzeichnis nach .mdf-Dateien und hängt
--   jede Datenbank an die aktuelle SQL Server-Instanz an, sofern:
--     - Die Datenbank noch nicht existiert.
--     - Die interne Datenbankversion nicht neuer ist als die des Servers.
-- 
-- Sicherheit:
--   Benötigt Mitgliedschaft in der festen Serverrolle 'sysadmin'.
--   Vorübergehende Aktivierung von xp_cmdshell wird nach der Ausführung
--   in den ursprünglichen Zustand zurückversetzt.
-- 
-- Einschränkungen:
--   - Funktioniert nur mit Datenbanken, die aus einer einzelnen .mdf-Datei
--     bestehen (keine zusätzlichen .ndf oder separate .ldf).
--   - Nur Laufwerksbuchstaben (z.B. D:\) werden unterstützt; UNC-Pfade
--     (\\server\share) nicht.
--   - Die .mdf-Dateien müssen den primären Datenbanknamen als Basis haben.
--   - Die Dateien müssen direkt im angegebenen Ordner liegen (keine Unterordner).
-- 
-- Änderungshistorie:
--   [Datum]  Initiale Version (korrigiert und erweitert)
-- =====================================================================

SET NOCOUNT ON;

DECLARE @AttachFromDir nvarchar(500) = N'F:\Daten\SQL'   -- Hier das Quellverzeichnis anpassen

-- Tabellen für Dateiliste und Prüfergebnisse
DECLARE @TabFileNames TABLE (FileName nvarchar(260));
DECLARE @TabFileAttribiute TABLE (attrName sql_variant, attrValue sql_variant);

-- Variablen für Cursor und Verarbeitung
DECLARE @readDirCmd    nvarchar(1000);
DECLARE @Aktuell       nvarchar(160);
DECLARE @dbs           nvarchar(256);
DECLARE @phys_name     nvarchar(520);
DECLARE @dbccstmt      nvarchar(1000);
DECLARE @dbsVersionAttach INT;
DECLARE @VersionServer INT;

-- =====================================================================
-- 1. Vorbereitung: xp_cmdshell-Zustand sichern
-- =====================================================================
DECLARE @cmdshell_was_activated INT;
SELECT @cmdshell_was_activated = CAST(value_in_use AS INT)
FROM sys.configurations
WHERE name = 'xp_cmdshell';

IF @cmdshell_was_activated = 0
BEGIN
    EXEC sp_configure 'show advanced options', 1;
    RECONFIGURE;
    EXEC sp_configure 'xp_cmdshell', 1;
    RECONFIGURE;
END

-- =====================================================================
-- 2. Dateiliste aus Verzeichnis holen
-- =====================================================================
SET @readDirCmd = N'dir /b "' + @AttachFromDir + N'"\*.mdf';
INSERT INTO @TabFileNames (FileName)
EXEC xp_cmdshell @readDirCmd;

-- Ungültige Einträge entfernen (NULL, "File Not Found", leere Zeilen)
DELETE FROM @TabFileNames
WHERE FileName IS NULL 
   OR FileName = 'File Not Found'
   OR LTRIM(RTRIM(FileName)) = '';

-- =====================================================================
-- 3. Bereits angehängte Datenbankdateien herausfiltern
-- =====================================================================
DELETE a
FROM @TabFileNames a
INNER JOIN sys.master_files b
    ON UPPER(@AttachFromDir + N'\' + a.FileName) = UPPER(b.physical_name);

-- Falls keine Dateien übrig sind, abbrechen
IF NOT EXISTS (SELECT 1 FROM @TabFileNames)
BEGIN
    PRINT N'Keine neuen Dateien gefunden. Prüfen Sie das Verzeichnis: ' + @AttachFromDir;
    GOTO Cleanup;
END

-- =====================================================================
-- 4. Version des Servers ermitteln
-- =====================================================================
SELECT @VersionServer = CONVERT(INT, DATABASEPROPERTYEX('master', 'version'));

-- =====================================================================
-- 5. Cursor über die verbleibenden Dateien
-- =====================================================================
DECLARE cf CURSOR LOCAL FAST_FORWARD FOR
    SELECT FileName FROM @TabFileNames ORDER BY FileName;

OPEN cf;
FETCH NEXT FROM cf INTO @Aktuell;

WHILE @@FETCH_STATUS = 0
BEGIN
    BEGIN TRY
        SET @phys_name = @AttachFromDir + N'\' + @Aktuell;
        SET @dbccstmt = N'DBCC CHECKPRIMARYFILE (' + QUOTENAME(@phys_name, '"') + N', 2)';

        -- Prüfinformationen aus der MDF-Datei holen
        DELETE FROM @TabFileAttribiute;   -- Vorherige Daten löschen
        INSERT INTO @TabFileAttribiute
        EXEC (@dbccstmt);

        -- Datenbanknamen extrahieren
        SELECT @dbs = CONVERT(nvarchar(256), attrValue)
        FROM @TabFileAttribiute
        WHERE attrName = 'Database name';

        -- Datenbankversion der Datei extrahieren
        SELECT @dbsVersionAttach = CONVERT(INT, attrValue)
        FROM @TabFileAttribiute
        WHERE attrName = 'Database version';

        -- Prüfen, ob Datenbank bereits existiert oder Version zu hoch ist
        IF (@dbsVersionAttach > @VersionServer)
        BEGIN
            PRINT N'[Übersprungen] ' + @dbs + N' – Datenbankversion (' + CAST(@dbsVersionAttach AS nvarchar) + 
                  N') ist neuer als Serverversion (' + CAST(@VersionServer AS nvarchar) + N').';
        END
        ELSE IF EXISTS (SELECT 1 FROM sys.databases WHERE name = @dbs)
        BEGIN
            PRINT N'[Übersprungen] ' + @dbs + N' – Datenbank ist bereits am Server vorhanden.';
        END
        ELSE
        BEGIN
            -- Attach mit CREATE DATABASE (modern, statt sp_attach_single_file_db)
            DECLARE @attachSQL nvarchar(2000);
            SET @attachSQL = N'CREATE DATABASE ' + QUOTENAME(@dbs) + 
                             N' ON (FILENAME = ' + QUOTENAME(@phys_name, '''') + N') ' +
                             N'FOR ATTACH;';
            EXEC sp_executesql @attachSQL;

            PRINT N'[Erfolg]   Datenbank "' + @dbs + N'" angehängt von Datei: ' + @Aktuell;
        END

    END TRY
    BEGIN CATCH
        PRINT N'[Fehler]   Bei Datei "' + @Aktuell + N'": ' + ERROR_MESSAGE();
    END CATCH

    -- Temporäre Tabelle für nächsten Durchlauf leeren
    DELETE FROM @TabFileAttribiute;

    FETCH NEXT FROM cf INTO @Aktuell;
END

CLOSE cf;
DEALLOCATE cf;

-- =====================================================================
-- 6. Aufräumen: xp_cmdshell auf ursprünglichen Zustand zurücksetzen
-- =====================================================================
Cleanup:
IF @cmdshell_was_activated = 0
BEGIN
    EXEC sp_configure 'xp_cmdshell', 0;
    RECONFIGURE;
    EXEC sp_configure 'show advanced options', 0;
    RECONFIGURE;
END

SET NOCOUNT OFF;