martes, 21 de julio de 2026

Un verdadero clásico: conocías el paquete DBMS_DB_VERSION? Compilación condicional en la base de datos Oracle


Es un paquete pequeño, pero contiene más elementos de los que normalmente se utilizan. Su propósito principal es permitir que el código PL/SQL sea compatible entre distintas versiones de Oracle mediante compilación condicional y comprobaciones de versión.

Este paquete expone constantes que permiten identificar la versión mayor, la release y comprobar características específicas de la base de datos.

Una de las razones por las que existe este paquete es permitir escribir código compatible con varias versiones de Oracle.

El paquete principalmente es llamado a través de sus constantes VERSION y RELEASE, no expone procedimientos, funciones ni parámetros, sino únicamente constantes públicas como:

Además de VERSION y RELEASE, el paquete define constantes booleanas que permiten saber si el código se está compilando para una determinada versión.

En la versión de base de datos Oracle AI Database 26ai, podemos observar en el código, como las constantes para las versiones de base de datos 11.1 hacia atrás han sido marcadas como obsoletas.

Las constantes VER_LEx, son especialmente útiles cuando el objetivo es mantener compatibilidad hacia atrás.

      LINE TEXT
---------- ------------------------------------------------------------------------------
         1 package dbms_db_version is
         2   version constant pls_integer :=
         3           23; -- RDBMS version number
         4   release constant pls_integer := 0;  -- RDBMS release number
         5
         6   /* The following boolean constants follow a naming convention. Each
         7      constant gives a name for a boolean expression. For example,
         8      ver_le_9_1  represents version <=  9 and release <= 1
         9      ver_le_10_2 represents version <= 10 and release <= 2
        10      ver_le_10   represents version <= 10
        11
        12      Code that references these boolean constants (rather than directly
        13      referencing version and release) will benefit from fine grain
        14      invalidation as the version and release values change.
        15
        16      A typical usage of these boolean constants is
        17
        18          $if dbms_db_version.ver_le_10 $then
        19            version 10 and ealier code
        20          $elsif dbms_db_version.ver_le_11 $then
        21            version 11 code
        22          $else
        23            version 12 and later code
        24          $end
        25
        26      This code structure will protect any reference to the code
        27      for version 12. It also prevents the controlling package
        28      constant dbms_db_version.ver_le_11 from being referenced
        29      when the program is compiled under version 10. A similar
        30      observation applies to version 11. This scheme works even
        31      though the static constant ver_le_11 is not defined in
        32      version 10 database because conditional compilation protects
        33      the $elsif from evaluation if the dbms_db_version.ver_le_10 is
        34      TRUE.
        35   */
        36
        37   /* Deprecate boolean constants for unsupported releases */
        38
        39   ver_le_9_1    constant boolean := FALSE;
        40   PRAGMA DEPRECATE(ver_le_9_1);
        41   ver_le_9_2    constant boolean := FALSE;
        42   PRAGMA DEPRECATE(ver_le_9_2);
        43   ver_le_9      constant boolean := FALSE;
        44   PRAGMA DEPRECATE(ver_le_9);
        45   ver_le_10_1   constant boolean := FALSE;
        46   PRAGMA DEPRECATE(ver_le_10_1);
        47   ver_le_10_2   constant boolean := FALSE;
        48   PRAGMA DEPRECATE(ver_le_10_2);
        49   ver_le_10     constant boolean := FALSE;
        50   PRAGMA DEPRECATE(ver_le_10);
        51   ver_le_11_1   constant boolean := FALSE;
        52   PRAGMA DEPRECATE(ver_le_11_1);
        53   ver_le_11_2   constant boolean := FALSE;
        54   ver_le_11     constant boolean := FALSE;
        55   ver_le_12_1   constant boolean := FALSE;
        56   ver_le_12_2   constant boolean := FALSE;
        57   ver_le_12     constant boolean := FALSE;
        58   ver_le_18     constant boolean := FALSE;
        59   ver_le_19     constant boolean := FALSE;
        60   ver_le_20     constant boolean := FALSE;
        61   ver_le_21     constant boolean := FALSE;
        62   ver_le_23     constant boolean := TRUE;
        63
        64 end dbms_db_version;

64 rows selected.

SQL>

Esta es una funcionalidad poco conocida pero que tiene más de 20 años de existencia.



[oracle@oracle-server-26ai ~]$ sqlplus /nolog

