Skip to Main Content
  • Questions
  • to find the list of procedures and their respective tables used inside the package

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Laxmi.

Asked: October 26, 2016 - 3:54 pm UTC

Last updated: March 20, 2018 - 11:26 am UTC

Version: Oracle 11.2.0.3.0

Viewed 50K+ times! This question is

You Asked

Hi Tom,

Is there any possible way to find the list of stored procedures and their respective tables being used inside the package?

I have tried using the below query, but i am getting the result as all the tables used inside the package (if i filter using any procedure name also pop out with the same set of result ).


WITH TESTING AS
(
select
DISTINCT
name,
type,
decode(usage,'DECLARATION', 'body only', 'DEFINITION', 'spec and body', usage) defined_on,
line body_line,
object_name
from user_identifiers ui
where type = 'PROCEDURE'
and usage_context_id = (select usage_id
from user_identifiers
where object_name = ui.object_name
and object_type = ui.object_type
and usage_context_id = 0)
and object_name = 'MY_PACKAGE_NAME'
and object_type = 'PACKAGE BODY'
order by line
)

SELECT DISTINCT X.*, Y.REFERENCED_NAME, Y.REFERENCED_TYPE FROM TESTING X, dba_dependencies Y
WHERE X.OBJECT_NAME = Y.NAME
AND X.OBJECT_NAME = 'MY_PACKAGE_NAME'
AND Y.REFERENCED_TYPE IN ('TABLE')
AND Y.OWNER = 'OWNER_NAME'
AND X.NAME = 'MY_PROCEDURE_NAME'
ORDER BY X.NAME, Y.REFERENCED_TYPE;

Can you please help on this?

and Chris said...

List of procedures in a package? Easy:

select * from user_identifiers
where  object_type like 'PACKAGE%'
and    usage in ('DECLARATION', 'DEFINITION')
and    type in ('FUNCTION', 'PROCEDURE');


List of tables in a package? Easy:

select distinct referenced_name 
from   user_dependencies
where  referenced_type = 'TABLE'
and    type like 'PACKAGE%'


