Back in 2006, Mich Talebzadeh createdImage may be NSFW.
Clik here to view. the sp__montableusage stored procedure for reporting on table and index usage based on the MDA tables in Sybase ASE 12.5x using the MDA tables as part of the excellent Extended Stored Procedures by Ed Barlow (AKA the Ed Barlow Stored Procedures). In v15, Sybase deprecated the rowcnt() and reserved_pgs() functions that was used to report on the estimated number of rows in favor of the row_count() and reserved_pages() functions respectfully. I’ve updated the stored procedure to use the row_count() and reserved_pages() functions.
$ diff sp__montableusage.12 sp__montableusage.15 10,15c10,17 < ------------------------------------------------------------------------------------------------ < -- Vers| Date | Who | DA | Description < -------+--------+--------------------+----+----------------------------------------------------- < -- 1.0 |07/04/06| Mich Talebzadeh | | Stored procedure giving comprehensive report < -- | | | | about table and index usage based on the MDA tables < -------+--------+--------------------+----+----------------------------------------------------- --- > -------------------------------------------------------------------------------------------------- > -- Vers| Date | Who | DA | Description > -------+----------+--------------------+----+----------------------------------------------------- > -- 1.1 |11/18/2013| Jason Froebe | | Replace rowcnt with row_count() and reserved_pgs with > -- | | | | reserved_pages() > -- 1.0 |07/04/2006| Mich Talebzadeh | | Stored procedure giving comprehensive report > -- | | | | about table and index usage based on the MDA tables > -------+----------+--------------------+----+----------------------------------------------------- 27,28c29,30 < rowtotal = rowcnt(i.doampg), < reserved = (reserved_pgs(i.id, i.doampg) + reserved_pgs(i.id, i.ioampg)) * (low / 1024) --- > rowtotal = row_count(db_id(), o.id), > reserved = reserved_pages(db_id(), o.id, i.indid) * (low / 1024)
use sybsystemprocs go IF EXISTS(SELECT 1 FROM sysobjects WHERE type = 'P' AND name = 'sp__montableusage') BEGIN DROP PROCEDURE sp__montableusage END go create procedure sp__montableusage as -------------------------------------------------------------------------------------------------- -- Vers| Date | Who | DA | Description -------+----------+--------------------+----+----------------------------------------------------- -- 1.1 |11/18/2013| Jason Froebe | | Replace rowcnt with row_count() and reserved_pgs with -- | | | | reserved_pages() -- 1.0 |07/04/2006| Mich Talebzadeh | | Stored procedure giving comprehensive report -- | | | | about table and index usage based on the MDA tables -------+----------+--------------------+----+----------------------------------------------------- begin set nocount on -- -- First of all work out the table rows and sizes (reserved) -- select "Owner" = user_name(o.uid), TableName = o.name, IndexName = i.name, low = d.low, rowtotal = row_count(db_id(), o.id), reserved = reserved_pages(db_id(), o.id, i.indid) * (low / 1024) into #t1 from sysobjects o, sysindexes i, master.dbo.spt_values d where o.type = 'U' and i.id = o.id and d.number = 1 and d.type = "E" select distinct Owner, TableName, RowTotal = convert(char(11), sum(rowtotal)), Reserved = sum(reserved) into #table_size from #t1 group by Owner, TableName -- Identify tables accessed with a table scan SELECT "Owner" = user_name(o.uid), "TableName" = o.name, "LogicalReads" = m.LogicalReads, "PagesRead" = m.PagesRead, "WhenLastUsed" = m.LastUsedDate, "Used" = m.UsedCount INTO #tabscan from sysobjects o, master..monOpenObjectActivity m where o.type = 'U' and o.id = m.ObjectID and m.IndexID = 0 and m.DBID = db_id() and object_name(m.ObjectID, m.DBID) not like 'sa_%' and object_name(m.ObjectID, m.DBID) not like '%__sa%' and object_name(m.ObjectID, m.DBID) not like 'rs_%' and m.UsedCount > 0 print "" print 'Tables accessed with Table scans ONLY, no index usage' print "" SELECT "TableName" = substring(t.Owner+"."+t.TableName, 1, 30), "Rows" = convert(numeric(10,0),s.RowTotal), "Size/KB" = convert(numeric(10,0),s.Reserved), "LogicalReads" = t.LogicalReads, "PagesRead" = t.PagesRead, "Table scanned" = str(t.Used, 8, 0), "When last table scanned" = t.WhenLastUsed FROM #tabscan t, #table_size s WHERE t.Owner = s.Owner and t.TableName = s.TableName and not exists (select 1 from master..monOpenObjectActivity m where object_name(m.ObjectID, m.DBID) = t.TableName and object_name(m.ObjectID, m.DBID) = s.TableName and m.DBID = db_id() and m.IndexID > 0 and m.LastUsedDate is not NULL) ORDER BY t.Owner, t.TableName -- -- Identify tables with no DML -- SELECT "Owner" = user_name(o.uid), "TableName" = o.name, "LogicalReads" = m.LogicalReads, "LockRequests" = m.LockRequests, "Operations" = m.Operations, "Selected" = m.OptSelectCount, "WhenLastSelected" = m.LastOptSelectDate, "Used" = m.UsedCount, "WhenLastUsed" = m.LastUsedDate INTO #dormant from sysobjects o, master..monOpenObjectActivity m where object_name(m.ObjectID, m.DBID) = o.name and o.type = 'U' and o.id = m.ObjectID and m.IndexID = 0 -- Only tables! and m.DBID = db_id() and object_name(m.ObjectID, m.DBID) not like 'sa_%' and object_name(m.ObjectID, m.DBID) not like '%__sa%' and object_name(m.ObjectID, m.DBID) not like 'rs_%' and m.RowsInserted = 0 and m.RowsUpdated = 0 and m.RowsDeleted = 0 PRINT "" PRINT "Displaying dormant tables with no DML activity, table scan or index usage" PRINT "" SELECT "TableName" = substring(t.Owner+"."+t.TableName, 1, 30), "Rows" = convert(numeric(10,0),s.RowTotal), "Size/KB" = convert(numeric(10,0),s.Reserved), "LogicalReads" = t.LogicalReads, "LockRequests" = t.LockRequests FROM #dormant t, #table_size s WHERE t.Owner = s.Owner and t.TableName = s.TableName and t.WhenLastUsed is NULL -- table has never been used by the optimiser -- and no index of this table has been used by the optimiser and not exists (select 1 from master..monOpenObjectActivity m where object_name(m.ObjectID, m.DBID) = t.TableName and object_name(m.ObjectID, m.DBID) = s.TableName and m.DBID = db_id() and m.IndexID > 0 and m.LastUsedDate is not NULL) ORDER BY t.Owner, t.TableName -- -- Identify tables with DML activity -- SELECT "Owner" = user_name(o.uid), "TableName" = o.name, "LogicalReads" = m.LogicalReads, "LockRequests" = m.LockRequests, "Operations" = m.Operations, "Selected" = m.OptSelectCount, "WhenLastSelected" = m.LastOptSelectDate, "Used" = m.UsedCount, "WhenLastUsed" = m.LastUsedDate, "Inserted" = m.RowsInserted, "Updated" = m.RowsUpdated, "Deleted" = m.RowsDeleted INTO #temp from sysobjects o, master..monOpenObjectActivity m where object_name(m.ObjectID, m.DBID) = o.name and o.type = 'U' and o.id = m.ObjectID and m.IndexID = 0 and m.DBID = db_id() and object_name(m.ObjectID, m.DBID) not like 'sa_%' and object_name(m.ObjectID, m.DBID) not like '%__sa%' and object_name(m.ObjectID, m.DBID) not like 'rs_%' and (m.RowsInserted > 0 or m.RowsUpdated > 0 or m.RowsDeleted > 0) SELECT "TableName" = object_name(m.ObjectID, m.DBID), "IndexName" = i.name, "Selected" = m.OptSelectCount, "WhenLastSelected" = m.LastOptSelectDate, "Used" = m.UsedCount, "WhenLastUsed" = m.LastUsedDate into #used from master..monOpenObjectActivity m, sysindexes i where m.IndexID > 0 and m.IndexID <> 255 -- ignore text, image data chain and m.IndexID = i.indid and m.ObjectID = i.id and m.DBID = db_id() print "" if exists(select 1 from #used where Selected = 0 and Used = 0) begin print "" print 'Indexes never selected or used by the optimizer' print "" select "TableName" = substring(i.Owner+"."+i.TableName, 1, 30), u.IndexName, "IndexSize/KB" = i.reserved, u.Selected, u.Used from #used u, #t1 i where u.TableName = i.TableName and u.IndexName = i.IndexName and u.Selected = 0 and u.Used = 0 order by u.TableName, u.IndexName end if exists(select 1 from #used where Selected > 0 and Used = 0) begin print "" print 'Indexes selected by the optimizer but never used in query' print "" select "TableName" = substring(i.Owner+"."+i.TableName, 1, 30), u.IndexName, "IndexSize/KB" = i.reserved, u.Selected, "When Last selected" = u.WhenLastSelected from #used u, #t1 i where u.TableName = i.TableName and u.IndexName = i.IndexName and u.Selected > 0 and u.Used = 0 order by u.TableName, u.IndexName end if exists(select 1 from #used where Selected = 0 and Used > 0) begin print "" print 'Indexes Used by the optimizer but never selected' print "" select "TableName" = substring(i.Owner+"."+i.TableName, 1, 30), u.IndexName, "IndexSize/KB" = i.reserved, u.Selected from #used u, #t1 i where u.TableName = i.TableName and u.IndexName = i.IndexName and u.Selected = 0 and u.Used > 0 order by u.TableName, u.IndexName end PRINT "" PRINT "Displaying tables with DML activity" PRINT "" SELECT "TableName" = substring(t.Owner+"."+t.TableName, 1, 30), "Rows" = convert(numeric(10,0),s.RowTotal), "Size/KB" = convert(numeric(10,0),s.Reserved), t.Inserted, t.Updated, t.Deleted, t.LockRequests, "SUM DML ACTIVITY/ROWS " = CASE WHEN t.Inserted+t.Updated+t.Deleted > 0 and convert(numeric(10,0),s.RowTotal) > 0 THEN convert(varchar(9),(t.Inserted+t.Updated+t.Deleted)/convert(numeric(10, 0),s.RowTotal)) WHEN t.Inserted+t.Updated+t.Deleted > 0 and convert(numeric(10,0),s.RowTotal) = 0 THEN " ==> Update stats advisable" END FROM #temp t, #table_size s WHERE t.Owner = s.Owner and t.TableName = s.TableName ORDER BY t.Owner, t.TableName -- -- work out sum of index usage for tables where index(s) have been used -- SELECT TableName, SumUsed =sum(Used) into #sumused from #used where Used > 0 group by TableName print "" select TableName, IndexName, Selected, Used, WhenLastUsed into #clean from #used where Used > 0 print "" print 'Tables accessed with Table scans and index usage as well' print "" SELECT "TableName" = substring(t.Owner+"."+t.TableName, 1, 30), "Rows" = convert(numeric(10,0),s.RowTotal), "Size/KB" = convert(numeric(10,0),s.Reserved), "LogicalReads" = t.LogicalReads, "PagesRead" = t.PagesRead, "Table Scans" = str(t.Used, 8, 0), "Index Usage" = str(u.SumUsed, 8, 0), "IndexUsage/TableScan" = str(u.SumUsed*1.0/t.Used*1.0, 9, 2) FROM #tabscan t, #table_size s, #sumused u WHERE t.Owner = s.Owner and t.TableName = s.TableName and t.TableName = u.TableName and s.TableName = u.TableName and u.SumUsed > 0 ORDER BY t.Owner, t.TableName -- SELECT TableName, IndexName, Selected, Used, "Selected_over_sum_selected" = convert(numeric(10,2),Selected*1.0/sum(Selected)*1.0), "Used_over_sum_used" = convert(numeric(10,2),Used*1.0/sum(Used)*1.0), "Used_over_selected" = convert(numeric(10,2),Used*1.0/Selected*1.0) into #results from #clean group by TableName if exists (select 1 from #results) begin print "" print 'Index usage analysis' print "" select "TableName" = substring(i.Owner+"."+i.TableName, 1, 30), r.IndexName, "IndexSize/KB" = i.reserved, r.Selected, r.Used, --"Selected/SUM(Selected)" = r.Selected_over_sum_selected, "Used/SUM(Used)" = convert(numeric(10,2),r.Used_over_sum_used) --"Used/Selected" = r.Used_over_selected from #results r, #t1 i where r.TableName = i.TableName and r.IndexName = i.IndexName --and r.Used_over_sum_used < 1.0 order by r.TableName, r.Used_over_sum_used desc end end go grant exec on sp__montableusage to public go exit