SQL*Plus: Release 23.26.2.0.0 - Production on Wed Jul 22 00:12:04 2026
Version 23.26.2.0.0

Copyright (c) 1982, 2026, Oracle.  All rights reserved.

SQL> connect / as sysdba
Connected.
SQL> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1                           READ WRITE NO
SQL> alter session set container=pdb1;

Session altered.

PL/SQL procedure successfully completed.

El paquete no devuelve la versión completa de la base de datos, sólo la versión base
SQL> set serveroutput on SQL> begin dbms_output.put_line( dbms_db_version.version ); end; / 23 PL/SQL procedure successfully completed.

Podemos generar una versión de la consulta ampliada para poder mostrar un poco más de
información utilizando la constante RELEASE
SQL> set serveroutput on declare begin dbms_output.put_line('Oracle Version'); dbms_output.put_line('--------------'); dbms_output.put_line( dbms_db_version.version || '.' || dbms_db_version.release ); dbms_output.put_line('Compatible: ' || dbms_db_version.version || '.' || dbms_db_version.release ); end; / Oracle Version -------------- 23.0 Compatible: 23.0
PL/SQL procedure successfully completed.

Compilación condicional
Oracle permite usar directivas especiales:
  • $IF
  • $THEN
  • $ELSE
  • $END

Estas son evaluadas por el compilador.

Por ejemplo:
SQL> DECLARE x number; BEGIN $IF DBMS_DB_VERSION.VERSION >= 23 $THEN x := 10; $ELSE x := 20; $END DBMS_OUTPUT.PUT_LINE('Valor de x: '||x); END; / Valor de x: 10 PL/SQL procedure successfully completed.

En este caso el compilador dependiendo la versión en la cuál compiles el programa,
así será la generación del código que utilice. por eso en la ejecución del procedimiento
en la versión 26.23.2 devuelve el valor 10
Pero si ejecutamos el mismo código en una versión 19c, el resultado será 20.
SQL> select BANNER_FULL from v$version; BANNER_FULL -------------------------------------------------------------------------------- Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production Version 19.22.0.0.0 SQL> DECLARE x number; BEGIN $IF DBMS_DB_VERSION.VERSION >= 23 $THEN x := 10; $ELSE x := 20; $END DBMS_OUTPUT.PUT_LINE('Valor de x: '||x); END; / Valor de x: 20 PL/SQL procedure successfully completed. Todo esto esta muy bien, pero el procedimiento va más allá.
Podemos utilizar las constantes boleanas, para determinar cuál es la versión de la
base de datos de tal forma que basado en el valor obtenido, ejecute la parte del código
que corresponda.
Ejecutando en Oracle AI Database 26ai.
SQL> DECLARE x NUMBER; BEGIN IF DBMS_DB_VERSION.VER_LE_19 THEN x := 10; ELSIF DBMS_DB_VERSION.VER_LE_23 THEN x := 13; ELSE x := 20; END IF; DBMS_OUTPUT.PUT_LINE('Valor de x: ' || x); END; / Valor de x: 13 PL/SQL procedure successfully completed.

Ahora veamos un ejemplo de ejecución sobre una acción real a nivel de la base de datos.
Necesitamos crear un procedimiento que sea capaz de borrar una tabla, pero que dependiendo
de la versión que sea, utilice el método con el condicionamiento IF EXISTS o simplemente un
DROP TABLE, al no existe la validación, como en versiones 19c o previas. #### Oracle 19c SQL> create table t1( x number, y char); Table created. SQL> desc t1 Name Null? Type ---------------------- -------- ---------- X NUMBER Y CHAR(1)
Al ejecutar el procedimiento con el IF EXISTS en 19c, provoca que el procedimiento genere
un error de ejecución ORA-00933
SQL> set serveroutput on SQL> DECLARE BEGIN EXECUTE IMMEDIATE 'DROP TABLE IF EXISTS t1'; DBMS_OUTPUT.PUT_LINE('Tabla T1 eliminada (si existía).'); EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLERRM); END; / ORA-00933: SQL command not properly ended PL/SQL procedure successfully completed.
Ahora hagamos el mismo proceso, pero con la sintáxis aceptada en 19c.
SQL> DECLARE BEGIN EXECUTE IMMEDIATE 'DROP TABLE t1'; DBMS_OUTPUT.PUT_LINE('Tabla T1 eliminada.'); EXCEPTION WHEN OTHERS THEN IF SQLCODE = -942 THEN DBMS_OUTPUT.PUT_LINE('La tabla T1 no existe.'); ELSE RAISE; END IF; END; / Tabla T1 eliminada. PL/SQL procedure successfully completed. SQL> create table t1( x number, y char); Table created.