(assuming there's no dynamical SQL...)

List of tables within each procedure in each package? Ummmm.....

The problem is, packaged procedures/functions aren't objects in their own right. So they don't have their own entry in *_dependencies. That's just at the package level.

So you have to kludge together a query that:

- Finds the tables from user_dependencies/user_tables
- Joins this to user_source where the upper(text) like '%' || table_name || '%'
- Find use the line declaration/definitions of procedures from user_identifiers to calculate the start/end line of these
- Return the user_source lines between these start and ends:

drop table t2 purge;
drop table t1 purge;
create table t1 (
  x int
);
create table t2 (
  x int
);

create or replace package pkg as
  procedure p;
  function f return t2.x%type;
end pkg;
/

create or replace package body pkg as
  procedure p as
  
  begin
    for c in (select * from t1) loop
      null;
    end loop;
  end p;
  
  function f return t2.x%type as
    retval t2.x%type;
  begin
    select x into retval from t2
    where  rownum = 1;
    return retval ;
  end f;
end pkg;
/

select pname, tab, type, text from (
select ui.name pname, ud.table_name tab, us.type, 
       ui.st, ui.en, us.line, us.text,
       max(line) over (partition by us.name, us.type) mx_line
from   user_source us
join   user_tables ud
on     upper(us.text) like '%' || table_name || '%'
join   (select name, object_type, object_name, 
               line st, lead(line) over (partition by object_type order by line)-1 en
        from   user_identifiers
        where  type in ('FUNCTION', 'PROCEDURE')
        and    usage in ('DECLARATION', 'DEFINITION')
        and    object_type like 'PACKAGE%') ui
on     ui.object_name = us.name
and    ui.object_type = us.type
where  us.name = 'PKG'
)
where  line between st and nvl(en, mx_line);

PNAME  TAB  TYPE          TEXT                                   
F      T2   PACKAGE         function f return t2.x%type;
        
P      T1   PACKAGE BODY      for c in (select * from t1) loop
  
F      T2   PACKAGE BODY      select x into retval from t2
      
F      T2   PACKAGE BODY      retval t2.x%type;
                 
F      T2   PACKAGE BODY    function f return t2.x%type as


Of course, there are a few gotchas with this:

- It doesn't pick up package level variables
- The table name might be in the code but not as a table reference (e.g. a comment)
- Nested procedures break it

create or replace package body pkg as

  global_v t2.x%type;

  procedure p as
  
  begin
    for c in (select * from t1) loop
      null;
    end loop;
  end p;
  
  function f return t2.x%type as
    retval t2.x%type;
    procedure nest is
    begin
   null;
    end nest;
          
  begin
    /* t1 comment */
    select x into retval from t2
    where  rownum = 1;
    return retval ;
  end f;
end pkg;
/

select pname, tab, type, text from (
select ui.name pname, ud.table_name tab, us.type, 
       ui.st, ui.en, us.line, us.text,
       max(line) over (partition by us.name, us.type) mx_line
from   user_source us
join   user_tables ud
on     upper(us.text) like '%' || table_name || '%'
join   (select name, object_type, object_name, 
               line st, lead(line) over (partition by object_type order by line)-1 en
        from   user_identifiers
        where  type in ('FUNCTION', 'PROCEDURE')
        and    usage in ('DECLARATION', 'DEFINITION')
        and    object_type like 'PACKAGE%') ui
on     ui.object_name = us.name
and    ui.object_type = us.type
where  us.name = 'PKG'
)
where  line between st and nvl(en, mx_line);

PNAME  TAB  TYPE          TEXT                                   
F      T2   PACKAGE         function f return t2.x%type;
        
P      T1   PACKAGE BODY      for c in (select * from t1) loop
  
F      T2   PACKAGE BODY      retval t2.x%type;
                 
F      T2   PACKAGE BODY    function f return t2.x%type as
      
NEST   T2   PACKAGE BODY      select x into retval from t2
      
NEST   T1   PACKAGE BODY      /* t1 comment */


So use with care...

Rating

  (2 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Laxmi, October 28, 2016 - 2:40 pm UTC

Thanks a lot Chris..

this solution partially fix my requirement, but yes, as you said we cant list out objects(not exactly) exists inside the package.
Thanks a lot for the solution provided and the detailed explanation

There is useful code already possible

Sunil Kantilal, March 19, 2018 - 11:55 am UTC

Hi,

The code written is doesn't full fill some basic requirement from where a oracle developer can start picking and reusing the code.

Let me know what you think. Refer the link here,

https://stackoverflow.com/questions/14812401/oracle-table-usage-across-stored-procedures

I have written a code to perform code analysis on package body (given table and schema) and works well for me. It can be extended to find other objects like functions and bring all information in single run. It can also be used to break down the package body into functions and procedure row values.

Chris Saxon
March 20, 2018 - 11:26 am UTC

Doesn't work for dynamic SQL:

create table t (
  x int
);

create or replace procedure p ( p int ) as
  cur sys_refcursor;
begin
  open cur for 
   'select * from t';
end p;
/

var owner varchar2(30);
var Tablename varchar2(30);

exec :owner := 'CHRIS';
exec :Tablename := 'T';

select user from dual;

USER    
CHRIS 

WITH TableDep as
-- This table returns references where the table is used within the code for UPDATE OR INSERT
(
SELECT 
owner as schemaname,
name as packagename, 
type as typename,
TEXT as refcodeline,
CASE WHEN upper(text) LIKE '%INSERT%' THEN 'INSERT'
     WHEN upper(text) LIKE '%UPDATE%' THEN 'UPDATE'
   WHEN upper(text) LIKE '%MERGE%' THEN 'MERGE'
END AS opr,
:Tablename AS Tablename,
line refline
FROM dba_source WHERE upper(owner) = upper(:OWNER)  
      AND type = 'PACKAGE BODY' 
      AND (
            upper(text) LIKE ('%INSERT INTO '||:Tablename||'%')
            OR 
            upper(text) LIKE ('%UPDATE%'||:Tablename||' %')
            OR
            upper(text) LIKE ('%MERGE%'||:Tablename||' %')
           )
  ),
ProcedureDetails as
-- This code build all procedures within the package for references that is found in above query
(
SELECT 
  owner as schemaname,
  name as packagename, 
  type as typename,
  TEXT,
  trim(REGEXP_SUBSTR(TEXT, '(PROCEDURE [[:print:]]+)\(',1,1,null,1))   as procedure_name,
  line startline,
  LEAD(line, 1) OVER (partition by name order by line)-1 as endline
FROM dba_source
WHERE owner = upper(:OWNER) 
      AND type = 'PACKAGE BODY' 
      AND upper(text) LIKE '%PROCEDURE%(%'
      and exists (SELECt 1 FROM TableDep WHERE TableDep.packagename=name)
)
,ProcCode as
-- This code builds procedures into one cell per program for a given package. Later to find the  effected procedures
(
SELECT 
       ProcTag.packagename ,
       ProcTag.schemaname,
       ProcTag.typename,
       ProcTag.PROCEDURE_NAME,
       ProcTag.startline,
       ProcTag.endline,
       TO_CLOB(rtrim(xmlagg(xmlelement(e,codeline.text).extract('//text()') order by line).GetClobVal(),',')) as Procedure_Code
FROM
    ProcedureDetails ProcTag
    INNER JOIN dba_source codeline ON ProcTag.packagename=codeline.name
                                      AND ProcTag.schemaname=codeline.owner
                                      and ProcTag.typename=codeline.type
                                      and codeline.line between ProcTag.startline and ProcTag.endline
--WHERE PROCEDURE_NAME='PROCEDURE TRANS_KAT_INSO'
    group by 
       ProcTag.packagename ,
       ProcTag.schemaname,
       ProcTag.typename,
       ProcTag.PROCEDURE_NAME,
       ProcTag.startline,
       ProcTag.endline
      )
-- extract all the reference code for the given table selected with it complete procedure code.
SELECT
ProcHeader.Packagename, ProcHeader.schemaname, ProcHeader.typename, ProcHeader.procedure_name, ProcHeader.Procedure_Code ,ProcHeader.startline,ProcHeader.endline,ProcReference.Tablename, ProcReference.opr
FROM 
  ProcCode ProcHeader
  INNER JOIN 
        (
          SELECT DISTINCT ProcCode.Packagename, ProcCode.schemaname, ProcCode.typename, ProcCode.procedure_name , TableDep.Tablename, TableDep.opr
          FROM               ProcCode 
                  INNER JOIN TableDep ON ProcCode.packagename=TableDep.packagename
                                            AND ProcCode.schemaname=TableDep.schemaname
                                            and ProcCode.typename=TableDep.typename
                                            and TableDep.refline between ProcCode.startline and ProcCode.endline
        ) ProcReference
    ON    ProcHeader.Packagename=ProcReference.Packagename
          AND ProcHeader.schemaname=ProcReference.schemaname
          AND ProcHeader.typename=ProcReference.typename
          AND ProcHeader.procedure_name=ProcReference.procedure_name;

no rows selected

More to Explore

Analytics

Analytic SQL got you confused? Check out Connor McDonald's complete video course.