Tabellen zwischen Dateigruppen
verschieben und kopieren
Warum mehrere Dateigruppen sinnvoll sind, wie man Tabellen darin verschiebt und ab SQL Server 2016 SP2 sogar direkt in eine Zieldateigruppe kopiert – alles mit T-SQL.
Warum mehrere Dateigruppen?
Standardmäßig legt SQL Server alle Objekte in der Dateigruppe PRIMARY ab. Das reicht für viele Anwendungsfälle – aber spätestens wenn eine Datenbank auf mehrere physische Datenträger verteilt werden soll oder historische Daten von den aktiven Betriebsdaten getrennt werden müssen, sind eigene Dateigruppen das Mittel der Wahl.
Typische Anwendungsfälle sind:
- Archivierung: Historische Daten auf günstigere, langsamere Speicher auslagern.
- Reporting: Eine Kopie einer Tabelle auf einem separaten Datenträger bereitstellen, damit Reports die OLTP-Last nicht erhöhen.
- Kapazität: Wenn eine einzelne Datei die physische Plattenkapazität sprengt, können Dateigruppen auf mehrere Laufwerke verteilt werden.
- Backup-Strategie: Dateigruppen können selektiv gesichert werden – nützlich bei sehr großen Datenbanken.
Testumgebung einrichten
Alle folgenden Beispiele bauen auf einer einfachen Testdatenbank auf. Das Skript erstellt die Datenbank TestDB mit zwei Tabellen und einer zweiten Dateigruppe HISTORY.
Datenbank und Tabellen anlegen
USE master; GO CREATE DATABASE TestDB; GO USE TestDB; GO -- Tabelle für Bestellungen (mit PK = Clustered Index) CREATE TABLE OrderData ( OrderID INT NOT NULL, Description NVARCHAR(50), CONSTRAINT PK_OrderData PRIMARY KEY (OrderID) ); GO -- Tabelle für Bestellprotokoll CREATE TABLE OrderLog ( OrderLogID INT NOT NULL IDENTITY(1,1), OrderID INT NOT NULL, LogDate DATETIME DEFAULT GETDATE(), CONSTRAINT PK_OrderLog PRIMARY KEY (OrderLogID) ); GO -- Testdaten einfügen INSERT INTO OrderData (OrderID, Description) VALUES (1, 'Bestellung 1'), (2, 'Bestellung 2'), (3, 'Bestellung 3'), (4, 'Bestellung 4'); INSERT INTO OrderLog (OrderID, LogDate) VALUES (1, '2026-01-01'), (2, '2026-01-02'), (3, '2026-01-03');
Zweite Dateigruppe hinzufügen
Nun erweitern wir die Datenbank um die Dateigruppe HISTORY. Der Dateiname und -pfad können frei gewählt werden – das ermöglicht zum Beispiel die Ablage auf einem anderen physischen Laufwerk.
USE master; GO -- Dateigruppe registrieren ALTER DATABASE TestDB ADD FILEGROUP HISTORY; -- Physische Datei der Dateigruppe zuordnen ALTER DATABASE TestDB ADD FILE ( NAME = 'HistoryData', FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\DATA\TestDB_History.ndf' ) TO FILEGROUP HISTORY; GO
.ndf (nicht .mdf). Die primäre Datei bleibt immer .mdf.
Aktuelle Dateigruppen und Tabellenzuordnungen prüfen
USE TestDB; GO -- Alle Dateigruppen der Datenbank SELECT * FROM sys.filegroups; -- Tabelle → Index → Dateigruppe SELECT o.[name] AS Tabelle, i.[name] AS Indexname, fg.[name] AS Dateigruppe FROM sys.indexes i INNER JOIN sys.filegroups fg ON i.data_space_id = fg.data_space_id INNER JOIN sys.all_objects o ON i.object_id = o.object_id WHERE o.type = 'U';
Beide Tabellen erscheinen zunächst in der Dateigruppe PRIMARY. Das ändern wir im nächsten Schritt.
Tabelle in eine andere Dateigruppe verschieben
Eine Tabelle mit einem geclusterten Index (typischerweise der Primärschlüssel) wird implizit durch den Clustered Index in einer Dateigruppe gespeichert. Wird der Index neu erstellt und dabei eine andere Dateigruppe angegeben, folgen die Daten automatisch.
Methode 1: DROP CONSTRAINT mit MOVE TO (einfach, aber unvollständig)
Die Option MOVE TO beim Löschen des Constraints verschiebt die Tabellendaten in die Zieldateigruppe – löscht den Index aber ersatzlos. Der Index muss danach manuell neu angelegt werden.
ALTER TABLE OrderLog DROP CONSTRAINT PK_OrderLog WITH (MOVE TO HISTORY);
CREATE UNIQUE CLUSTERED INDEX PK_OrderLog ON OrderLog (OrderLogID) WITH (DROP_EXISTING = ON) ON HISTORY;
Methode 2: CREATE INDEX mit DROP_EXISTING (empfohlen)
Die empfohlene Vorgehensweise kombiniert das Löschen des alten und das Erstellen des neuen Index in einem einzigen atomaren Schritt. DROP_EXISTING = ON sorgt dafür, dass der bestehende Index ersetzt wird – Sperrzeiten sind deutlich kürzer als bei zwei getrennten Operationen.
USE TestDB; GO -- Schritt 1: Vorhandenen Index-Namen und Aufbau ermitteln SELECT i.[name] AS IndexName, c.[name] AS Spalte, ic.is_descending_key, ic.key_ordinal FROM sys.indexes i INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id WHERE i.object_id = OBJECT_ID('OrderLog') AND i.type = 1; -- 1 = Clustered -- Schritt 2: Verschieben durch Neuanlage mit DROP_EXISTING CREATE UNIQUE CLUSTERED INDEX PK_OrderLog ON OrderLog (OrderLogID) WITH (DROP_EXISTING = ON) ON HISTORY; GO
Index-Namen und Spaltenzusammensetzung des vorhandenen Clustered Index mit
sys.indexesundsys.index_columnsermitteln.CREATE ... WITH (DROP_EXISTING = ON) ON HISTORYausführen – der Index wird in einem Schritt verschoben.Ergebnis mit der Kontrollabfrage auf
sys.indexesverifizieren.
Heap-Tabellen verschieben
Eine Heap-Tabelle hat keinen geclusterten Index. SQL Server speichert die Zeilen ohne definierte Reihenfolge. Da es keinen Clustered Index gibt, den man „in eine andere Dateigruppe" neu erstellen könnte, ist ein Umweg nötig.
Vorgehen Schritt für Schritt
USE TestDB; GO -- 1. Temporären Clustered Index auf Ziel-Dateigruppe erstellen -- (verschiebt die Daten physisch in die Dateigruppe HISTORY) CREATE UNIQUE CLUSTERED INDEX UIX_OrderLog_Temp ON OrderLog (OrderLogID) ON HISTORY; GO -- 2. Index wieder löschen, wenn die Tabelle ein Heap bleiben soll DROP INDEX UIX_OrderLog_Temp ON OrderLog; GO -- Resultat: Heap liegt nun in der Dateigruppe HISTORY
In der Praxis ist es selten sinnvoll, eine produktive Tabelle als Heap zu belassen. Wenn die Tabelle ohnehin einen Primärschlüssel erhalten soll, kann der Schritt „Index löschen" entfallen.
| Ausgangslage | Vorgehen | Aufwand |
|---|---|---|
| Tabelle mit Clustered Index (PK) | CREATE ... WITH (DROP_EXISTING = ON) ON HISTORY |
Gering |
| Heap-Tabelle, soll Heap bleiben | Temp. Index erstellen → verschieben → Index löschen | Mittel |
| Heap-Tabelle, PK soll gesetzt werden | Direkt Clustered Index auf Zieldateigruppe erstellen | Gering |
Tabellenkopie in einer anderen Dateigruppe erstellen
Ab SQL Server 2016 (13.x) SP2 unterstützt SELECT ... INTO eine optionale ON-Klausel, mit der die Zieldateigruppe direkt beim Erstellen der neuen Tabelle angegeben werden kann. Damit entfällt das nachträgliche Verschieben einer kopierten Tabelle vollständig.
USE TestDB; GO -- Vollständige Kopie von OrderLog in der Dateigruppe HISTORY SELECT * INTO OrderLogHistory ON HISTORY FROM OrderLog; -- Nur historische Datensätze (selektive Kopie) SELECT * INTO OrderLogArchive ON HISTORY FROM OrderLog WHERE LogDate < '2026-01-01';
Versionsübersicht
| SQL Server Version | SELECT INTO ON <Dateigruppe> | DROP_EXISTING |
|---|---|---|
| 2014 und älter | Nicht verfügbar | Verfügbar |
| 2016 RTM / SP1 | Nicht verfügbar | Verfügbar |
| 2016 SP2 und neuer | Verfügbar | Verfügbar |
| 2017, 2019, 2022 | Verfügbar | Verfügbar |
Vollständiges Beispiel: Archivierungsworkflow
Der folgende Block zeigt einen typischen Archivierungsworkflow: Historische Daten werden in die Dateigruppe HISTORY kopiert, mit einem Index versehen und anschließend aus der Quelltabelle entfernt.
USE TestDB; GO BEGIN TRANSACTION; -- 1. Historische Daten in Archivtabelle kopieren SELECT * INTO OrderLogArchive ON HISTORY FROM OrderLog WHERE LogDate < DATEADD(YEAR, -1, GETDATE()); -- 2. Index auf der Archivtabelle erstellen CREATE CLUSTERED INDEX CIX_OrderLogArchive_LogDate ON OrderLogArchive (LogDate) ON HISTORY; -- 3. Archivierte Zeilen aus der Produktionstabelle entfernen DELETE FROM OrderLog WHERE LogDate < DATEADD(YEAR, -1, GETDATE()); COMMIT TRANSACTION; GO