SQL Server - Filegroups

SQL Server Filegroups: 

When working with SQL Server, we commonly think about databases in terms of tables, indexes, data files, and transaction log files. But between the logical database objects and the physical data files sits another important architectural component: Filegroups.

Filegroups provide a logical layer for organizing SQL Server data files and controlling where database objects are stored. When designed correctly, filegroups can help with:

  • Storage organization
  • Large database management
  • File and filegroup-level backup strategies
  • VLDB administration
  • Read-only archival data
  • Partitioning strategies
  • Administrative flexibility
Key Concept: A SQL Server filegroup is a logical container for one or more data files. Database objects can be assigned to filegroups, while SQL Server stores the actual pages inside the physical files belonging to those filegroups.

Understanding SQL Server Storage Architecture

Before discussing filegroups, let us understand where they fit within SQL Server storage architecture.

SQL Server Instance
↓
Database
↓
Filegroups
↓
Data Files (.mdf / .ndf)
↓
Extents
↓
Pages
↓
Tables and Indexes

A very important point is that filegroups contain data files logically. The physical files themselves store the actual database pages.

SQL Server Database File Types

A SQL Server database can normally contain three important file categories.

File Type Typical Extension Purpose
Primary Data File .mdf Primary database data file containing system information and user data depending on placement.
Secondary Data File .ndf Additional data files used to distribute or organize database storage.
Transaction Log File .ldf Stores transaction log records used for transactional consistency and recovery.
Important: Filegroups apply to SQL Server data files. Transaction log files are managed separately and are not members of filegroups.

What Is a Filegroup?

A filegroup is a logical collection of one or more SQL Server data files.

Instead of requiring the administrator to manage object placement directly against individual physical files, SQL Server allows objects to be associated with a filegroup.

The simplified relationship is:

Database
↓
Filegroup
↓
One or More Data Files
↓
Database Pages

PRIMARY Filegroup

Every SQL Server database contains a PRIMARY filegroup.

The primary data file belongs to the PRIMARY filegroup. Unless another filegroup is explicitly configured as the default, new objects are normally created in PRIMARY.

A simple database could therefore look like:

MyDatabase
│
├── PRIMARY Filegroup
  │    └── MyDatabase.mdf
└── Transaction Log
            └── MyDatabase_log.ldf

User-Defined Filegroups

DBAs can create additional filegroups to separate database objects logically.

For example:

PRIMARY
FG_DATA
FG_INDEX
FG_ARCHIVE

These names are administrator-defined and should ideally describe the purpose of the storage.

Creating a Filegroup

Use ALTER DATABASE to add a new filegroup.

ALTER DATABASE [YourDatabase]
ADD FILEGROUP [FG_DATA];

The filegroup now exists logically, but it still needs one or more physical data files before it can store data.

Adding a Data File to a Filegroup

ALTER DATABASE [YourDatabase]
ADD FILE
(
    NAME = N'YourDatabase_Data01',
    FILENAME = N'D:\SQLData\YourDatabase_Data01.ndf',
    SIZE = 1024MB,
    FILEGROWTH = 256MB
)
TO FILEGROUP [FG_DATA];

This creates a secondary data file and assigns it to FG_DATA.

DBA Best Practice: Configure sensible initial file sizes and fixed MB/GB autogrowth increments based on workload and storage capacity. Avoid creating tiny files that must constantly grow.

Multiple Files Inside a Filegroup

A filegroup can contain multiple data files.

FG_DATA
│
├── Data01.ndf
├── Data02.ndf
├── Data03.ndf
└── Data04.ndf

SQL Server uses a proportional-fill allocation mechanism when allocating pages among files in a filegroup. Files with more available free space generally receive proportionally more allocations.

Important: Adding more files does not automatically guarantee better performance. File layout should be based on actual I/O, capacity, allocation, workload and storage architecture requirements.

Creating a Table on a Specific Filegroup

Once a filegroup is available, a table can be explicitly placed on the filegroup.

CREATE TABLE dbo.CustomerOrders
(
    OrderID     BIGINT NOT NULL,
    CustomerID  INT NOT NULL,
    OrderDate   DATETIME2 NOT NULL,
    Amount      DECIMAL(18,2) NOT NULL
)
ON [FG_DATA];

