Thursday, February 23, 2012

Find common dependencies between objects using DBA_DEPENDENCIES

Probably, you are familiar with the situation to discover a batch of objects in your database getting invalidated, all of the sudden.
This is an aftermath of a DDL statement executed on an object, that is used, directly or indirectly, by all invalid objects.
Also, this object will, probably, not be invalid, when you discover this situation.
The following query will find an object's dependencies:
SELECT REFERENCED_OWNER, REFERENCED_NAME, REFERENCED_TYPE
FROM DBA_DEPENDENCIES
START WITH OWNER = [OBJECT'S OWNER]
AND NAME = [OBJECT'S NAME]
AND TYPE = [OBJECT'S TYPE]
CONNECT BY OWNER = PRIOR REFERENCED_OWNER
AND NAME = PRIOR REFERENCED_NAME
AND TYPE = PRIOR REFERENCED_TYPE;

If you want to find N objects' common dependencies, you should intersect the results of the above query for all objects:
SELECT REFERENCED_OWNER, REFERENCED_NAME, REFERENCED_TYPE
FROM DBA_DEPENDENCIES
START WITH OWNER = [OBJECT_1'S OWNER]
AND NAME = [OBJECT_1'S NAME]
AND TYPE = [OBJECT_1'S TYPE]
CONNECT BY OWNER = PRIOR REFERENCED_OWNER
AND NAME = PRIOR REFERENCED_NAME
AND TYPE = PRIOR REFERENCED_TYPE
INTERSECT
SELECT REFERENCED_OWNER, REFERENCED_NAME, REFERENCED_TYPE
FROM DBA_DEPENDENCIES
START WITH OWNER = [OBJECT_2'S OWNER]
AND NAME = [OBJECT_2'S NAME]
AND TYPE = [OBJECT_2'S TYPE]
CONNECT BY OWNER = PRIOR REFERENCED_OWNER
AND NAME = PRIOR REFERENCED_NAME
AND TYPE = PRIOR REFERENCED_TYPE
INTERSECT
...
...
...
INTERSECT
SELECT REFERENCED_OWNER, REFERENCED_NAME, REFERENCED_TYPE
FROM DBA_DEPENDENCIES
START WITH OWNER = [OBJECT_N'S OWNER]
AND NAME = [OBJECT_N'S NAME]
AND TYPE = [OBJECT_N'S TYPE]
CONNECT BY OWNER = PRIOR REFERENCED_OWNER
AND NAME = PRIOR REFERENCED_NAME
AND TYPE = PRIOR REFERENCED_TYPE;

Now, we should create a procedure to get this result easier.
Fist, we will need 3 tables.

In COMP_OBJS table, you will need to insert all objects' names, types and owners, of which you want to find the common dependencies.
SQL> DESC HELPDESK.COMP_OBJS
 Name                 Null?    Type
 -------------------- -------- -------------
 OBJECT_NAME                   VARCHAR2(128)
 OBJECT_TYPE                   VARCHAR2(30)
 OWNER                         VARCHAR2(30)

In COMP_DEP1 table, the 1st object's dependencies will be stored and in this you will find the common dependencies, after you run the procedure.
SQL> DESC HELPDESK.COMP_DEP1
 Name                 Null?    Type
 -------------------- -------- -------------
 OBJECT_NAME                   VARCHAR2(128)
 OBJECT_TYPE                   VARCHAR2(30)
 OWNER                         VARCHAR2(30)

In COMP_DEP2 table, we will store, one by one, the 2nd to Nth objects' dependencies.
SQL> DESC HELPDESK.COMP_DEP2
 Name                 Null?    Type
 -------------------- -------- -------------
 OBJECT_NAME                   VARCHAR2(128)
 OBJECT_TYPE                   VARCHAR2(30)
 OWNER                         VARCHAR2(30)

After you have created them, grant privileges to SYSTEM.
SQL> GRANT ALL ON HELPDESK.COMP_OBJS TO SYSTEM;
Grant succeeded.
SQL> GRANT ALL ON HELPDESK.COMP_DEP1 TO SYSTEM;
Grant succeeded.
SQL> GRANT ALL ON HELPDESK.COMP_DEP2 TO SYSTEM;
Grant succeeded.

Procedure COMP_DEPS will check if each COMP_DEP1's row exists in COMP_DEP2.
If not, then it will delete this row from COMP_DEP1.
In the end, COMP_DEP1's contents will be the common dependencies of our N objects.
The procedure is:
CREATE OR REPLACE PROCEDURE SYSTEM.COMP_DEPS
IS
   TYPE T_CURSOR IS REF CURSOR;

   V_CURSOR1   T_CURSOR;
   V_CURSOR2   T_CURSOR;
   V_NAME1     VARCHAR2 (128);
   V_TYPE1     VARCHAR2 (30);
   V_OWNER1    VARCHAR2 (30);
   V_NAME2     VARCHAR2 (128);
   V_TYPE2     VARCHAR2 (30);
   V_OWNER2    VARCHAR2 (30);
   V_COUNT1    INTEGER;
   V_COUNT2    INTEGER := 0;
BEGIN
   EXECUTE IMMEDIATE 'TRUNCATE TABLE HELPDESK.COMP_DEP1';

   EXECUTE IMMEDIATE 'TRUNCATE TABLE HELPDESK.COMP_DEP2';

   OPEN V_CURSOR1 FOR
      SELECT OBJECT_NAME, OBJECT_TYPE, OWNER FROM HELPDESK.COMP_OBJS;

   LOOP
      FETCH V_CURSOR1
      INTO V_NAME1, V_TYPE1, V_OWNER1;

      EXIT WHEN V_CURSOR1%NOTFOUND;

      IF V_COUNT2 = 0
      THEN
         INSERT INTO HELPDESK.COMP_DEP1 (OBJECT_NAME, OBJECT_TYPE, OWNER)
            (    SELECT REFERENCED_NAME, REFERENCED_TYPE, REFERENCED_OWNER
                   FROM DBA_DEPENDENCIES
             START WITH     OWNER = V_OWNER1
                        AND NAME = V_NAME1
                        AND TYPE = V_TYPE1
             CONNECT BY     OWNER = PRIOR REFERENCED_OWNER
                        AND NAME = PRIOR REFERENCED_NAME
                        AND TYPE = PRIOR REFERENCED_TYPE);
         COMMIT;
         V_COUNT2 := 1;
      ELSE
         INSERT INTO HELPDESK.COMP_DEP2 (OBJECT_NAME, OBJECT_TYPE, OWNER)
            (    SELECT REFERENCED_NAME, REFERENCED_TYPE, REFERENCED_OWNER
                   FROM DBA_DEPENDENCIES
             START WITH     OWNER = V_OWNER1
                        AND NAME = V_NAME1
                        AND TYPE = V_TYPE1
             CONNECT BY     OWNER = PRIOR REFERENCED_OWNER
                        AND NAME = PRIOR REFERENCED_NAME
                        AND TYPE = PRIOR REFERENCED_TYPE);
         COMMIT;

         OPEN V_CURSOR2 FOR
            SELECT OBJECT_NAME, OBJECT_TYPE, OWNER FROM HELPDESK.COMP_DEP1;

         LOOP
            FETCH V_CURSOR2
            INTO V_NAME2, V_TYPE2, V_OWNER2;

            EXIT WHEN V_CURSOR2%NOTFOUND;

            SELECT COUNT (*)
              INTO V_COUNT1
              FROM HELPDESK.COMP_DEP2
             WHERE     OBJECT_NAME = V_NAME2
                   AND OBJECT_TYPE = V_TYPE2
                   AND OWNER = V_OWNER2;

            IF (V_COUNT1 = 0)
            THEN
               DELETE FROM HELPDESK.COMP_DEP1
                     WHERE     OBJECT_NAME = V_NAME2
                           AND OBJECT_TYPE = V_TYPE2
                           AND OWNER = V_OWNER2;

               COMMIT;
            END IF;
         END LOOP;

         EXECUTE IMMEDIATE 'TRUNCATE TABLE HELPDESK.COMP_DEP2';

         CLOSE V_CURSOR2;
      END IF;
   END LOOP;

   CLOSE V_CURSOR1;
END COMP_DEPS;
/

Let's do a test.
Initially, we insert into COMP_OBJS table the objects we want to compare:
SQL> SELECT * FROM HELPDESK.COMP_OBJS;

OBJECT_NAMEOBJECT_TYPEOWNER
XXACC_TEST_LOCKBOXPROCEDUREXXACC
AR_BOE_REMIT_RECEIPTS_VVIEWAPPS
DUNPACKAGE BODYDUNNER
XXCRM_PLIROMESVIEWXXE

Let's assume these 4 objects were invalidated simultaneously and we want to find their common dependencies, to see if we can discover on which object a DDL was executed and caused the invalidations.
We execute COMP_DEPS:
SQL> EXEC SYSTEM.COMP_DEPS;

PL/SQL procedure successfully completed.

Let's see the result:
SQL> SELECT * FROM HELPDESK.COMP_DEP1;

OBJECT_NAMEOBJECT_TYPEOWNER
AR_CASH_RECEIPTS_ALLTABLEAR

So, all 4 initial objects share only one dependency, table AR.AR_CASH_RECEIPTS_ALL.
An ALTER statement on this table invalidated all 4 of them.
Now, you can check your audit mechanisms to find out who executed the ALTER.

No comments:

Post a Comment