Technical Article

DATABASE / TABLE SIZE ESTIMATER

,


The code in this procedure takes standard formula’s given by Microsoft and calculates approximate size of a table as per the fields  & indexs on that table. The procdure can be quickly be used in a loop to work for entire database.



Refer to article http://msdn.microsoft.com/library/default.asp?url=/library/en-us/createdb/cm_8_des_02_92k3.asp


for the actual document containing the formula’s used.

There are 2 major assumptions made
1. All The VARIABLE LENGTH columns will be completely filled. (you can change it easily)
2. FillFactor for all indexes in tables are same.

Usage:
declare  @objectid int, @num_rows float , @fill_factor  smallint
select @objectid = object_id('authors'), @num_rows = 2000 , @fill_factor = 90
exec gettablesize ( @objectid ,@num_rows ,@fill_factor  )

Rerurns:
Row Size , Data Size in KB, Clustered Index Size in KB,NonClustered Index Size in KB and total Size in KB



/**********************************************************************************************
Procedure Name   : gettablesize 
Author           : Amit Jethva 
Date             : Feb 11 2004 11:31AM
Purpose          : Table Size Estimation 
Tables Referred  : sysobjects,sysindexes, sysindexkeys ,syscolumns 
Input Parameters :
1. objectid : Id of Table For which size is to be estimated. 
Output Parameters:
   None, A Recordset containing Row Size , Data Size in KB, Clustered Index Size in KB,NonClustered Index Size in KB and total Size in KB

**********************************************************************************************/

create proc gettablesize ( @objectid int, @rows float , @fill_factor  smallint )
as
begin



declare 
        @num_rows                       float, 
@fixed_data_sizeint ,
@num_cols int ,
@num_variable_colsint , 
@has_clust_indexbit ,
@max_var_sizeint ,
@null_bitmapint ,
@variable_data_sizeint ,
@row_sizeint ,
@rows_per_pageint ,
@free_rows_per_page int ,
@num_pagesint , 

@num_ckey_colsint , 
@fixed_ckey_sizeint ,
@num_variable_ckey_colsint ,
@max_var_ckey_sizeint , 

@num_key_colsint , 
@fixed_key_sizeint ,
@num_variable_key_colsint ,
@max_var_key_sizeint , 

@cindex_null_bitmapint ,
@variable_ckey_sizeint ,
@cindex_row_sizeint ,
@cindex_rows_per_pageint , 
@num_pages_clevel_0float ,
@num_cindex_pagesint ,
@clustered_index_size_in_bytes  int ,
@index_null_bitmapint ,
@variable_key_sizeint ,
@nl_index_row_sizeint ,
@index_row_sizeint ,
@index_rows_per_pageint , 
@nl_index_rows_per_pageint , 
@num_pages_level_0float ,
@num_index_pagesint ,
@nc_index_size_in_bytes  int ,
@total_nc_index_size_in_bytes  int ,
@free_index_rows_per_pageint , 
@total_nc_index_size_in_kbytes  float ,
@data_space_used_in_kb  float ,
@clustered_index_size_in_kbytes float, 
@data_space_used_in_byte  bigint ,
@indidtinyint 


        if not  exists ( select 1 from sysobjects where type ='U' and id = @objectid ) 
        begin
           print 'User Table specified by given id does not exits. please use a vaild objectid'
           return 1
        end

        select @num_rows = @rows 
select @num_cols = count(*) ,
@fixed_data_size= sum(case when c.xtype in (231, 167, 165, 99) then 0 else c.length end  )  ,
@num_variable_cols= sum(case when c.xtype in (231, 167, 165, 99) then 1 else 0 end ) ,
@max_var_size= sum(case when c.xtype in (231, 167, 165, 99) then c.length else 0 end  )  ,
@has_clust_index= objectproperty ( o.id, 'tablehasclustindex'  ), 
@num_ckey_cols= isnull( ( select keycnt from sysindexes i where i.id = o.id and i.indid = 1 ) , 0 ) ,
@fixed_ckey_size= case objectproperty ( o.id, 'tablehasclustindex' )when 0 then 0 else ( 
select isnull(sum(ic.length) , 0) from sysindexkeys ik inner join syscolumns ic 
on (ic.colid = ik.colid and ic.id = ik.id )
where ik.id = o.id and ik.indid = 1   
and ic.xtype not in  (231, 167, 165, 99)
 ) end , 
