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

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