Tuesday, July 8, 2014

Estimate size of MSSQL Table with indices

How to estimate the size of a MSSQL table with indices?

It's not enough to just multiply number of rows with the size of each row, because there are other entities involved, mostly the index. Relevant concepts are clustered/non-clustered index, unique/non-unique index, fill factor of an index, fix/variable length column, page size (8192 or 8K bytes), Null bitmap etc.

For MSSQL 2008, see MSDN articles [1][2][3]. For other versions of MSSQL, relevant links are in articles [1][2].

[1] Estimating the Size of a Clustered Index (MSSQL 2008)
[2] Estimating the Size of a Nonclustered Index (MSSQL 2008)
[3] Estimating the Size of a Table with a Clustered Index (MSSQL 2000)

Note that "Step 1. Calculate the Space Used to Store Data in the Leaf Level" in [1] is basically copied from [3], the only difference in [1] is the addition of "3. If the clustered index is nonunique, account for the uniqueifier column:". This actually calculates the combined size of both data and index in the leaf level nodes. In that the title of article [1] is inaccurate.

A query that directly read data and index information from the database is below (from here):

with pages as (
    SELECT object_id, SUM (reserved_page_count) as reserved_pages, SUM (used_page_count) as used_pages,
            SUM (case 
                    when (index_id < 2) then (in_row_data_page_count + lob_used_page_count + row_overflow_used_page_count)
                    else lob_used_page_count + row_overflow_used_page_count
                 end) as pages
    FROM sys.dm_db_partition_stats
    group by object_id
), extra as (
    SELECT p.object_id, sum(reserved_page_count) as reserved_pages, sum(used_page_count) as used_pages
    FROM sys.dm_db_partition_stats p, sys.internal_tables it
    WHERE it.internal_type IN (202,204,211,212,213,214,215,216) AND p.object_id = it.object_id
    group by p.object_id
)
SELECT object_schema_name(p.object_id) + '.' + object_name(p.object_id) as TableName,  
       (p.reserved_pages + isnull(e.reserved_pages, 0)) * 8 as reserved_kb,
        pages * 8 as data_kb,
        (CASE WHEN p.used_pages + isnull(e.used_pages, 0) > pages 
              THEN (p.used_pages + isnull(e.used_pages, 0) - pages) ELSE 0 END) * 8 as index_kb,
        (CASE WHEN p.reserved_pages + isnull(e.reserved_pages, 0) > p.used_pages + isnull(e.used_pages, 0) 
         THEN (p.reserved_pages + isnull(e.reserved_pages, 0) - p.used_pages + isnull(e.used_pages, 0)) else 0 end) * 8 as unused_kb
from pages p
left outer join extra e on p.object_id = e.object_id

No comments:

Blog Archive

Followers