@num_variable_ckey_cols = case objectproperty ( o.id, 'tablehasclustindex' )when 0 then 0 else ( 
select count(* ) from sysindexkeys ik inner join syscolumns ic 
on (ic.colid = ik.colid and ic.id = ik.id )
where ik.id = o.id and ik.indid = 1   
and ic.xtype in  (231, 167, 165, 99) 
) end,
@max_var_ckey_size= case objectproperty ( o.id, 'tablehasclustindex' )when 0 then 0 else ( 
select isnull(sum(ic.length) , 0) from sysindexkeys ik inner join syscolumns ic 
on (ic.colid = ik.colid and ic.id = ik.id )
where ik.id = o.id and ik.indid = 1   
and ic.xtype in  (231, 167, 165, 99)
 ) end 

from sysobjects o inner join syscolumns c on ( o.id = c.id )
where o.id = @objectid 
group by o.id,o.name 

/* null_bitmap) = 2 + (( num_cols + 7) / 8 ) */select @null_bitmap = floor(2 + (( @num_cols + 7) / 8 ) )  

/* total size of variable-length columns (variable_data_size) = 2 + (num_variable_cols x 2) + max_var_size
if there are no variable-length columns, set variable_data_size to 0.
this formula assumes that all variable-length columns are 100 percent full. */select  @variable_data_size = case when @num_variable_cols = 0 then 0 else 2 + (@num_variable_cols * 2) +  
@max_var_size end  

/* total row size (row_size) = fixed_data_size + variable_data_size + null_bitmap +4
the final value of 4 represents the data row header. */
select @row_size= @fixed_data_size + @variable_data_size + @null_bitmap + 4 

/* number of rows per page (rows_per_page) = ( 8096 ) / (row_size + 2) 
because rows do not span pages, the number of rows per page should be rounded down to the nearest whole row */select @rows_per_page= ceiling ( 8096 /  ( @row_size + 2 ) ) 


/* if a clustered index is to be created on the table, calculate the number of reserved free rows per page, 
based on the fill factor specified. if no clustered index is to be created, specify fill_factor as 100. 
number of free rows per page (free_rows_per_page) = 8096 x ((100 - fill_factor) / 100) / (row_size + 2)
the fill factor used in the calculation is an integer value rather than a percentage.
because rows do not span pages, the number of rows per page should be rounded down to the nearest whole row. as the  
fill factor grows, more data will be stored on each page and there will be fewer pages.
*/
select @free_rows_per_page = ceiling( 8096 * ( ( 100 - @fill_factor) / 100) / (@row_size + 2) ) 
/*calculate the number of pages required to store all the rows: 
number of pages (num_pages) = num_rows / (rows_per_page - free_rows_per_page) */
select @num_pages=  ceiling ( convert(float, @num_rows / (@rows_per_page - @free_rows_per_page) ) )

/* the amount of space required to store the data in a table (8192 total bytes per page): 
table_size_in_bytes = 8192 x num_pages */

select @data_space_used_in_byte = 8192 * @num_pages 
select @data_space_used_in_kb  = @data_space_used_in_byte  / 1024    


/* space used to store the clustered index */

/* if there are fixed-length columns in the clustered index, a portion of the index row is reserved for the null  
bitmap. calculate its size: 
index null bitmap (cindex_null_bitmap) = 2 + (( num_ckey_cols + 7) / 8 ) */ 
select @cindex_null_bitmap= floor(2 + (( @num_ckey_cols + 7) / 8 ) )  

/* total size of variable length columns (variable_ckey_size) = 2 + (num_variable_ckey_cols x 2) + max_var_ckey_size
if there are no variable-length columns, set variable_ckey_size to 0.
this formula assumes that all variable-length key columns are 100 percent full. 
*/
select @variable_ckey_size= case when @num_variable_ckey_cols = 0 then 0 else  2 + (@num_variable_ckey_cols *  
2) + ( @max_var_ckey_size * @fill_factor / 100  )  end 

