

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Análisis de las dependencias de los objetos para realizar migraciones parciales de bases de datos de Oracle a PostgreSQL
<a name="multilevel-object-analysis-for-database-migration-from-oracle-to-postgresql"></a>

*anuradha chintha, Amazon Web Services*

## Resumen
<a name="multilevel-object-analysis-for-database-migration-from-oracle-to-postgresql-summary"></a>

Este patrón describe la importancia de identificar y administrar de forma sistemática las dependencias del sistema al migrar una base de datos de Oracle a Amazon Relational Database Service (Amazon RDS) o Amazon Aurora PostgreSQL. En una migración parcial, solo se migra un subconjunto de objetos de base de datos y datos de la base de datos original, mientras que la base de datos de origen sigue funcionando y sirviendo a aplicaciones que dependen de componentes no migrados

Debe identificar y analizar el alcance de la migración cuando gestione bases de datos a gran escala que tengan aplicaciones con acoplamiento ajustado con dependencias en las fases anteriores y posteriores. Para iniciar una migración parcial, identifique los objetos que están en el alcance, incluidas las tablas, los desencadenadores, las vistas, los procedimientos almacenados, las funciones y los paquetes. El proceso de identificación del alcance sigue un enfoque integral:
+ Los objetos que están en el alcance de primer nivel se identifican mediante referencias directas en el código de la aplicación y en los trabajos críticos específicos de los módulos.
+ Los objetos de segundo nivel se obtienen mediante un análisis de dependencia exhaustivo.

Cuando comprenda cómo interactúan las diferentes partes del sistema, podrá planificar mejor el orden correcto para mover los componentes de la base de datos y reducir el riesgo de que se produzcan errores en la migración. En la siguiente tabla se enumeran los distintos tipos de análisis de las dependencias.


| 
| 
| Tipo de análisis | Áreas de interés | Finalidad | 
| --- |--- |--- |
| Dependencias de objetos | TablasVistasProcedimientos almacenadosFuncionesDesencadenadores | Identifica las relaciones entre los objetos de la base de datos y sus estructuras jerárquicas | 
| Dependencias de segmentos | Relaciones de claves externasRelaciones de claves principalesReferencias entre esquemas | Mapea las relaciones de los datos y mantiene la integridad de las referencias | 
| Dependencias de seguridad | Permisos de usuarioJerarquías de rolesPrivilegios de objeto | Garantiza el control de acceso, la migración y el mantenimiento de la seguridad adecuados | 
| Patrones de acceso | Operaciones de lecturaOperaciones de escritura | Determina los patrones de interacción de las bases de datos | 

Para mantener la coherencia entre los sistemas de origen y destino, establezca mecanismos de sincronización de datos durante el periodo de transición. También debe modificar el código y las funciones de la aplicación para gestionar la distribución de datos en las bases de datos de Oracle de origen y las bases de datos de PostgreSQL de destino.

## Requisitos previos y limitaciones
<a name="multilevel-object-analysis-for-database-migration-from-oracle-to-postgresql-prereqs"></a>

**Requisitos previos **
+ Un activo Cuenta de AWS
+ Una base de datos de Oracle (origen)
+ Una base de datos de Amazon RDS o Amazon Aurora PostgreSQL (destino)

**Versiones de producto**
+ Oracle 19c o posterior
+ PostgreSQL 16 o posterior

## Arquitectura
<a name="multilevel-object-analysis-for-database-migration-from-oracle-to-postgresql-architecture"></a>

**Pila de tecnología de origen**
+ Oracle 19c o posterior

**Pila de tecnología de destino**
+ Amazon RDS o Amazon Aurora PostgreSQL

**Arquitectura de destino**

En el siguiente diagrama se muestra el proceso de migración de una base de datos de Oracle en las instalaciones a Amazon RDS para Oracle, que incluye lo siguiente:
+ Identificación de las dependencias de la base de datos
+ Migración del código y los objetos de la base de datos mediante AWS Schema Conversion Tool ()AWS SCT
+ Migración de datos mediante AWS Database Migration Service ()AWS DMS
+ Replicar los cambios en curso mediante la captura de datos sobre cambios (CDC) mediante AWS DMS

