Quantcast
Viewing all articles
Browse latest Browse all 12

Ed Barlow Procedures: sp__montableusage updated for SAP Sybase ASE 15 and higher

Back in 2006, Mich Talebzadeh createdImage may be NSFW.
Clik here to view.
SAP Sybase
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

sp__montableusage


Viewing all articles
Browse latest Browse all 12

Trending Articles