/* total index row size (cindex_row_size) = fixed_ckey_size + variable_ckey_size + cindex_null_bitmap + 1 + 8 */ 

select @cindex_row_size= @fixed_ckey_size + @variable_ckey_size + @cindex_null_bitmap + 1 + 8 

/* the number of index rows per page (8096 free bytes per page): 
number of index rows per page (cindex_rows_per_page) = ( 8096 ) / (cindex_row_size + 2)
because index rows do not span pages, the number of index rows per page should be rounded down to the nearest whole  
row.
*/select @cindex_rows_per_page = ceiling( ( 8096.0 ) / @cindex_row_size + 2  )

/*
calculate the number of pages required to store all the index rows at each level of the index. 
number of pages (level 0) (num_pages_clevel_0) = (data_space_used / 8192) / cindex_rows_per_page
number of pages (level 1) (num_pages_clevel_1) = num_pages_clevel_0 / cindex_rows_per_page

repeat the second calculation, dividing the number of pages calculated from the previous level n by  
cindex_rows_per_page until the number of pages for a given level n (num_pages_clevel_n) equals one (index root page). for  
example, to calculate the number of pages required for the second index level:
number of pages (level 2) (num_pages_clevel_2) = num_pages_clevel_1 / cindex_rows_per_page

for each level, the number of pages estimated should be rounded up to the nearest whole page.
sum the number of pages required to store each level of the index:
total number of pages (num_cindex_pages) = num_pages_clevel_0 + num_pages_clevel_1 +
num_pages_clevel_2 + ... + num_pages_clevel_n   */

select @num_pages_clevel_0  = ceiling ( ( @data_space_used_in_byte / 8192.0 ) / @cindex_rows_per_page )


select @num_cindex_pages  = @num_pages_clevel_0 

while @num_pages_clevel_0 > 1 
begin
-- print @num_pages_clevel_0 
select @num_pages_clevel_0 = ceiling(@num_pages_clevel_0 /  @cindex_rows_per_page )
select @num_cindex_pages = @num_cindex_pages + @num_pages_clevel_0 

end 

/* clustered index size (bytes) = 8192 x num_cindex_pages */
select @clustered_index_size_in_bytes  = 8192 * @num_cindex_pages 
if @has_clust_index = 0 
select @clustered_index_size_in_bytes   = 0
select @clustered_index_size_in_kbytes = @clustered_index_size_in_bytes / 1024.0 

declare ind_cursor cursor for 
select indid , keycnt from sysindexes  i
where i.indid between 2 and 254  
and i.name not like '[_]wa[_]sys%' 
and i.id = @objectid 
select @total_nc_index_size_in_bytes =  0 
open ind_cursor 

fetch next from ind_cursor into @indid , @num_key_cols  

while @@fetch_status = 0
begin
/* 
calculate the space used to store each additional nonclustered index
the following steps can be used to estimate the amount of space required to store each additional  
nonclustered index: 

a nonclustered index definition can include fixed-length and variable-length columns. to estimate the size of  
the nonclustered index, you must calculate the space each of these groups of columns occupies within the index row: 
number of columns in index key = num_key_cols */
/* sum of bytes in all fixed-length key columns = fixed_key_size */select @fixed_key_size= isnull(sum(ic.length) , 0) 
from sysindexkeys ik inner join syscolumns ic 
on (ic.colid = ik.colid and ic.id = ik.id )
where ik.id = @objectid and ik.indid = @indid 
and ic.xtype not in  (231, 167, 165, 99)
 

/*
number of variable-length columns in index key = num_variable_key_cols
maximum size of all variable-length key columns = max_var_key_size */
select  @num_variable_key_cols =count(* )  , 
@max_var_key_size=  isnull(sum(ic.length) , 0)
from sysindexkeys ik inner join syscolumns ic 
on (ic.colid = ik.colid and ic.id = ik.id )
where ik.id = @objectid and ik.indid = @indid 
and ic.xtype in  (231, 167, 165, 99) 