Ahora agreguemos la opción de compilación condicional al script, para que no nos genere error
en la versión 19c. DECLARE BEGIN $IF DBMS_DB_VERSION.VERSION >= 23 $THEN EXECUTE IMMEDIATE 'DROP TABLE IF EXISTS t1'; $ELSE BEGIN EXECUTE IMMEDIATE 'DROP TABLE t1'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END; $END DBMS_OUTPUT.PUT_LINE('Proceso finalizado.'); END; / Proceso finalizado. PL/SQL procedure successfully completed. SQL> desc t1 ERROR: ORA-04043: object t1 does not exist SQL>

En la version de Oracle AI Database 26ai, ambas instrucciones serán ejecutadas sin presentar
ningún error, ya que ambas son válidas. ##Oracle 26ai SQL> create table t1( x number, y char); Table created. SQL> DECLARE BEGIN EXECUTE IMMEDIATE 'DROP TABLE t1'; DBMS_OUTPUT.PUT_LINE('Tabla T1 eliminada.'); EXCEPTION WHEN OTHERS THEN IF SQLCODE = -942 THEN DBMS_OUTPUT.PUT_LINE('La tabla T1 no existe.'); ELSE RAISE; END IF; END; / Tabla T1 eliminada. PL/SQL procedure successfully completed.
¿Por qué Oracle diseñó este mecanismo?

Esto lo hizo pensado principalmente para fabricantes de software (ISVs) y equipos que mantienen una misma base de código para múltiples versiones de Oracle. Por ejemplo, un producto que debe soportar Oracle 19c, 21c y 23ai puede distribuir el mismo código fuente y dejar que cada base de datos compile automáticamente la variante adecuada.


El dato histórico interesante:

Aunque el paquete existe desde 9.2, su verdadero protagonismo llegó con PL/SQL Conditional Compilation, introducida en Oracle Database 10g Release 2 (10.2). Fue entonces cuando Oracle empezó a recomendar expresiones como:
$IF DBMS_DB_VERSION.VER_LE_10 $THEN
   -- Código para Oracle 10g o anterior
$ELSIF DBMS_DB_VERSION.VER_LE_11 $THEN
   -- Código para Oracle 11g
$ELSE
   -- Código para versiones posteriores
$END

Antes de 10.2 el paquete ya existía, pero no podía aprovecharse con este mecanismo de compilación condicional, que es precisamente el uso para el que fue diseñado

Esta característica recién ha cumplido 24 años de existencia. La conocías.?


lunes, 13 de julio de 2026

Cuando te contratan para perder.

 

Hace unos días empecé a ver Ted Lasso. Curiosamente, después de una copa de vino, la serie me llevó a pensar en algo que he visto repetirse durante más de treinta años trabajando en tecnología.

No hablo de servidores, bases de datos o proyectos.

Hablo de personas.

Con el tiempo uno descubre que no todas las contrataciones persiguen el mismo objetivo.

Hay empresas que contratan para construir.
Otras contratan para transformar.
Pero también existen aquellas que contratan para encontrar a quién cargarle el peso de un fracaso que comenzó mucho antes de que esa persona llegara.

Es una realidad incómoda.
Muchas veces creemos que nos buscan porque somos la mejor opción para resolver un problema complejo. Nos ilusiona pensar que podremos aportar nuestra experiencia, ordenar el caos y dejar una organización mejor de como la encontramos.

Sin embargo, en algunas ocasiones el desenlace ya estaba decidido.
El presupuesto ya estaba agotado.
La confianza ya estaba rota.
La dirección ya había perdido el rumbo.
Las decisiones importantes ya habían sido tomadas.

Y la contratación no era el comienzo de un rescate, sino el último acto antes del cierre del telón.

No eres el capitán del barco.

Eres el nombre que aparecerá en el informe final.

Durante mi carrera he visto excelentes profesionales fracasar en lugares donde, simplemente, nadie podía tener éxito.

Personas inteligentes, responsables y comprometidas que terminaron cuestionando su capacidad, cuando en realidad habían sido colocadas en un escenario donde el resultado estaba condicionado desde el primer día.

En informática esto ocurre con frecuencia.