The table is now associated with FG_DATA.

Placing an Index on a Filegroup

Indexes can also be created on specific filegroups.

CREATE NONCLUSTERED INDEX IX_CustomerOrders_OrderDate
ON dbo.CustomerOrders (OrderDate)
ON [FG_INDEX];

This provides additional flexibility when designing storage for large or specialized databases.

Important: Simply separating indexes and tables into different filegroups does not guarantee a performance improvement. The underlying physical storage and workload characteristics determine whether meaningful I/O separation actually exists.

A Critical Point About Clustered Indexes

A SQL Server clustered index contains the table's data at its leaf level. Therefore, moving or creating a clustered index on another filegroup can effectively move the table's data to that filegroup.

Conceptually:

Heap on PRIMARY
↓
Create Clustered Index ON FG_DATA
↓
Table Data Stored with Clustered Index
↓
Data Moves to FG_DATA

This distinction is especially important when planning data movement between filegroups.

Default Filegroup

Every database has a default filegroup. Objects created without an explicit filegroup specification are created in the default filegroup.

You can make a user-defined filegroup the default:

ALTER DATABASE [YourDatabase]
MODIFY FILEGROUP [FG_DATA] DEFAULT;

After this change, objects created without an explicit filegroup specification will normally be allocated to FG_DATA.

How to Check Filegroups

SELECT
    name,
    type_desc,
    is_default,
    is_read_only
FROM sys.filegroups
ORDER BY name;

Check Files and Their Filegroups

The following query helps identify which physical files belong to which filegroups:

SELECT
    fg.name AS FilegroupName,
    df.name AS LogicalFileName,
    df.physical_name AS PhysicalFileName,
    df.size * 8.0 / 1024 AS SizeMB,
    df.max_size,
    df.growth,
    df.is_percent_growth
FROM sys.database_files AS df
LEFT JOIN sys.filegroups AS fg
    ON df.data_space_id = fg.data_space_id
ORDER BY fg.name, df.file_id;
DBA Tip: Review not only file size, but also autogrowth configuration and available storage capacity. A correctly designed filegroup can still experience an outage if its files cannot grow and the underlying volume becomes full.

Find Which Filegroup Contains a Table

For a non-partitioned table, the data space associated with the heap or clustered index can be inspected to identify its placement.

SELECT
    s.name AS SchemaName,
    t.name AS TableName,
    i.name AS IndexName,
    i.index_id,
    ds.name AS DataSpaceName
FROM sys.tables AS t
INNER JOIN sys.schemas AS s
    ON t.schema_id = s.schema_id
INNER JOIN sys.indexes AS i
    ON t.object_id = i.object_id
INNER JOIN sys.data_spaces AS ds
    ON i.data_space_id = ds.data_space_id
WHERE i.index_id IN (0,1)
ORDER BY s.name, t.name;

For partitioned objects, the data space may represent a partition scheme rather than a single filegroup, so partition metadata should also be reviewed.

Why Do We Need Filegroups?

A small database usually does not need a complex filegroup structure. For large databases, however, filegroups can provide significant administrative flexibility.

1. Logical Storage Organization

Different categories of information can be organized into different filegroups.

FG_ACTIVE    → Current transactional data
FG_HISTORY   → Historical data
FG_ARCHIVE   → Archived data

2. Large Database Administration

For very large databases, separating data logically can make capacity planning, backup design, archival operations and data lifecycle management easier.

3. Filegroup Backup and Restore

SQL Server supports file and filegroup backup operations. This can be particularly valuable for very large databases where the backup and recovery strategy is deliberately designed around filegroup architecture.

BACKUP DATABASE [YourDatabase]
FILEGROUP = 'FG_ARCHIVE'
TO DISK = 'D:\SQLBackup\YourDatabase_FG_ARCHIVE.bak'
WITH COMPRESSION, STATS = 10;

4. Read-Only Historical Data

A filegroup containing historical information that no longer changes can potentially be configured as read-only.

