Calculating disk space requirements for secondary indexes

/*
 * table_size_info.rw
 *
 * DESCRIPTION:
 * Report to view table size information.  It selects all table/index info
 * from the system catelogs.  Then, based on the last time the structure was
 * modified, it will calculate the amount of pages the table will take if
 * remodified with the parameters used the last time (like MINPAGES &
 * FILLFACTOR).
 *
 * CREATION:
 *      Mark Kale
 *
 * MODIFICATIONS:
 * Init    Date    Comments
 * ----    ----    --------
 * mdk	   092496  Added total size of db
 * mdk     092496  Got rid of length and key_length columns, added Kb
Waste
 *
 */

.name   table_size_info

.shortremark    DBA:  Table Size & Re-Modification Information

.lm 0
.rm 80
.pl 66
.noformfeeds

.setup
    set autocommit on;          /* for interactive use */
    set lockmode session where readlock = nolock;

.query
    select
        db = dbmsinfo ('database'),
        t.table_name,
        t.storage_structure,
        t.is_compressed,
        t.table_type,
        t.num_rows,
        t.row_width,
        t.number_pages,
        t.overflow_pages,
        data_fill = float8 (float8(t.table_dfillpct) / 100),
        leaf_fill = float8 (float8(t.table_lfillpct) / 100),
        index_fill = float8 (float8(t.table_ifillpct) / 100),
        minpages = t.table_minpages,
        maxpages = t.table_maxpages,
        c.column_name,
        c.column_nulls,
        c.column_datatype,
        col_len = c.column_length
    from
        iitables t,
        iicolumns c
    where
        t.table_owner = dbmsinfo ('dba')    /* DBA tables */
    and t.storage_structure != 'HEAP'       /* have no keys */
    and t.table_type in ('T', 'I')          /* also get indexes */
    and t.table_name = c.table_name
    and t.table_owner = c.table_owner
    and c.key_sequence > 0                  /* only count keys */

    union

    select
        db = dbmsinfo ('database'),
        t.table_name,
        t.storage_structure,
        t.is_compressed,
        t.table_type,
        t.num_rows,
        t.row_width,
        t.number_pages,
        t.overflow_pages,
        data_fill = (t.table_dfillpct / 100),
        leaf_fill = (t.table_lfillpct / 100),
        index_fill = (t.table_ifillpct / 100),
        minpages = t.table_minpages,
        maxpages = t.table_maxpages,
        column_name = '',
        column_nulls = 'N',
        column_datatype = '',
        col_len = 0
    from
        iitables t
    where
        t.table_owner = dbmsinfo ('dba')    /* DBA tables */
    and t.storage_structure = 'HEAP'
    and t.table_type = 'T'

.sort   number_pages :desc, table_name, column_name

.position
    table_name (0),
    storage_structure (24),
    number_pages (30),
    overflow_pages (46),
    num_rows (54)

.declare
    key_len         = integer,  /* key length */
    rows_per_page   = integer,  /* number of tuples per data page */
    page_break      = integer,
    x               = integer,
    free            = integer,
    keys_per_lpage  = integer,  /* number of keys per leaf page */
    keys_per_ipage  = integer,  /* number of keys per index page */
    max_keys        = integer,
    leaf_pages      = integer,
    data_pages      = integer,
    remainder       = integer,
    sprig_pages     = integer,
    index_pages     = integer,
    required_pages  = integer,
    csum            = integer with value "0",
    nsum            = integer with value "0",
    rsum            = integer with value "0",
    waste           = float

/* Need a separate header for REPORT since we want the report description only
 * on the first page.
 */
.header report
    .left   .pr "DB: ", uppercase (db) (c0)
    .center .pr "Table Size Information"
    .right  .pr "As of: ", date('today') (d"03-feb-01")
    .nl
    .right  .pr "Page : ", page_number
    .center .pr "TABLE_SIZE_INFO.RW"
    .nl 2
    .tab 5  .pln
    "This report shows all DBA-owned tables, there current size as
reported by"
    .tab 5  .pln
    "the system catelogs, and the amount of space the table would require if"
    .tab 5  .pln
    "re-modified with the same parameters as it was before.  A %Waste is also"
    .tab 5  .pln
    "given which shows the relative waste.  If OVFLW is indicated, it means the"
    .tab 5  .pln
    "table/index is actually 'fuller' than it would be if re-modified. For"
    .tab 5  .pln
        "compressed tables, the compression ratio is shown."
    .nl 2
    .ul
    .tab table_name .pr "Table"
    .tab storage_structure .pr "Type"
    .right number_pages .pr "# Pages"
    .right 45 .pr "Req Pgs"
    .right overflow_pages .pr "# Ovflw"
    .right num_rows .pr "# Rows"
    .right 73 .pr "Waste(Kb)"
    .tab 74 .pr "%Waste"
    .nl
    .noul
    .let page_break = 1

