SQL Server: Tabellen zwischen Dateigruppen verschieben

T-SQL SQL Server 2016+ Dateigruppen

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.

Hintergrund

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.
Hinweis zur Methodik: Das Verschieben einer Tabelle in SQL Server erfolgt nicht durch ein direktes „Umbenennen" des Speicherorts. Stattdessen wird der geclusterte Index neu erstellt – und dabei die Zieldateigruppe angegeben. Die Tabellendaten folgen dem Clustered Index.

Vorbereitung

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

T-SQL — Testdatenbank
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.

T-SQL — Dateigruppe anlegen
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
Dateiendung: Sekundäre Datenbankdateien erhalten üblicherweise die Endung .ndf (nicht .mdf). Die primäre Datei bleibt immer .mdf.

Aktuelle Dateigruppen und Tabellenzuordnungen prüfen

T-SQL — Dateigruppenübersicht
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.


Kernthema

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.

Nachteil: Index wird nicht neu erstellt
ALTER TABLE OrderLog 
DROP CONSTRAINT PK_OrderLog 
WITH (MOVE TO HISTORY);
Besser: Direkt in Zielgruppe neu erstellen
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.

T-SQL — Empfohlene Methode
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
Vorteil von DROP_EXISTING: SQL Server führt intern zunächst den neuen Index auf, bevor der alte gelöscht wird. Dadurch bleibt der Constraint während der gesamten Operation erhalten – es gibt keinen Zeitpunkt, an dem die Tabelle unindiziert ist.
  1. Index-Namen und Spaltenzusammensetzung des vorhandenen Clustered Index mit sys.indexes und sys.index_columns ermitteln.

  2. CREATE ... WITH (DROP_EXISTING = ON) ON HISTORY ausführen – der Index wird in einem Schritt verschoben.

  3. Ergebnis mit der Kontrollabfrage auf sys.indexes verifizieren.


Sonderfall

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.

Wichtig: Eine Heap-Tabelle kann nicht direkt in eine andere Dateigruppe verschoben werden. Sie muss zuerst einen geclusterten Index erhalten, der dann die Zieldateigruppe definiert. Soll die Tabelle anschließend wieder ein Heap sein, muss der Index danach explizit gelöscht werden.

Vorgehen Schritt für Schritt

T-SQL — Heap in andere Dateigruppe verschieben
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.

AusgangslageVorgehenAufwand
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

Ab SQL Server 2016 SP2

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.

T-SQL — SELECT INTO mit Dateigruppe (ab 2016 SP2)
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';
Einschränkungen von SELECT INTO: Die neu erstellte Tabelle ist ein Heap ohne Indizes, ohne Constraints und ohne Fremdschlüssel. Für eine produktive Nutzung müssen PK, Indizes und ggf. Constraints nachträglich angelegt werden.

Versionsübersicht

SQL Server VersionSELECT INTO ON <Dateigruppe>DROP_EXISTING
2014 und älterNicht verfügbarVerfügbar
2016 RTM / SP1Nicht verfügbarVerfügbar
2016 SP2 und neuerVerfügbarVerfügbar
2017, 2019, 2022VerfügbarVerfü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.

T-SQL — Archivierungsworkflow
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
Tipp – Integrität sicherstellen: Der gesamte Workflow sollte in einer Transaktion laufen. Schlägt das Erstellen der Archivtabelle oder das Anlegen des Index fehl, wird auch das DELETE aus der Quelltabelle nicht ausgeführt. Keine Datenverluste bei Fehlern.
SQL Server · Dateigruppen · T-SQL  ·  Artikel zur Weiterverwendung freigegeben