Llega un nuevo gerente de TI cuando la empresa lleva años sin invertir.
Contratan un DBA cuando las políticas de respaldo nunca existieron.
Incorporan un arquitecto cuando el sistema acumula veinte años de deuda técnica.

Nombran un líder cuando el equipo perdió la confianza hace mucho tiempo.

Después, cuando nada cambia en unos cuantos meses, la conclusión parece sencilla:
"El nuevo tampoco funcionó."
Pero la pregunta correcta casi nunca es esa.

La pregunta debería ser:
¿Realmente alguien quería que funcionara?

Con los años también entendí que este fenómeno no pertenece únicamente al mundo laboral.
Sucede en las relaciones personales.

Hay personas que llegan intentando reconstruir vínculos que llevan años deteriorándose.
Intentan comunicarse.
Intentan comprender.
Intentan cambiar.

Mientras la otra parte ya tomó la decisión de irse hace mucho tiempo.

No buscan reconstruir.
Buscan confirmar que ya no había nada por hacer.

Y entonces aparece alguien dispuesto a luchar una batalla que ya había terminado antes de comenzar.

La experiencia, sin embargo, también enseña algo esperanzador.

No todos los desafíos imposibles son una trampa.
Algunas de las mejores historias profesionales nacen precisamente de proyectos que parecían condenados.
La diferencia suele estar en algo que pocas veces aparece en una descripción de puesto.

La voluntad real de cambiar.
Cuando existe esa voluntad, incluso con pocos recursos, los equipos encuentran caminos.

Cuando no existe, ni el mejor profesional del mundo puede fabricar compromiso donde nadie quiere asumirlo.

Si pudiera dar un consejo a quienes apenas comienzan su carrera, no sería "acepten todos los retos".

Sería algo distinto.
Aprendan primero a identificar la naturaleza del desafío.
Antes de aceptar un puesto, hagan preguntas difíciles:
  • ¿Por qué se fue la persona anterior?
  • ¿Cuántas personas han ocupado ese cargo en los últimos años?
  • ¿Qué decisiones puede tomar realmente quien ocupe el puesto?
  • ¿Existe presupuesto para cambiar las cosas?
  • ¿La dirección está dispuesta a modificar aquello que provocó el problema?
Las respuestas a esas preguntas suelen revelar mucho más que una entrevista de una hora.

El talento es importante.

Pero también lo es elegir las batallas correctas.

Y para quienes ya acumulamos algunas décadas de experiencia, el aprendizaje quizá sea diferente.

No debemos confundir resiliencia con sacrificio infinito.

No tenemos que demostrar constantemente que podemos salvar cualquier proyecto.

Hay momentos en que la mayor muestra de madurez profesional consiste en reconocer que una organización no busca soluciones.

Busca un responsable.

Y son dos cosas completamente distintas.

Decir "no" también forma parte de la experiencia.

Renunciar antes de convertirnos en el chivo expiatorio de decisiones ajenas también es una forma de proteger nuestra integridad profesional.

No porque tengamos miedo de fracasar.

Sino porque entendimos que no todo fracaso nos pertenece.

Después de tantos años sigo creyendo que las personas pueden cambiar organizaciones.

He visto equipos extraordinarios lograr cosas que parecían imposibles.

Pero también aprendí que ninguna persona, por brillante que sea, puede reemplazar la voluntad colectiva de hacer que las cosas no funcionen.

Hay barcos que todavía pueden repararse.
Y hay barcos que solo buscan un nuevo capitán para explicar por qué terminaron hundiéndose.

La experiencia consiste, precisamente, en aprender a distinguir unos de otros.

Porque, al final, el verdadero éxito profesional no siempre consiste en salvar todos los barcos.

A veces consiste en reconocer cuáles todavía quieren navegar.

No me hagan mucho caso, yo ya estoy un poco viejo y creo que la entrada en años me empieza a pasar factura.

Solo tengan presente una cosa, la vida es corta, mucho más de lo que crees cuando tienes 20, 30 o 40 años. Despues de los 50's, sólo piensas en una cosa: Como tener paz y como vivir bien.

Algunos dirán que con 58 años, aún me queda mucho por hacer. Y si, tienen mucha razón, pero mis prioridades actuales, son muy distintas a las de hace 20 años atrás.

En ocasiones, desearía correr detrás de un balón, saltar y golpearlo como lo hacía cuando tenía 20. Pero la realidad es otra. Mi cuerpo me lo recuerda cada mañana al levantarme y cada día al final. Ya no soy lo mismo y nunca más lo volveré a hacer. Esa es la realidad.