ALTER DATABASE [YourDatabase]
MODIFY FILEGROUP [FG_ARCHIVE] READ_ONLY;

To make the filegroup writable again:

ALTER DATABASE [YourDatabase]
MODIFY FILEGROUP [FG_ARCHIVE] READ_WRITE;

Filegroups and Table Partitioning

Filegroups become particularly useful when combined with SQL Server partitioned tables.

For example, a large sales table may contain many years of data:

2023 → FG_2023
2024 → FG_2024
2025 → FG_2025
2026 → FG_2026

The logical architecture becomes:

Partitioned Table
↓
Partition Scheme
↓
Partition Function
↓
Different Partitions
↓
Filegroups
↓
Physical Data Files
Architecture Note: Partitioning and filegroups are related but different concepts. A partition scheme maps table or index partitions to filegroups. Creating multiple filegroups by itself does not partition a table.

Real-World Filegroup Design

Consider a large transactional database that contains current orders as well as several years of historical orders.

A possible architecture could look like:

PRIMARY
  → System/default objects

FG_ACTIVE
  → Current transactional data

FG_HISTORY
  → Historical operational data

FG_ARCHIVE
  → Older archival data

The archive filegroup could eventually become read-only if the archived information no longer changes.

This can provide a cleaner architecture for managing a very large database, but it should be introduced because the database has a real lifecycle, backup, recovery, or capacity-management requirement, not simply because multiple filegroups look more sophisticated.

Moving Data Between Filegroups

Object movement between filegroups requires planning.

For tables with clustered indexes, rebuilding the clustered index onto another filegroup is one method of moving the table's data.

CREATE UNIQUE CLUSTERED INDEX CIX_CustomerOrders_OrderID
ON dbo.CustomerOrders (OrderID)
WITH (DROP_EXISTING = ON)
ON [FG_DATA];
Production Warning: Moving large tables or rebuilding large indexes can generate substantial transaction log activity, consume temporary storage and create I/O pressure. Estimate space and log requirements before performing this operation on a production database.

Removing a Data File from a Filegroup

A data file cannot simply be removed while allocated pages remain inside it. A common administrative method is to first empty the file:

DBCC SHRINKFILE
(
    N'YourDatabase_Data04',
    EMPTYFILE
);

Once the file is empty and all requirements are satisfied, it can be removed:

ALTER DATABASE [YourDatabase]
REMOVE FILE [YourDatabase_Data04];
Important: EMPTYFILE is an administrative data-movement operation. It should not be confused with routinely shrinking database files as a maintenance strategy.

Removing a Filegroup

Once a user-defined filegroup contains no files or objects that prevent removal and is no longer required, it can be removed.

ALTER DATABASE [YourDatabase]
REMOVE FILEGROUP [FG_DATA];

Never perform this operation until the filegroup's dependencies and contents have been fully validated.

File Size and Autogrowth Strategy

Filegroup design and file sizing should be considered together.

Avoid designs similar to:

Initial Size: 10 MB
Autogrowth: 1 MB
Expected Database Size: 500 GB

Such a configuration can require an excessive number of growth operations.

A better design is to:

  • Estimate expected database growth
  • Pre-size data files appropriately
  • Use controlled fixed-size autogrowth increments
  • Monitor disk free space
  • Review unexpected growth patterns
  • Plan capacity before critical thresholds are reached

Common Filegroup Mistakes

1. Creating Multiple Filegroups Without a Requirement

Additional filegroups add administrative complexity. Use them where they solve an actual storage, backup, recovery, partitioning, lifecycle or manageability requirement.

2. Assuming More Files Automatically Improve Performance

More files do not automatically mean more performance. Underlying I/O architecture and workload behavior matter.

3. Separating Indexes Without Physical I/O Separation

Creating FG_DATA and FG_INDEX provides logical separation, but if every physical file uses the same underlying storage path, the expected physical I/O benefit may not exist.

4. Ignoring File Autogrowth Settings

Badly configured growth settings can create unnecessary growth events and operational surprises.

5. Allowing the PRIMARY Filegroup to Become Unnecessarily Large