Para obtener más información, consulte [Integración AWS Database Migration Service con AWS Schema Conversion Tool](https://docs.aws.amazon.com/SchemaConversionTool/latest/userguide/CHAP_DMSIntegration.html) en la AWS documentación.

![](http://docs.aws.amazon.com/es_es/prescriptive-guidance/latest/patterns/images/pattern-img/90160825-3199-4382-95a8-ad63139c5c89/images/b09c36a4-27fa-412e-877e-57a31bcce0dc.png)


## Tools (Herramientas)
<a name="multilevel-object-analysis-for-database-migration-from-oracle-to-postgresql-tools"></a>

**Servicios de AWS**
+ [Amazon Relational Database Service (Amazon RDS)](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Oracle.html) para Oracle lo ayuda a configurar, utilizar y escalar una base de datos relacional de Oracle en la Nube de AWS.
+ Amazon Aurora es un motor de base de datos relacional completamente administrado diseñado para la nube y compatible con MySQL y PostgreSQL.
+ AWS Schema Conversion Tool (AWS SCT) admite migraciones de bases de datos heterogéneas al convertir automáticamente el esquema de la base de datos de origen y la mayoría del código personalizado a un formato compatible con la base de datos de destino.
+ [AWS Database Migration Service (AWS DMS)](https://docs.aws.amazon.com/dms/latest/userguide/Welcome.html) le ayuda a migrar los almacenes de datos a una combinación de configuraciones locales y en la nube Nube de AWS o entre ellas.

**Otros servicios**
+ [Oracle SQL Developer](https://www.oracle.com/database/technologies/appdev/sqldeveloper-landing.html) es un entorno de desarrollo integrado que simplifica el desarrollo y la administración de bases de datos de Oracle, tanto en implementaciones tradicionales como en implementaciones basadas en la nube. Para este patrón, puede usar [SQL\*Plus](https://docs.oracle.com/cd/B19306_01/server.102/b14357/qstart.htm).

## Prácticas recomendadas
<a name="multilevel-object-analysis-for-database-migration-from-oracle-to-postgresql-best-practices"></a>

Para ver prácticas recomendadas sobre el aprovisionamiento y migración de bases de datos de Oracle, consulte [Best practices for migrating to Amazon RDS for Oracle](https://docs.aws.amazon.com/prescriptive-guidance/latest/migration-oracle-database/best-practices.html).

## Epics
<a name="multilevel-object-analysis-for-database-migration-from-oracle-to-postgresql-epics"></a>

### Identificación de las dependencias de objetos
<a name="identify-object-dependencies"></a>


| Tarea | Descripción | Habilidades requeridas | 
| --- | --- | --- | 
| Cree una tabla de objetos. | Identifique los objetos que son esenciales para la funcionalidad de la aplicación y cree una tabla con el nombre `DEPENDENT_ANALYSIS_BASELINE`. Agregue registros para cada objeto a la tabla. Para obtener ejemplos, consulte la sección *Información adicional*. | Ingeniero de datos, Administrador de base de datos | 
| Cree un procedimiento de la base de datos | Cree un procedimiento almacenado llamado `sp_object_dependency_analysis` para analizar las dependencias de los objetos en ambas direcciones (hacia adelante y hacia atrás) mediante los datos de la tabla `DBA_DEPENDENCIES`. Para obtener ejemplos, consulte la sección *Información adicional*. | Ingeniero de datos, Administrador de base de datos | 
| Ejecute el procedimiento. | Ejecute los scripts en cada nivel sucesivo hasta que no se encuentren nuevas dependencias entre objetos. Todas las dependencias y niveles se almacenan en la tabla `DEPENDENT_ANALYSIS_BASELINE`. | Administrador de base de datos, ingeniero de datos | 

### Creación de un procedimiento para las dependencias a nivel de segmento
<a name="create-a-procedure-for-segment-level-dependencies"></a>


| Tarea | Descripción | Habilidades requeridas | 
| --- | --- | --- | 
| Cree una tabla de dependencias. | Cree una tabla de dependencias a nivel de segmento denominada `REFERENTIAL_ANALYSIS_BASELINE`. Cuando se detecten todas las dependencias a nivel de objeto, compruebe las tablas principales de `DEPENDENT_ANALYSIS_BASELINE` consultando la tabla `DBA_CONSTRAINT`.<br />Excluya las dependencias en las que otras tablas hacen referencia a las tablas de referencia. La reposición gestiona estas relaciones. A continuación se muestra un ejemplo de script:<pre>CREATE TABLE REFERENTIAL_ANALYSIS_BASELINE<br />(CHILD_OWNER VARCHAR2(50 BYTE),<br />CHILD_NAME VARCHAR2(100 BYTE),<br />PARENT_OWNER VARCHAR2(50 BYTE),<br />PARENT_NAME VARCHAR2(50 BYTE),<br />REFERENCE_PATH VARCHAR2(1000 BYTE));</pre> | Ingeniero de datos, Administrador de base de datos | 
| Cree un procedimiento de la base de datos | Cree un procedimiento denominado `SP_OBJECT_REFERENTIAL_ANALYSIS` y genere un análisis referencial para todos los objetos identificados. Para obtener ejemplos, consulte la sección *Información adicional*. | Ingeniero de datos, Administrador de base de datos | 
| Ejecute el procedimiento. | Ejecute el procedimiento para obtener las dependencias referenciales. Genere detalles del objeto de análisis referencial en `REFERENTIAL_ANALYSIS_BASELINE`. | Ingeniero de datos, Administrador de base de datos | 

### Identificación de objetos que leen y escriben
<a name="identify-objects-that-read-and-write"></a>


| Tarea | Descripción | Habilidades requeridas | 
| --- | --- | --- | 
| Cree tablas para los objetos de lectura y escritura. | Utilice el siguiente script para crear una tabla de objetos de lectura denominada `TABLE_READ_OBJECT_DETAILS` y una tabla de objetos de escritura denominada `TABLE_WRITE_OBJECT_DETAILS`:<pre>CREATE TABLE TABLE_READ_OBJECT_DETAILS<br />(OWNER VARCHAR2(50 BYTE),<br />TAB_NAME VARCHAR2(50 BYTE),<br />READER_OWNER VARCHAR2(50 BYTE),<br />READER_NAME VARCHAR2(50 BYTE),<br />READER_TYPE VARCHAR2(50 BYTE));</pre><pre>CREATE TABLE TABLE_WRITE_OBJECT_DETAILS<br />(TABLE_NAME VARCHAR2(100 BYTE),<br />WRITEOBJ_OWNER VARCHAR2(100 BYTE),<br />WRITEOBJ_NAME VARCHAR2(100 BYTE),<br />WRITEOBJ_TYPE VARCHAR2(100 BYTE),<br />LINE VARCHAR2(100 BYTE),<br />TEXT VARCHAR2(4000 BYTE),<br />OWNER VARCHAR2(50 BYTE));</pre> | Ingeniero de datos, Administrador de base de datos | 
| Cree un procedimiento para el análisis. | Cree los procedimientos `SP_READER_OBJECTS_ANALYSIS` y `SP_WRITER_OBJECTS_ANALYSIS` para analizar los objetos de lectura y escritura, respectivamente. Estos procedimientos utilizan la coincidencia de patrones para buscar objetos relacionados. Para ver un ejemplo, consulte la sección *Información adicional*. | Ingeniero de datos, Administrador de base de datos | 
| Ejecute los procedimientos. | Ejecute estos procedimientos para identificar los objetos dependientes. | Administrador de base de datos, ingeniero de datos | 

### Revisión de privilegios de base de datos
<a name="review-database-privileges"></a>


| Tarea | Descripción | Habilidades requeridas | 
| --- | --- | --- | 
| Cree una tabla para revisar los privilegios. | Cree una tabla para analizar los privilegios denominada `OBJECT_PRIVS_ANALYSIS`. Para capturar de forma recursiva los privilegios de los objetos de la tabla `DEPENDENT_ANALYSIS_BASELINE`, utilice el siguiente script:<pre>CREATE TABLE OBJECT_PRIVS_ANALYSIS<br />(OWNER VARCHAR2(50 BYTE),<br />OBJECT_NAME VARCHAR2(50 BYTE),<br />USER_NAME VARCHAR2(50 BYTE),<br />PRIVS VARCHAR2(50 BYTE));</pre> | Ingeniero de datos, Administrador de base de datos | 
| Cree un procedimiento para revisar los privilegios. | Cree un procedimiento denominado `SP_OBJECT_PRIVS_ANALYSIS`. Genere un análisis de privilegios para los objetos identificados. Para obtener ejemplos, consulte la sección *Información adicional*. | Administrador de base de datos, ingeniero de datos | 
| Ejecute el procedimiento. | Ejecute el procedimiento para capturarlos en la tabla `OBJECT_PRIVS_ANALYSIS`. | Administrador de base de datos, ingeniero de datos | 

## Resolución de problemas
<a name="multilevel-object-analysis-for-database-migration-from-oracle-to-postgresql-troubleshooting"></a>


| Problema | Solución | 
| --- | --- | 
| No se puede acceder a las tablas del diccionario | Asegúrese de que el usuario que creó los objetos de análisis pueda acceder a las tablas de DBA. | 

## Recursos relacionados
<a name="multilevel-object-analysis-for-database-migration-from-oracle-to-postgresql-resources"></a>

**Documentación de AWS**
+ [Documentación de Amazon RDS y Aurora](https://docs.aws.amazon.com/rds/)
+ [Oracle database 19c to Amazon Aurora PostgreSQL migration playbook](https://docs.aws.amazon.com/dms/latest/oracle-to-aurora-postgresql-migration-playbook/chap-oracle-aurora-pg.html)
+ [¿Qué es? AWS Database Migration Service](https://docs.aws.amazon.com/dms/latest/userguide/)
+ [What is the AWS Schema Conversion Tool?](https://docs.aws.amazon.com/SchemaConversionTool/latest/userguide/)

**Otra documentación**
+ [Oracle database objects](https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/Database-Objects.html)

## Información adicional
<a name="multilevel-object-analysis-for-database-migration-from-oracle-to-postgresql-additional"></a>

**Script para `DEPENDENT_ANALYSIS_BASELINE`**

```
CREATE TABLE DEPENDENT_ANALYSIS_BASELINE
(OWNER VARCHAR2(128 BYTE) NOT NULL ENABLE,
OBJECT_NAME VARCHAR2(128 BYTE) NOT NULL ENABLE,
OBJECT_TYPE VARCHAR2(20 BYTE),
DEPEDNCY_LEVEL NUMBER,
PROJECT_NEED VARCHAR2(20 BYTE),
CATAGORY VARCHAR2(4000 BYTE),
COMMENTS VARCHAR2(4000 BYTE),
CATAGORY1 CLOB,
COMMENTS1 CLOB,
CUSTOMER_COMMENTS VARCHAR2(1000 BYTE),
BACKFILL_TO_GUS VARCHAR2(1000 BYTE),
BACKFILL_NEAR_REAL_TIME_OR_BATCH VARCHAR2(1000 BYTE),
PK_EXISTS VARCHAR2(3 BYTE),
UI_EXISTS VARCHAR2(3 BYTE),
LOB_EXISTS VARCHAR2(3 BYTE),
MASTER_LINK VARCHAR2(100 BYTE),
CONSTRAINT PK_DEPENDENT_ANALYSIS_BASELINE PRIMARY KEY (OWNER,OBJECT_NAME,OBJECT_TYPE));
```

**Procedimientos para `SP_WRITER_OBJECTS_ANALYSIS`**

```
CREATE OR REPLACE PROCEDURE SP_WRITER_OBJECTS_ANALYSIS IS
BEGIN
  EXECUTE IMMEDIATE 'TRUNCATE TABLE TABLE_WRITE_OBJECT_DETAILS';
  FOR I IN (SELECT OWNER, OBJECT_NAME FROM DEPENDENT_ANALYSIS_BASELINE WHERE OBJECT_TYPE = 'TABLE')
  LOOP
    INSERT INTO TABLE_WRITE_OBJECT_DETAILS(OWNER, TABLE_NAME, WRITEOBJ_OWNER, WRITEOBJ_NAME, WRITEOBJ_TYPE, LINE, TEXT)
    SELECT DISTINCT I.OWNER, I.OBJECT_NAME, OWNER WRITEOBJ_OWNER, NAME, TYPE, LINE, TRIM(TEXT)
    FROM DBA_SOURCE 
    WHERE UPPER(TEXT) LIKE '%' || I.OBJECT_NAME || '%'
      AND (UPPER(TEXT) LIKE '%INSERT%' || I.OBJECT_NAME || '%' 
        OR UPPER(TEXT) LIKE '%UPDATE%' || I.OBJECT_NAME || '%' 
        OR UPPER(TEXT) LIKE '%DELETE%' || I.OBJECT_NAME || '%' 
        OR UPPER(TEXT) LIKE '%UPSERT%' || I.OBJECT_NAME || '%' 
        OR UPPER(TEXT) LIKE '%MERGE%' || I.OBJECT_NAME || '%') 
      AND UPPER(TEXT) NOT LIKE '%PROCEDURE%' 
      AND UPPER(TEXT) NOT LIKE 'PROCEDURE%' 
      AND UPPER(TEXT) NOT LIKE '%FUNCTION%' 
      AND UPPER(TEXT) NOT LIKE 'FUNCTION%'
      AND UPPER(TEXT) NOT LIKE '%TRIGGER%' 
      AND UPPER(TEXT) NOT LIKE 'TRIGGER%' 
      AND UPPER(TRIM(TEXT)) NOT LIKE '%AFTER UPDATE%' 
      AND UPPER(TRIM(TEXT)) NOT LIKE 'BEFORE UPDATE%' 
      AND UPPER(TRIM(TEXT)) NOT LIKE 'BEFORE INSERT%' 
      AND UPPER(TRIM(TEXT)) NOT LIKE 'AFTER INSERT%' 
      AND UPPER(TRIM(TEXT)) NOT LIKE 'BEFORE DELETE%' 
      AND UPPER(TRIM(TEXT)) NOT LIKE 'AFTER DELETE%' 
      AND UPPER(TRIM(TEXT)) NOT LIKE '%GGLOGADM.GG_LOG_ERROR%' 
      AND (TRIM(TEXT) NOT LIKE '/*%' AND TRIM(TEXT) NOT LIKE '--%' ) 
      AND (OWNER, NAME, TYPE) IN (SELECT OWNER, NAME, TYPE FROM DBA_DEPENDENCIES WHERE REFERENCED_NAME = I.OBJECT_NAME);
  END LOOP;
END;
```

**Script para `SP_READER_OBJECTS_ANALYSIS`**

```
CREATE OR REPLACE PROCEDURE SP_READER_OBJECTS_ANALYSIS IS
BEGIN
  EXECUTE IMMEDIATE 'TRUNCATE TABLE TABLE_READ_OBJECT_DETAILS';
  FOR I IN (SELECT OWNER, OBJECT_NAME FROM DEPENDENT_ANALYSIS_BASELINE WHERE OBJECT_TYPE = 'TABLE')
  LOOP
    INSERT INTO TABLE_READ_OBJECT_DETAILS
    SELECT DISTINCT i.owner, i.object_name, owner, name, type 
    FROM dba_dependencies 
    WHERE referenced_name = I.OBJECT_NAME
    AND referenced_type = 'TABLE' 
    AND type NOT IN ('SYNONYM', 'MATERIALIZED VIEW', 'VIEW') 
    AND (owner, name, type) NOT IN (
      SELECT DISTINCT owner, trigger_name, 'TRIGGER' 
      FROM dba_triggers 
      WHERE table_name = I.OBJECT_NAME 
      AND table_owner = i.owner
      UNION ALL
      SELECT DISTINCT owner, name, type 
      FROM dba_source
      WHERE upper(text) LIKE '%' || I.OBJECT_NAME || '%' 
      AND (upper(text) LIKE '%INSERT %' || I.OBJECT_NAME || '%' 
        OR upper(text) LIKE '%UPDATE% ' || I.OBJECT_NAME || '%' 
        OR upper(text) LIKE '%DELETE %' || I.OBJECT_NAME || '%' 
        OR upper(text) LIKE '%UPSERT %' || I.OBJECT_NAME || '%' 
        OR upper(text) LIKE '%MERGE %' || I.OBJECT_NAME || '%') 
      AND upper(text) NOT LIKE '%PROCEDURE %' 
      AND upper(text) NOT LIKE 'PROCEDURE %'
      AND upper(text) NOT LIKE '%FUNCTION %' 
      AND upper(text) NOT LIKE 'FUNCTION %'
      AND upper(text) NOT LIKE '%TRIGGER %'
      AND upper(text) NOT LIKE 'TRIGGER %'
      AND upper(trim(text)) NOT LIKE 'BEFORE INSERT %'
      AND upper(trim(text)) NOT LIKE 'BEFORE UPDATE %' 
      AND upper(trim(text)) NOT LIKE 'BEFORE DELETE %' 
      AND upper(trim(text)) NOT LIKE 'AFTER INSERT %' 
      AND upper(trim(text)) NOT LIKE 'AFTER UPDATE %' 
      AND upper(trim(text)) NOT LIKE 'AFTER DELETE %' 
      AND (trim(text) NOT LIKE '/*%' AND trim(text) NOT LIKE '--%'));
  END LOOP;
END;
```

**Script para `SP_OBJECT_REFERENTIAL_ANALYSIS`**

```
CREATE OR REPLACE PROCEDURE SP_OBJECT_REFERENTIAL_ANALYSIS IS
BEGIN
  EXECUTE IMMEDIATE 'TRUNCATE TABLE REFERENTIAL_ANALYSIS_BASELINE';
  INSERT INTO REFERENTIAL_ANALYSIS_BASELINE
  WITH rel AS (
    SELECT DISTINCT c.owner, c.table_name, c.r_owner r_owner,
      (SELECT table_name FROM dba_constraints 
       WHERE constraint_name = c.r_constraint_name 
       AND owner = c.r_owner) r_table_name 
    FROM dba_constraints c 
    WHERE constraint_type = 'R' 
    AND c.owner NOT IN (SELECT username FROM dba_users WHERE oracle_maintained = 'Y')
    AND c.r_owner NOT IN (SELECT username FROM dba_users WHERE oracle_maintained = 'Y')),
  tab_list AS (
    SELECT OWNER, object_name 
    FROM DEPENDENT_ANALYSIS_BASELINE 
    WHERE UPPER(OBJECT_TYPE) = 'TABLE')
  SELECT DISTINCT owner child_owner, table_name child, r_owner parent_owner,
    r_table_name parent, SYS_CONNECT_BY_PATH(r_table_name, ' -> ') || ' -> ' || table_name PATH
  FROM rel 
  START WITH (r_owner, r_table_name) IN (SELECT * FROM tab_list)
  CONNECT BY NOCYCLE (r_owner, r_table_name) = ((PRIOR owner, PRIOR table_name))
  UNION
  SELECT DISTINCT owner child_owner, table_name child, r_owner parent_owner,
    r_table_name parent, SYS_CONNECT_BY_PATH(table_name, ' -> ') || ' -> ' || r_table_name PATH
  FROM rel 
  START WITH (owner, table_name) IN (SELECT * FROM tab_list)
  CONNECT BY NOCYCLE (owner, table_name) = ((PRIOR r_owner, PRIOR r_table_name));
END;
```

**Script para `SP_OBJECT_PRIVS_ANALYSIS`**

```
CREATE OR REPLACE PROCEDURE SP_OBJECT_PRIVS_ANALYSIS IS
  V_SQL VARCHAR2(4000);
  V_CNT NUMBER;
BEGIN
  V_SQL := 'TRUNCATE TABLE OBJECT_PRIVS_ANALYSIS';
  EXECUTE IMMEDIATE V_SQL;
  FOR I IN (SELECT OWNER, OBJECT_NAME FROM DEPENDENT_ANALYSIS_BASELINE WHERE OBJECT_TYPE = 'TABLE')
  LOOP
    INSERT INTO OBJECT_PRIVS_ANALYSIS(OWNER, OBJECT_NAME, USER_NAME, PRIVS)
    WITH obj_to_role AS (
      SELECT DISTINCT GRANTEE role_name, 
        DECODE(privilege, 'SELECT', 'READ', 'REFERENCE', 'READ', 'INSERT', 'WRITE', 
               'UPDATE', 'WRITE', 'DELETE', 'WRITE', privilege) privs
      FROM DBA_TAB_PRIVS t, DBA_ROLES r 
      WHERE OWNER = I.OWNER 
      AND TYPE = 'TABLE' 
      AND TABLE_NAME = I.OBJECT_NAME 
      AND t.GRANTEE = r.ROLE 
      AND r.ROLE IN (SELECT ROLE FROM DBA_ROLES WHERE ORACLE_MAINTAINED = 'N')
    )
    SELECT I.OWNER, I.OBJECT_NAME, grantee, privs 
    FROM (
      -- Recursively Role to User mapping with privilege
      SELECT DISTINCT grantee, privs 
      FROM (SELECT rp.granted_role, rp.grantee, privs,
        (SELECT DECODE(COUNT(*), 0, 'ROLE', 'USER') 
         FROM (SELECT 'User' FROM DBA_users WHERE username = rp.GRANTEE)) grantee_type 
        FROM DBA_role_privs rp, obj_to_role r 
        WHERE rp.granted_role = r.role_name 
        AND grantee IN ((SELECT USERNAME FROM DBA_USERS WHERE ORACLE_MAINTAINED = 'N') 
                       UNION (SELECT ROLE FROM DBA_ROLES WHERE ORACLE_MAINTAINED = 'N'))
        AND granted_role IN (SELECT ROLE FROM DBA_ROLES WHERE ORACLE_MAINTAINED = 'N') 
        START WITH granted_role IN (SELECT DISTINCT role_name FROM obj_to_role) 
        CONNECT BY granted_role = PRIOR grantee) 
      WHERE grantee_type = 'USER'
    )
    UNION
    (
      -- Direct Object grants to User
      SELECT I.OWNER, I.OBJECT_NAME, GRANTEE, 
        DECODE(privilege, 'SELECT', 'READ', 'REFERENCE', 'READ', 'INSERT', 'WRITE',
               'UPDATE', 'WRITE', 'DELETE', 'WRITE', privilege) privs 
      FROM DBA_TAB_PRIVS, DBA_USERS 
      WHERE GRANTEE = USERNAME 
      AND OWNER = I.OWNER 
      AND TYPE = 'TABLE' 
      AND TABLE_NAME = I.OBJECT_NAME
    ) 
    ORDER BY 2 DESC;
  END LOOP;
END;
```

**Procedimientos para `SP_OBJECT_DEPENDENCY_ANALYSIS`**

```
CREATE OR REPLACE PROCEDURE SP_OBJECT_DEPENDENCY_ANALYSIS (v_level NUMBER) IS
  TYPE typ IS RECORD (
    schema VARCHAR2(100),
    obj_type VARCHAR2(100),
    obj_name VARCHAR2(100),
    path VARCHAR2(5000)
  );
  TYPE array IS TABLE OF typ;
  l_data array;
  c SYS_REFCURSOR;
  l_errors NUMBER;
  l_errno NUMBER;
  l_msg VARCHAR2(4000);
  l_idx NUMBER;
  l_level NUMBER;
BEGIN
  l_level := v_level + 1;
  OPEN c FOR 
    WITH obj_list AS (
      SELECT owner schema_name, object_type, object_name 
      FROM DEPENDENT_ANALYSIS_BASELINE 
      WHERE depedncy_level = v_level
    ),
    fw_dep_objects AS (
      SELECT level lvl, owner, name, type, referenced_owner, referenced_name,
        referenced_type, SYS_CONNECT_BY_PATH(name, ' -> ') || ' -> ' || referenced_name PATH 
      FROM dba_dependencies
      START WITH (owner, CASE WHEN type = 'PACKAGE BODY' THEN 'PACKAGE' ELSE type END, name) 
        IN (SELECT schema_name, object_type, object_name FROM obj_list)
      CONNECT BY NOCYCLE (owner, type, name) = 
        ((PRIOR referenced_owner, PRIOR referenced_type, PRIOR referenced_name))
    ),
    bw_dep_objects AS (
      SELECT level lvl, owner, name, type, referenced_owner, referenced_name,
        referenced_type, SYS_CONNECT_BY_PATH(name, ' <- ') || ' <- ' || referenced_name PATH 
      FROM dba_dependencies
      START WITH (referenced_owner, CASE WHEN referenced_type = 'PACKAGE BODY' THEN 'PACKAGE' 
        ELSE referenced_type END, referenced_name) IN (SELECT schema_name, object_type, object_name FROM obj_list)
      CONNECT BY NOCYCLE (referenced_owner, referenced_type, referenced_name) = 
        ((PRIOR owner, PRIOR type, PRIOR name))
    )
    SELECT * FROM (
      (SELECT DISTINCT referenced_owner schema, referenced_type obj_type, 
        referenced_name obj_name, path FROM fw_dep_objects)
      UNION
      (SELECT DISTINCT owner schema, type obj_type, name obj_name, path 
       FROM bw_dep_objects)
    )
    WHERE schema IN (SELECT username FROM all_users WHERE oracle_maintained = 'N')
    ORDER BY obj_type;

  LOOP
    FETCH c BULK COLLECT INTO l_data LIMIT 100;
    BEGIN
      FORALL i IN 1..l_data.count SAVE EXCEPTIONS
        INSERT INTO DEPENDENT_ANALYSIS_BASELINE (
          owner, object_name, object_type, catagory, depedncy_level, project_need, comments
        ) 
        VALUES (
          l_data(i).schema, 
          l_data(i).obj_name,
          CASE WHEN l_data(i).obj_type = 'PACKAGE BODY' THEN 'PACKAGE' ELSE l_data(i).obj_type END,
          'level ' || l_level || ' dependency',
          l_level,
          '',
          'from dependency proc' || l_data(i).path
        );
    EXCEPTION
      WHEN OTHERS THEN
        l_errors := sql%bulk_exceptions.count;
        FOR i IN 1..l_errors LOOP
          l_errno := sql%bulk_exceptions(i).error_code;
          l_msg := SQLERRM(-l_errno);
          l_idx := sql%bulk_exceptions(i).error_index;
          UPDATE DEPENDENT_ANALYSIS_BASELINE 
          SET catagory1 = catagory1 || ', found in level' || l_level || ' dependent of ' || l_data(l_idx).path,
              comments1 = comments1 || ', from dependency proc exception ' || l_data(i).path
          WHERE owner = l_data(l_idx).schema 
          AND object_name = l_data(l_idx).obj_name 
          AND object_type = l_data(l_idx).obj_type;
        END LOOP;
    END;
    EXIT WHEN c%NOTFOUND;
  END LOOP;
  CLOSE c;
END;
```