Lo único que nunca deberíamos permitirnos es cargar con la culpa de un fracaso cuya causa nunca estuvo en nuestras manos.

domingo, 12 de julio de 2026

Oracle Database 26ai "Autómata Independiente - Fleet Patching and Provisioning"- internamente conocido como FPP en modo local para actualizar a 23.26.2

 

El Independent Automaton de Fleet Patching and Provisioning, también descrito por Oracle internamente en el contexto de FPP Local Mode, es una herramienta diseñada para realizar out-of-place patching entre dos ORACLE_HOME instalados en el mismo servidor, incluso cuando:

  • no existe Oracle Grid Infrastructure;
  • no existe Oracle Restart;
  • no existe un servidor FPP central;
  • los Oracle Homes no son working copies;
  • los Homes fueron instalados mediante runInstaller u otro método convencional.
Oracle permite a través de esta opción que 2 ORACLE_HOME instalados y no administrados por un servidor FPP, pueden ser migrados o actualizados.

La versión disponible del FPP en modo local solo dispone de un comando:
  • commands: move
  • objects: database

No contiene las funciones completas de un servidor o cliente FPP, como:

  • rhpctl add image
  • rhpctl add workingcopy
  • rhpctl query job
  • rhpctl deploy home
  • rhpctl move gihome
  • rhpctl upgrade database
  • rhpctl -version

y esto no esta mal, ya que es el comportamiento esperado en la versión autónoma.

La herramienta identifica las bases asociadas al Home origen y las cambia al Home destino.

La sintaxis básica documentada es:

rhpctl move database \
  -sourcehome /ruta/home_origen \
  -desthome /ruta/home_destino

Mi escenario para este laboratorio es el siguiente:

FPP integra la ejecución de datapatch como parte del movimiento, salvo que se le indique explícitamente no hacerlo.

Un punto importante mientras no se elimine ni modifique el Home de origen, puede realizarse el movimiento inverso.

No obstante, debe distinguir entre:
  • retornar los binarios al Home anterior;
  • revertir los cambios SQL aplicados por datapatch.
Después de que el RU nuevo haya modificado el diccionario, el retorno no debe tratarse simplemente como un cambio de variables. El procedimiento debe considerar el rollback SQL del RU, la compatibilidad y las opciones que soporte rhpctl. Por ello, el Home anterior debe conservarse hasta completar todas las validaciones funcionales.

A continuación podrás ver el procedimiento completo para realizar el cambio o actualización que describí previamente.

[oracle@oracle-server-26ai bin]$ ./rhpctl move database -h

Moves a database from source Oracle home to the patched Oracle home.