For databases that genuinely need a multi-filegroup architecture, separating appropriate user objects from PRIMARY can provide cleaner storage organization and management.

6. Treating Filegroups as a Replacement for Backups

Filegroups are part of database storage architecture. They do not eliminate the need for a properly tested backup and recovery strategy.

Example Filegroup Design

For a sufficiently large database with clear data-lifecycle requirements, an architecture might look like:

Filegroup Purpose Example Objects
PRIMARY Primary/default storage Database system information and selected objects
FG_ACTIVE Active transactional data Orders, transactions
FG_HISTORY Historical data Older operational records
FG_ARCHIVE Archive storage Long-term historical data

Useful DBA Filegroup Queries

List Filegroups

SELECT
    data_space_id,
    name,
    type_desc,
    is_default,
    is_read_only
FROM sys.filegroups
ORDER BY data_space_id;

List Database Files

SELECT
    file_id,
    name,
    type_desc,
    physical_name,
    size * 8.0 / 1024 AS SizeMB,
    max_size,
    growth,
    is_percent_growth
FROM sys.database_files
ORDER BY file_id;

Map Files to Filegroups

SELECT
    fg.name AS FilegroupName,
    df.name AS LogicalFileName,
    df.physical_name,
    df.size * 8.0 / 1024 AS SizeMB
FROM sys.database_files AS df
INNER JOIN sys.filegroups AS fg
    ON df.data_space_id = fg.data_space_id
ORDER BY fg.name, df.file_id;

When Should You Use Multiple Filegroups?

Consider multiple filegroups when you have a genuine requirement such as:

  • Very large database management
  • Partitioned data architecture
  • Historical or archival data management
  • Read-only data
  • File/filegroup backup and recovery requirements
  • Specific storage placement requirements
  • Data lifecycle management
  • Administrative isolation of large datasets

For a small application database, a complicated filegroup architecture may provide little practical benefit.

How Should a DBA Think About Filegroups?

Business / Technical Requirement
↓
Data Size and Growth
↓
Data Lifecycle
↓
Backup and Restore Requirements
↓
Partitioning Requirements
↓
Storage Architecture
↓
Filegroup Design
↓
Data File Layout
↓
Monitoring and Capacity Management

SQL Server Filegroup Best Practices

  • Keep filegroup architecture as simple as the workload permits.
  • Create additional filegroups only when there is a clear operational benefit.
  • Use meaningful filegroup and logical file names.
  • Pre-size physical files based on expected growth.
  • Use sensible fixed-size autogrowth increments.
  • Monitor file and volume capacity proactively.
  • Understand the underlying storage before claiming performance benefits from file separation.
  • Plan transaction log capacity before moving large amounts of data.
  • Test file/filegroup backup and restore procedures before depending on them.
  • Document which business datasets belong to each filegroup.

The DBA Mindset

The wrong question is:

"How many filegroups should every SQL Server database have?"

The better question is:

"What storage, recovery, lifecycle, capacity or partitioning requirement would an additional filegroup solve for this database?"

If there is no clear answer, additional filegroups may simply be unnecessary complexity.

Final Thoughts

SQL Server filegroups are much more than logical names attached to database files. For the right workload, they become an important part of database storage architecture.

A well-designed filegroup strategy can provide:

  • Better logical data organization
  • More flexible storage management
  • Improved VLDB manageability
  • Filegroup-level backup options
  • Cleaner partitioning architecture
  • Better historical and archival data management

However, filegroups should solve real problems. Creating many filegroups or data files without understanding the workload can increase administrative complexity without delivering meaningful benefits.

Good SQL Server storage architecture is not about creating more files and filegroups. It is about placing and managing data according to workload, growth, recovery and lifecycle requirements.

Author: Ajit Kumar Patnaik
Technical Architects Zone (TAZ)
Practical Solutions | Real-World Experience | Continuous Learning

Comments

Popular posts from this blog

SQL Server Interview Questions & Answers: 200 Questions from Fundamentals to Architect Level

SQL Server Storage Architecture Explained: How SQL Server Stores Data Internally

SQL Server Performance Troubleshooting: Essential Scripts Every Database personnel or Architect Should Have