Wenn der Index nicht mehr weiß, wo oben ist
Indexe in SQL Server sind wie Karteikarten.
Wenn sie zu oft umsortiert, gelöscht und beschrieben werden,
stehen sie irgendwann kreuz und quer.
Das kostet Performance.
Vor allem bei großen Tabellen.
Und bei Joins.
Was bei Fragmentierung passiert
- SQL Server muss mehr Seiten lesen
- Mehr Leseoperationen = mehr I/O
- Mehr CPU durch ungünstige Pläne
- Langsame Abfragen, obwohl Index da ist
Ab 5 % Fragmentierung solltest Du aufräumen.
Ab 30 % wird’s Zeit für einen Rebuild.
Unterschied: Reorganize vs. Rebuild
| Maßnahme | Fragmentierung ab | Sperrt Tabelle | Statistiken neu? | Logging |
|---|---|---|---|---|
| Reorganize | 5 % | Nein | Nein | minimal |
| Rebuild | 30 % | Ja (Offline) | Ja | mehr Log |
Online-Rebuild geht nur in der Enterprise Edition.
Sonst steht die Tabelle kurz still.
Beispiel: Manuelle Analyse und Pflege
-- Fragmentierung auslesen
SELECT
OBJECT_NAME(i.object_id) AS Tabelle,
i.name AS Indexname,
ips.avg_fragmentation_in_percent,
ips.page_count
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
WHERE ips.page_count > 100
ORDER BY ips.avg_fragmentation_in_percent DESC;
Dann gezielt reorganisieren oder rebuilden:
-- Reorganize
ALTER INDEX [IX_Kunden_Name] ON dbo.Kunden REORGANIZE;
-- Rebuild
ALTER INDEX [IX_Kunden_Name] ON dbo.Kunden REBUILD WITH (FILLFACTOR = 90);
Automatisiert: Dynamisches Skript zur Pflege
DECLARE @Tabelle SYSNAME
DECLARE @Index SYSNAME
DECLARE @SQL NVARCHAR(MAX)
DECLARE cur CURSOR FOR
SELECT
OBJECT_NAME(i.object_id),
i.name
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
WHERE ips.avg_fragmentation_in_percent > 5
AND ips.page_count > 100;
OPEN cur
FETCH NEXT FROM cur INTO @Tabelle, @Index
WHILE @@FETCH_STATUS = 0
BEGIN
SET @SQL = '
IF EXISTS (
SELECT 1
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID(N''' + @Tabelle + '''), NULL, NULL, ''LIMITED'') ips
JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
WHERE i.name = ''' + @Index + ''' AND ips.avg_fragmentation_in_percent BETWEEN 5 AND 30
)
ALTER INDEX [' + @Index + '] ON [' + @Tabelle + '] REORGANIZE;
ELSE
ALTER INDEX [' + @Index + '] ON [' + @Tabelle + '] REBUILD WITH (FILLFACTOR = 90);'
EXEC sp_executesql @SQL
FETCH NEXT FROM cur INTO @Tabelle, @Index
END
CLOSE cur
DEALLOCATE cur
Läuft idealerweise nachts oder am Wochenende im SQL Agent.
Nur auf produktionsfernen Zeitfenstern.
Optional: Statistiken aktualisieren
Nach einem Rebuild sind die Statistiken frisch.
Nach Reorganize nicht.
Darum:
EXEC sp_updatestats;
Einmal pro Woche reicht bei den meisten KMU.
Monitoring: Letzte Indexpflege prüfen
SELECT
OBJECT_NAME(i.object_id) AS Tabelle,
i.name AS Indexname,
s.last_user_update
FROM sys.dm_db_index_usage_stats s
JOIN sys.indexes i ON s.object_id = i.object_id AND s.index_id = i.index_id
WHERE database_id = DB_ID()
ORDER BY s.last_user_update DESC;
So erkennst Du, ob ein Index überhaupt noch gebraucht wird.
Oder ob Du besser gleich ausmisten solltest.
Mein Setup in der Praxis
| Aufgabe | Intervall | Werkzeug |
|---|---|---|
| Fragmentierung prüfen | wöchentlich | SQL Agent Job |
| Reorg/Rebuild starten | automatisch | per Cursor / Skript |
| Stats aktualisieren | wöchentlich | sp_updatestats |
| Monitoring | monatlich | Reporting via View |
| Doku | jährlich | Export aus sys.indexes |
Ein Index ist kein Kunstwerk
Er muss funktionieren – nicht schön aussehen.
Aber wenn er fragmentiert ist, bringt er niemandem was.
Pflege ihn.
Automatisch. Regelmäßig.
Dann läuft der Laden. Auch ohne Drama.