/* if there are fixed-length columns in the index, a portion of the index row is reserved for the null  
bitmap. calculate its size: 
index null bitmap (index_null_bitmap) = 2 + (( num_key_cols + 7) / 8 ) 
only the integer portion of the above expression should be used; discard any remainder.  */select @index_null_bitmap= floor(2 + (( @num_key_cols + 7) / 8 ) )  


/* if there are variable-length columns in the index, determine how much space is used to store the columns  
within the index row:  
total size of variable length columns (variable_key_size) = 2 + (num_variable_key_cols x 2) +  
max_var_key_size

if there are no variable-length columns, set variable_key_size to 0.
this formula assumes that all variable-length key columns are 100 percent full. if you anticipate that a  
lower percentage of the variable-length key column storage space will be used, you can adjust the result by that percentage  
to yield a more accurate estimate of the overall index size.*/
select @variable_key_size= case when @num_variable_key_cols = 0 then 0 else  2 +  
(@num_variable_key_cols * 2) + ( @max_var_key_size * @fill_factor / 100  )  end 

if @has_clust_index = 1 
begin
/* non clustered index on a table with clustered index *//* calculate the nonleaf index row size: 
total nonleaf index row size (nl_index_row_size) = fixed_key_size + variable_key_size +  
index_null_bitmap + 1 + 8 */
select @nl_index_row_size = @fixed_key_size + @variable_key_size + @index_null_bitmap + 1 + 8 
/* calculate the number of nonleaf index rows per page: 
number of nonleaf index rows per page (nl_index_rows_per_page) = 
( 8096 ) / (nl_index_row_size + 2)

because index rows do not span pages, the number of index rows per page should be rounded down to the  
nearest whole row. */select @nl_index_rows_per_page = ceiling( ( 8096.0 ) / @nl_index_row_size + 2  )

/*
calculate the leaf index row size: 
total leaf index row size (index_row_size) = cindex_row_size + fixed_key_size + variable_key_size +  
index_null_bitmap + 1

the final value of 1 represents the index row header. cindex_row_size is the total index row size for  
the clustered index key. */
select @index_row_size = @cindex_row_size + @fixed_key_size + @variable_key_size + @index_null_bitmap  
+ 1


/*
calculate the number of leaf level index rows per page: 
number of leaf level index rows per page (index_rows_per_page) = ( 8096 ) / (index_row_size + 2)

because index rows do not span pages, the number of index rows per page should be rounded down to the  
nearest whole row. */
select @index_rows_per_page = ceiling(( 8096.0 ) / (@index_row_size + 2) )


/* 
calculate the number of reserved free index rows per page based on the fill factor specified for the  
nonclustered index. 
number of free index rows per page (free_index_rows_per_page) = 8096 x ((100 - fill_factor) / 100) /  
index_row_size 

the fill factor used in the calculation is an integer value rather than a percentage.

because index rows do not span pages, the number of index rows per page should be rounded down to the  
nearest whole row. */
select @free_index_rows_per_page = ceiling(8096 * ((100 - @fill_factor) / 100) / @index_row_size )


/* calculate the number of pages required to store all the index rows at each level of the index: 
number of pages (level 0) (num_pages_level_0) = num_rows / (index_rows_per_page -  
free_index_rows_per_page) 

number of pages (level 1) (num_pages_level_1) = num_pages_level_0 / nl_index_rows_per_page

repeat the second calculation, dividing the number of pages calculated from the previous level n by  
nl_index_rows_per_page until the number of pages for a given level n (num_pages_level_n) equals one (root page).

for example, to calculate the number of pages required for the second and third index levels:

number of data pages (level 2) (num_pages_level_2) = num_pages_level_1 / nl_index_rows_per_page

number of data pages (level 3) (num_pages_level_3) = num_pages_level_2 / nl_index_rows_per_page

for each level, the number of pages estimated should be rounded up to the nearest whole page.

sum the number of pages required to store each level of the index: 
total number of pages (num_index_pages) = num_pages_level_0 + num_pages_level_1 +num_pages_level_2 +  
... + num_pages_level_n */

select @num_pages_level_0 = ceiling( @num_rows / (@index_rows_per_page - @free_index_rows_per_page) )
select @num_index_pages  = @num_pages_level_0 