.header page
    .left   .pr "DB: ", uppercase (db) (c0)
    .center .pr "Table Size Information"
    .right  .pr "As of: ", date('today') (d"03-feb-01")
    .nl
    .right  .pr "Page : ", page_number
    .nl 2
    .ul
    .tab table_name .pr "Table"
    .tab storage_structure .pr "Type"
    .right number_pages .pr "# Pages"
    .right 45 .pr "Req Pgs"
    .right overflow_pages .pr "# Ovflw"
    .right num_rows .pr "# Rows"
    .right 72 .pr "Waste(MB)"
    .tab 74 .pr "%Waste"
    .nl
    .noul
    .let page_break = 1

.header table_name
    .let key_len = 0

.detail
    /* figure out the real key_length */
    .let key_len = key_len + col_len
    .if column_datatype = 'VARCHAR' .then
        .let key_len = key_len + 2
    .elseif column_datatype = 'DATE' .then
        .let key_len = 12
    .elseif column_datatype = 'MONEY' .then
        .let key_len = 10
    .endif

    .if column_nulls = 'Y' .then
        .let key_len = key_len + 1
    .endif

.footer table_name
    .tab table_name .pr table_name (c24)
    .if is_compressed = 'Y' .then
        .tab (storage_structure)
        .tab -1
        .pr "C"     /* for compressed */
    .endif
    .tab storage_structure .pr storage_structure (c5)
    .if number_pages < 1000000 .then
        .right number_pages
        .pr number_pages ("zzz,zzn")
    .else
        .right number_pages
        .pr number_pages ("zzzzzzn")
    .endif
    .if storage_structure = 'HASH' or storage_structure = 'ISAM' .then
        .if overflow_pages < 1000000 .then
            .right overflow_pages
            .pr overflow_pages ("zzz,zzn")
        .else
            .right overflow_pages
            .pr overflow_pages ("zzzzzzn")
        .endif
    .endif
    .if num_rows < 10000000 .then
        .right num_rows
        .pr num_rows ("z,zzz,zzn")
    .else
        .right num_rows
        .pr num_rows ("zzzzzzzzn")
    .endif

    .if storage_structure = 'HASH' .then
        .let rows_per_page = (data_fill * 2008) / (row_width + 2)
        .if rows_per_page < 1 .then .let rows_per_page = 1 .endif

        .let required_pages = (num_rows / rows_per_page)
        .if required_pages < minpages .then
            .let required_pages = minpages
        .elseif required_pages > maxpages and maxpages > 0 .then
            .let required_pages = maxpages
        .endif

    .elseif storage_structure = 'HEAP' .then
        .let rows_per_page = 2008 / (row_width + 2)
        .if rows_per_page < 1 .then .let rows_per_page = 1 .endif

        .let required_pages = (num_rows / rows_per_page)

    .elseif storage_structure = 'BTREE' .then

        .let rows_per_page = (data_fill * 2010) / (row_width + 2)
        .if rows_per_page < 1 .then .let rows_per_page = 1 .endif

        .let free = 2008 - (rows_per_page * (row_width + 2))
        .if (free > (2048 - (data_fill * 2048))) and (row_width <= free) .then
            .let rows_per_page = rows_per_page + 1
        .endif
        .if rows_per_page > 512 .then .let rows_per_page = 512 .endif

        /* For BTREE secondary indexes, all data is at leaf level */
        .if table_type = 'I' .then
            .let key_len = row_width
        .endif

        .let max_keys = (1964 / (key_len + 6)) - 2
        .let keys_per_lpage = max_keys * leaf_fill
        .let keys_per_ipage = max_keys * index_fill
        .if keys_per_lpage < 2 .then .let keys_per_lpage = 2 .endif
        .if keys_per_ipage < 2 .then .let keys_per_ipage = 2 .endif

        .let leaf_pages = (num_rows / keys_per_lpage)

        .let data_pages = leaf_pages *
                          int4(float8(keys_per_lpage) / rows_per_page + .9999)
        .let remainder = num_rows - (leaf_pages * keys_per_lpage)

        .if remainder > 0 .then
            .let leaf_pages = leaf_pages + 1
            .let data_pages = data_pages +
                              int4(float8(remainder) / rows_per_page + .9999)
        .endif

        .if table_type = 'I' .then
            .let data_pages = 0
        .endif

        .if leaf_pages <= keys_per_ipage .then
            .let sprig_pages = 0
        .else
            .let sprig_pages = (float8(leaf_pages) / keys_per_ipage +
.9999)
        .endif

        /* figure number of non-index pages, an iterative calulation which
         * is fudged here.  Will have a separate calculation for each level
         * of non-sprig index pages.
         */
        .let index_pages = 0
        .if sprig_pages > keys_per_ipage .then
            .let x = sprig_pages / keys_per_ipage
            .let index_pages = x
            .if x > keys_per_ipage .then
                .let x = x / keys_per_ipage
                .let index_pages = index_pages + x
                .if x > keys_per_ipage .then
                    .let x = x / keys_per_ipage
                    .let index_pages = index_pages + x
                    .if x > keys_per_ipage .then
                        .let x = x / keys_per_ipage
                        .let index_pages = index_pages + x
                        .if x > keys_per_ipage .then
                            .let x = x / keys_per_ipage
                            .let index_pages = index_pages + x
                        .endif
                    .endif
                .endif
            .endif
        .endif  /* have non-sprig pages */

        .let required_pages = data_pages + leaf_pages + sprig_pages +
                              index_pages + 2

    .elseif storage_structure = 'ISAM' .then

        .let rows_per_page = (data_fill * 2008) / (row_width + 2)
        .if rows_per_page < 1 .then .let rows_per_page = 1 .endif

        .let free = 2008 - (rows_per_page * (row_width + 2))
        .if (free > (2048 - (data_fill * 2048))) and (row_width <= free) .then
            .let rows_per_page = rows_per_page + 1
        .endif
        .if rows_per_page > 512 .then .let rows_per_page = 512 .endif

        .let data_pages = num_rows / rows_per_page

        .let keys_per_ipage = 2008 / (key_len + 2)
        .let index_pages = data_pages / keys_per_ipage

        .let required_pages = index_pages + data_pages
        .if required_pages >= 2 .then
            .let required_pages = required_pages + 2
        .else
            .let required_pages = 2
        .endif

    .endif

    .if required_pages <= 0 .then .let required_pages = 1 .endif
    .if required_pages < 1000000 .then
        .right 45
        .pr required_pages ("zzz,zzn")
    .else
        .right 45
        .pr required_pages ("zzzzzzn")
    .endif

    /* haven't figured out compression */
    .if is_compressed = 'N' .then
	.if (number_pages-required_pages) > 0 .then
	    .right 72 .pr ((number_pages-required_pages))*2 ("zzz,zzz")
	.endif
        .tab 74
        .let waste = (float8(number_pages - required_pages)
                        * 100 / required_pages)
        .if waste < 0 .then
            .pr "OvrFul"
        .else
            .pr waste (f6.1)
        .endif
        .let nsum = nsum + number_pages
        .let rsum = rsum + required_pages
    .else
        /* print compression ratio */
        .tab 74
        .let waste = required_pages / number_pages
        .pr waste (i4), ':1'
        .let csum = csum + number_pages
    .endif
    .nl

/*
 * DEBUG code
 *
.pln "tot = ", required_pages (i6), " data= ", data_pages (i6),
     " leaf= ", leaf_pages (i6), " sprig= ", sprig_pages (i6),
     " idx = ", index_pages (i6)

.pln "lpage = ", keys_per_lpage (i3), " ipage= ", keys_per_ipage (i3),
     "rows/p= ", rows_per_page (i3)

*/

.footer report
	.need 5
	.nl 2
	.pln "Database is: ", ((nsum+csum) * 2 / 1024) ("zzz,zzz,zzn"), "Mb"
	.pln "It requires: ", ((rsum+csum) * 2 / 1024) ("zzz,zzz,zzn"), "Mb"
	.pln "We can save: ", (((nsum+csum) - (rsum+csum)) * 2 / 1024)
		("zzz,zzz,zzn"), "Mb"



Mark Kale                                       mdkale@aol.com
Caribou Lake Software, Inc.
www.CaribouLake.com
Ingres Q & A
To William's Home Page

© William Yuan 2000

Email William