Usage: 	rhpctl move database -sid  -sourcehome  
		-desthome 
        
        [-eval]
        [-ignorewcpatches]
        [-stopoption ]
        [-drain_timeout 

sábado, 11 de julio de 2026

Neutralidad en Seguridad, algo de lo que no hablamos mucho.


En mis charlas sobre "Ethical Hacking", me encanta hablar sobre “Neutralidad en Seguridad”. Me gusta ampliar el principio clásico de mínimo privilegio desde una perspectiva puramente técnica, hacia una dimensión de gobernanza, responsabilidad y protección del propio usuario.

El principio tradicional de mínimo privilegio (Principle of Least Privilege, PoLP) establece:
Una identidad (usuario, aplicación, proceso o servicio) debe recibir únicamente los privilegios estrictamente necesarios para ejecutar sus funciones autorizadas, durante el tiempo necesario y bajo las condiciones necesarias.

Sin embargo mi concepto agrega una capa adicional: un exceso de privilegios no solo incrementa el riesgo para la organización, sino que rompe la neutralidad del usuario frente a un incidente.

Para mi: Neutralidad en seguridad: es el principio mediante el cual los privilegios asignados a una identidad digital deben mantenerse alineados estrictamente con sus responsabilidades reales, evitando que permisos innecesarios conviertan a dicha identidad en un punto injustificado de exposición, sospecha o atribución dentro de un incidente de seguridad.

El punto central desde mi perspectiva, es que la neutralidad ocurre cuando, los Permisos concedidos = Responsabilidad funcional. Ni menos, ni más.

Desde el punto de vista de seguridad, un exceso de privilegios genera varios problemas:

1. Incrementa la superficie de ataque
Ejemplo en base de datos:

Un analista requiere: 
  • SELECT sobre tablas de reportes, 
  • pero recibe: SELECT ANY TABLE o peor aún "DBA".
El atacante hereda todo lo que la cuenta puede hacer, no solamente lo que el usuario normalmente hace.

2. Rompe la presunción operacional de inocencia.
Supongamos:
  • Usuario A: necesita consultar clientes, tiene acceso solo lectura
  • Usuario B: necesita consultar clientes, pero además tiene: borrar registros, exportar información, crear usuarios, modificar privilegios.

Si ocurre una fuga, desde un análisis forense el Usuario A: 
  • ¿Tenía capacidad técnica? 
  • Respuesta: NO, menor probabilidad como origen.
En el caso del Usuario B:
  • ¿Tenía capacidad técnica? 
  • Respuesta: SÍ, debe investigarse. Aunque Usuario B sea inocente, los privilegios excesivos lo colocaron dentro del perímetro de sospecha.
3. La asignación excesiva de permisos crea responsabilidad compartida

Una frase que resume bien el concepto:

"Todo privilegio innecesario es una responsabilidad innecesaria transferida al usuario."

Dar privilegios no es solamente dar capacidad, también es asignar: 
  1. riesgo,
  2. trazabilidad, 
  3. obligación de custodia, 
  4. exposición ante auditoría.
Ustedes que opinan.?

jueves, 9 de julio de 2026

Oracle Enterprise Edition "** MISSING: 6604574/6604574" en nombre de objeto en estadísticas del AWR.

Tenemos un script para validar el crecimiento de objetos dentro de la base de datos en los últimos 6 días y cuando ejecutamos la consulta nos devuelve la siguiente salida.

OWNER                     SEGMENT_NAME                        SEGMENT_TYPE               GROWTH_MB------------------------- ----------------------------------- -------------------- ---------------
INTEGRACION               LOG                                 TABLE                       1,714.20
FCUBSLIVE                 GWTB_MSG_OUT_LOG                    TABLE                       1,258.01
INTEGRACION               SYS_LOB0000989853C00006$$           LOB                           832.05
FCUBSLIVE                 GWTB_MSG_IN_LOG                     TABLE                         750.85
INTEGRACION               TRACK_ENTRY_REQUEST_INTEGRACION_HIS TABLE                         733.66
                          TORY

** MISSING **             ** MISSING: 6604574/6604574         UNDEFINED                     684.62
** MISSING **             ** MISSING: 6618097/6618097         UNDEFINED                     684.56
** MISSING **             ** MISSING: 6611295/6611295         UNDEFINED                     681.16
** MISSING **             ** MISSING: 6624752/6624752         UNDEFINED                     615.75
** MISSING **             ** MISSING: 6631455/6631455         UNDEFINED                     613.48
** MISSING **             ** MISSING: 6597842/6597842         UNDEFINED                     612.60
FCUBSLIVE                 PK01_ICTB_DEPOSIT_EXECUTION_LO      INDEX                         355.53
** MISSING **             ** MISSING: 6591054/6591054         UNDEFINED                     342.05
FCUBSLIVE                 IDX_CLTP_AMT_DUE                    INDEX                         304.77
FCUBSLIVE                 PK_CLTP_ACCOUNT_COMP_CALC           INDEX                         286.41
FCUBSLIVE                 PK01_STTB_FIELD_LOG                 INDEX                         273.21
SYS                       I_SCHEDULER_JOB_RUN_DETAILS         INDEX                         261.97
** MISSING **             ** MISSING: 6576422/6576422         UNDEFINED                     256.87
** MISSING **             ** MISSING: 6584108/6584108         UNDEFINED                     243.75
SYS                       SCHEDULER$_INSTANCE_PK              INDEX                         234.50

20 rows selected.
El script en cuestión es el siguiente, asociado a una vista para poderlo ejecutar más fácil:
create view v_tablas_mas_crecimiento as
WITH snaps AS (
    SELECT
        MIN(snap_id) AS begin_snap,
        MAX(snap_id) AS end_snap
    FROM dba_hist_snapshot
    WHERE begin_interval_time >= SYSDATE - 6
),
seg_begin AS (
    SELECT
        obj.owner,
        obj.object_name AS segment_name,
        obj.object_type AS segment_type,
        ss.space_used_delta
    FROM dba_hist_seg_stat ss
    JOIN dba_hist_seg_stat_obj obj
      ON obj.dbid = ss.dbid
     AND obj.obj# = ss.obj#
     AND obj.dataobj# = ss.dataobj#
    JOIN snaps sn
      ON ss.snap_id = sn.begin_snap
),
seg_growth AS (
    SELECT
        obj.owner,
        obj.object_name AS segment_name,
        obj.object_type AS segment_type,
        SUM(ss.space_used_delta) AS growth_bytes
    FROM dba_hist_seg_stat ss
    JOIN dba_hist_seg_stat_obj obj
      ON obj.dbid = ss.dbid
     AND obj.obj# = ss.obj#
     AND obj.dataobj# = ss.dataobj#
    WHERE ss.snap_id BETWEEN (SELECT begin_snap FROM snaps)
                         AND (SELECT end_snap FROM snaps)
    GROUP BY obj.owner, obj.object_name, obj.object_type
)
SELECT *
FROM (
    SELECT
        owner,
        segment_name,
        segment_type,
        ROUND(growth_bytes/1024/1024,2) AS growth_mb
    FROM seg_growth
    WHERE growth_bytes > 0
    ORDER BY growth_bytes DESC
)
WHERE ROWNUM <= 20;

select * from  "V_TABLAS_MAS_CRECIMIENTO";

Qué son estos datos en el nombre del objeto  ** MISSING: 6591054/6591054 ?

Vamos a explicarlo.

En Oracle, cuando estás consultando crecimiento histórico (normalmente desde AWR usando DBA_HIST_SEG_STAT y DBA_HIST_SEG_STAT_OBJ) y en lugar del nombre del objeto aparece algo como:
** MISSING: 6624752/6624752

significa que AWR tiene estadísticas históricas de un segmento, pero ya no puede resolver el objeto asociado en el diccionario histórico de objetos.

El formato corresponde normalmente a:

** MISSING: <OBJ#>/<DATAOBJ#>

En tu caso:

** MISSING: 6624752/6624752
| |
| +-- DATA_OBJECT_ID
|
+---------- OBJECT_ID

¿Por qué ocurre?

Oracle guarda actividad de segmentos en:

DBA_HIST_SEG_STAT
|
| OBJ#
| DATAOBJ#
v
DBA_HIST_SEG_STAT_OBJ

Cuando intenta hacer el JOIN:

ss.obj# = obj.obj#
and ss.dataobj# = obj.dataobj#

y no encuentra la descripción histórica del objeto, muestra:

** MISSING

Causas más comunes

1. La tabla fue eliminada

Ejemplo:

CREATE TABLE TEMP_CARGA AS SELECT ...
DROP TABLE TEMP_CARGA PURGE;

El AWR todavía conserva:

  • lecturas
  • escrituras
  • crecimiento
  • bloques modificados

pero el objeto ya no existe.


2. TRUNCATE de la tabla

Muy frecuente.

Al hacer:

TRUNCATE TABLE FACTURAS;

Oracle cambia:

DATA_OBJECT_ID

Ejemplo:

Antes:

OBJECT_ID       6624752
DATA_OBJECT_ID 6624752

Después:

OBJECT_ID       6624752
DATA_OBJECT_ID 6639011

Para AWR parece otro segmento.


3. Operaciones MOVE / REBUILD

Ejemplos:

ALTER TABLE CLIENTES MOVE;

ALTER INDEX IDX_CLIENTES REBUILD;

Generan nuevo DATAOBJ#.


4. Objetos temporales creados por procesos

Muy común con:

  • Data Pump
  • Cargas masivas
  • ETL
  • cargas batch
  • APEX
  • reportes
  • particiones temporales

Cómo intentar identificarlo

Primero buscar si todavía existe:

SELECT owner,
object_name,
object_type,
object_id,
data_object_id,
created,
last_ddl_time
FROM dba_objects
WHERE object_id = 6624752
OR data_object_id = 6624752;

Si no devuelve nada, ya desapareció.


Buscarlo en recycle bin:

SELECT owner,
original_name,
object_name,
type,
droptime
FROM dba_recyclebin
WHERE base_object = 6624752;

También puedes revisar si AWR todavía tiene rastros:

SELECT *
FROM dba_hist_seg_stat_obj
WHERE obj# = 6624752
OR dataobj# = 6624752;

No necesariamente significa corrupción ni problema de diccionario.

Todos los Sábados a las 8:00PM