while @num_pages_level_0 > 1 
begin
-- print @num_pages_level_0 
select @num_pages_level_0 = ceiling(@num_pages_level_0 /  @nl_index_rows_per_page )
select @num_index_pages = @num_index_pages + @num_pages_level_0 

end 
end 
else
begin
/* non clustered index on a table without a clustered index *//* calculate the index row size: 
total index row size (index_row_size) = fixed_key_size + variable_key_size + index_null_bitmap + 1 +  
8 */select @index_row_size = @fixed_key_size + @variable_key_size + @index_null_bitmap + 1 + 8

/* calculate the number of index rows per page (8096 free bytes per page): 
number of index rows per page (index_rows_per_page) = ( 8096 ) / (index_row_size + 2) 
because index rows do not span pages, the number of index rows per page should be rounded down to the  
nearest whole row.*/select @index_rows_per_page = ceiling(( 8096 ) / (@index_row_size + 2))



/* calculate the number of reserved free index rows per leaf page, based on the fill factor specified  
for the nonclustered index. for more information, see fill factor. 
number of free index rows per leaf page (free_index_rows_per_page) = 8096 x ((100 - fill_factor) /  
100) / 
index_row_size

the fill factor used in the calculation is an integer value rather than a percentage.

because index rows do not span pages, the number of index rows per page should be rounded down to the  
nearest whole row. */select @free_index_rows_per_page = ceiling( 8096 * (( 100 - @fill_factor) / 100) / @index_row_size )

/* calculate the number of pages required to store all the index rows at each level of the index: 
number of pages (level 0) (num_pages_level_0) = num_rows / (index_rows_per_page -  
free_index_rows_per_page)

number of pages (level 1) (num_pages_level_1) = num_pages_level_0 / index_rows_per_page

repeat the second calculation, dividing the number of pages calculated from the previous level n by  
index_rows_per_page until the number of pages for a given level n (num_pages_level_n) equals one (root page). for example, to  
calculate the number of pages required for the second index level:

number of pages (level 2) (num_pages_level_2) = num_pages_level_1 / index_rows_per_page

for each level, the number of pages estimated should be rounded up to the nearest whole page.

sum the number of pages required to store each level of the index:

total number of pages (num_index_pages) = num_pages_level_0 + num_pages_level_1 + num_pages_level_2 +  
... + num_pages_level_n */


select @num_pages_level_0 = ceiling( @num_rows / (@index_rows_per_page - @free_index_rows_per_page) )
select @num_index_pages  = @num_pages_level_0 

while @num_pages_level_0 > 1 
begin
-- print @num_pages_level_0 
select @num_pages_level_0 = ceiling(@num_pages_level_0 /  @index_rows_per_page )
select @num_index_pages = @num_index_pages + @num_pages_level_0 

end 


end 

/*calculate the size of the nonclustered index: nonclustered index size (bytes) = 8192 x num_index_pages 
*/select @nc_index_size_in_bytes = 8192 * @num_index_pages
-- print @nc_index_size_in_bytes 
select @total_nc_index_size_in_bytes = @total_nc_index_size_in_bytes +  @nc_index_size_in_bytes 

fetch next from ind_cursor into @indid , @num_key_cols 
end

close ind_cursor 
deallocate  ind_cursor 


select @total_nc_index_size_in_kbytes   = @total_nc_index_size_in_bytes  / 1024.0

select 'row size' = @row_size,
--'number of pages' = @num_pages, 
--'data size in bytes' = @data_space_used_in_byte  ,
'data size in kb' = @data_space_used_in_kb  , 
--'clustered_index_size_in_bytes'   = @clustered_index_size_in_bytes  ,
'clustered_index_size_in_kbytes'  = @clustered_index_size_in_kbytes , 
--'nclustered_index_size_in_bytes'   = @total_nc_index_size_in_bytes  ,
'nclustered_index_size_in_kbytes'  = @total_nc_index_size_in_kbytes  ,
'total size'   = @data_space_used_in_kb  + @clustered_index_size_in_kbytes  + @total_nc_index_size_in_kbytes  

end 
go

Rate

5 (1)

Share

Share

Rate

5 (1)