How to Check SQL Server Database Size & Analyze Storage Distribution (Tables, Logs, Indexes)


2 views

The simplest way to check your SQL Server database size is using the sp_spaceused stored procedure:

USE YourDatabaseName;
EXEC sp_spaceused;

This returns two key metrics:

  • database_size: Total allocated space (including unused space)
  • unallocated space: Available space within the allocation

For a granular view of storage distribution by object, use this query:

SELECT 
    t.NAME AS TableName,
    s.Name AS SchemaName,
    p.rows AS RowCounts,
    SUM(a.total_pages) * 8 AS TotalSpaceKB, 
    SUM(a.used_pages) * 8 AS UsedSpaceKB, 
    (SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS UnusedSpaceKB
FROM 
    sys.tables t
INNER JOIN      
    sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN 
    sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN 
    sys.allocation_units a ON p.partition_id = a.container_id
LEFT OUTER JOIN 
    sys.schemas s ON t.schema_id = s.schema_id
WHERE 
    t.NAME NOT LIKE 'dt%' 
    AND t.is_ms_shipped = 0
    AND i.OBJECT_ID > 255 
GROUP BY 
    t.Name, s.Name, p.Rows
ORDER BY 
    TotalSpaceKB DESC;

To check transaction log size and usage:

DBCC SQLPERF(LOGSPACE);

Or for more details:

SELECT 
    name AS [Database Name],
    size/128.0 AS [Total Size in MB],
    size/128.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS INT)/128.0 AS [Available Space In MB]
FROM 
    sys.database_files
WHERE 
    type_desc = 'LOG';

To see physical file distribution:

SELECT 
    name AS [File Name], 
    physical_name AS [Physical Path],
    size/128.0 AS [Size in MB],
    CAST(FILEPROPERTY(name, 'SpaceUsed') AS INT)/128.0 AS [Used in MB],
    size/128.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS INT)/128.0 AS [Free in MB],
    CONVERT(DECIMAL(5,2), 
        (CAST(FILEPROPERTY(name, 'SpaceUsed') AS INT)/128.0) / (size/128.0) * 100
    ) AS [Percent Used]
FROM 
    sys.database_files;

Create a stored procedure for regular monitoring:

CREATE PROCEDURE dbo.usp_GetDatabaseSizeSummary
AS
BEGIN
    -- Database summary
    EXEC sp_spaceused;
    
    -- File-level details
    SELECT 
        DB_NAME() AS DatabaseName,
        name AS FileName,
        type_desc AS FileType,
        size/128.0 AS SizeMB,
        CAST(FILEPROPERTY(name, 'SpaceUsed') AS INT)/128.0 AS UsedMB,
        size/128.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS INT)/128.0 AS FreeMB
    FROM 
        sys.database_files;
END

The most straightforward way to get your database size is using the system stored procedure sp_spaceused:

EXEC sp_spaceused;

This returns:

database_name   database_size   unallocated space
--------------  --------------  -----------------
YourDBName      1250.50 MB      245.25 MB

For a more granular view of where your data is actually stored:

SELECT 
    t.NAME AS TableName,
    s.Name AS SchemaName,
    p.rows AS RowCounts,
    SUM(a.total_pages) * 8 AS TotalSpaceKB, 
    SUM(a.used_pages) * 8 AS UsedSpaceKB, 
    (SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS UnusedSpaceKB
FROM 
    sys.tables t
INNER JOIN      
    sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN 
    sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN 
    sys.allocation_units a ON p.partition_id = a.container_id
LEFT OUTER JOIN 
    sys.schemas s ON t.schema_id = s.schema_id
WHERE 
    t.NAME NOT LIKE 'dt%' 
    AND t.is_ms_shipped = 0
    AND i.OBJECT_ID > 255 
GROUP BY 
    t.Name, s.Name, p.Rows
ORDER BY 
    TotalSpaceKB DESC;

To specifically check transaction log size:

DBCC SHOWFILESTATS;

Or for more detailed log info:

SELECT 
    name AS [File Name], 
    physical_name AS [Physical Name],
    size/128.0 AS [Total Size in MB],
    size/128.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS int)/128.0 AS [Available Space In MB]
FROM 
    sys.database_files
WHERE 
    type_desc = 'LOG';

For a comprehensive view including all database objects:

SELECT 
    OBJECT_NAME(i.object_id) AS ObjectName,
    i.name AS IndexName,
    i.type_desc AS IndexType,
    s.used_page_count * 8 AS UsedSizeKB,
    s.reserved_page_count * 8 AS ReservedSizeKB
FROM 
    sys.dm_db_partition_stats s
INNER JOIN 
    sys.indexes i ON s.object_id = i.object_id AND s.index_id = i.index_id
ORDER BY 
    s.reserved_page_count DESC;

Here's a query I frequently use to identify tables consuming the most space:

WITH TableSizes AS (
    SELECT
        t.name AS TableName,
        s.Name AS SchemaName,
        p.rows AS RowCounts,
        SUM(a.total_pages) * 8 AS TotalSpaceKB
    FROM 
        sys.tables t
    INNER JOIN      
        sys.indexes i ON t.OBJECT_ID = i.object_id
    INNER JOIN 
        sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
    INNER JOIN 
        sys.allocation_units a ON p.partition_id = a.container_id
    LEFT OUTER JOIN 
        sys.schemas s ON t.schema_id = s.schema_id
    GROUP BY 
        t.Name, s.Name, p.Rows
)
SELECT TOP 10
    TableName,
    SchemaName,
    RowCounts,
    TotalSpaceKB,
    CAST(TotalSpaceKB / 1024.0 AS DECIMAL(10,2)) AS TotalSpaceMB,
    (TotalSpaceKB * 100.0) / (SELECT SUM(TotalSpaceKB) FROM TableSizes) AS PercentOfTotal
FROM 
    TableSizes
ORDER BY 
    TotalSpaceKB DESC;