Mostrando las entradas con la etiqueta PL/SQL. Mostrar todas las entradas
Mostrando las entradas con la etiqueta PL/SQL. Mostrar todas las entradas

martes, 2 de mayo de 2023

Oracle 23c - Mejoras a Sentencias y Expresiones CASE en PL/SQL


Oracle Database 23c introduce algunas mejoras a las sentencias y expresiones CASE que las hacen más fáciles de usar en PL/SQL


CASE con Comparaciones Adicionales a Igualdad (Sentencia y Expresión)

Hasta la versión 23c, tanto las sentencias como expresiones CASE permitían solo comparaciones por igualdad cuando se usaba en su forma simple, si se deseaba comparar de otra forma debía usarse en su forma "de búsqueda" como mostramos a continuación:

Ejemplo de Forma Simple (sólo Igualdad)

Cuando necesitamos usar solo igualdades, podemos usar la forma simple:

CASE TipoCliente
    WHEN 1 THEN 'Mayorista'
    WHEN 2 THEN 'Minorista'
    WHEN 3 THEN 'No Clasificado'
END CASE


Ejemplo de Forma de Búsqueda

Pero si necesitamos buscar valores no sólo por igualdad entonces tenemos que usar la forma de búsqueda, de la siguiente manera:

CASE
    WHEN Saldo < 100000 THEN 'Deudor - Crítico'
    WHEN Saldo < 0 THEN 'Deudor - Normal'
    WHEN Saldo = 0 THEN 'Neutro'
    WHEN Saldo > 0 THEN 'Acreedor'
END CASE

La diferencia es que en cada cláusula WHEN debemos escribir una operación de comparación que devuelva un valor verdadero o falso, teniendo que repetir en este caso el nombre de la columna que deseamos comparar.


Case de Forma Simple en Oracle 23c

Ahora es posible usar una sentencia CASE de forma más simple, especificando la columna o expresión a utilizar para comparar una sola vez, y aclarando en cada cláusula WHEN la operación de comparación (mayor, menor, distinto, etc) que se desea realizar, en caso que no sea de tipo igualdad:

CASE Saldo
    WHEN < 100000 THEN 'Deudor - Crítico'
    WHEN < 0 THEN 'Deudor - Normal'
    WHEN 0 THEN 'Neutro'
    WHEN > 0 THEN 'Acreedor'
END CASE

A continuación vemos un ejemplo muy simple de su uso como expresión (esta mejora también aplica a CASE como sentencia, no sólo como expresión):

DECLARE vTipo VARCHAR2(20); BEGIN FOR cSegmentos IN (SELECT SEGMENT_NAME, BLOCKS FROM USER_SEGMENTS
                       ORDER BY SEGMENT_NAME FETCH FIRST 10 ROWS ONLY) LOOP vTipo := CASE cSegmentos.BLOCKS WHEN > 1000 THEN 'Gigante' WHEN > 100 THEN 'Grande' WHEN > 0 THEN 'Normal' WHEN 0 THEN 'Vacío' END; DBMS_OUTPUT.PUT_LINE (cSegmentos.SEGMENT_NAME || ' es ' || vTipo); END LOOP; END; /

Lo cual devuelve lo siguiente:


CASE con Múltiples Comparaciones (Sentencia y Expresión)

Adicionalmente, cuando existe más de una condición que debe devolver el mismo valor, podemos usar todas ellas en la misma sección de la cláusula WHEN en vez de tener que especificar una cláusula por cada condición, usando si deseamos la opción de comparación por cualquier tipo de operación.

A continuación, vemos un ejemplo pero usando una sentencia CASE en vez de una expresión (le mejora es válida para ambas)  y usando también la opción de comparación no sólo por igualdad sino también por rangos:

DECLARE vTipo VARCHAR2(20); BEGIN FOR cSegmentos IN (SELECT SEGMENT_NAME, BLOCKS FROM USER_SEGMENTS
                       ORDER BY SEGMENT_NAME FETCH FIRST 10 ROWS ONLY) LOOP CASE cSegmentos.BLOCKS     WHEN > 1000 THEN vTipo := 'Gigante';
WHEN > 100 THEN vTipo := 'Grande';
WHEN 8, 16, 32, 64 THEN vTipo := 'Normal';
WHEN 0 THEN vTipo := 'Vacío';
END CASE; DBMS_OUTPUT.PUT_LINE (cSegmentos.SEGMENT_NAME || ' es ' || vTipo); END LOOP; END;
/


Conclusión

Estas mejoras a la sentencia y expresión CASE simplifican la escritura de código PL/SQL permitiendo escribir estas acciones en forma más concisa y clara.


Si desean conocer más sobre Oracle 23c, es recomendable que vean estos artículos en este blog como punto de partida:

Adicionalmente, pueden consultar todos los artículos relacionados a Oracle Database 23c agrupados en en el tag Database 23c.


martes, 9 de marzo de 2021

Oracle 21c - Mejoras a Expresiones Calificadas

A partir de Oracle 21c se agregan tres nuevos tipos opciones de iterador para su uso en expresiones calificadas. El uso de expresiones calificadas mejora la claridad de los programas y la productividad del programador. Las mismas se encuentran disponibles desde Oracle 18c, incorporando nuevas funcionalidades en Oracle 21c.

Algunas de estas mejoras están asociadas a otras mejoras PL/SQL, como ser las del iterador FOR .. LOOP que vimos en los dos artículos anteriores (parte #1 y parte #2)


Iteradores

A continuación veremos los tres nuevos iteradores que pueden ser usados en expresiones calificadas.

Iterador por Indice

Este iterador permite popular una colección proporcionando expresiones para el cálculo tanto del índice como del valor a almacenar.

Ejemplo:

DECLARE 
  TYPE TablaT IS TABLE OF PLS_INTEGER INDEX BY PLS_INTEGER;;
  vTablaT    TablaT;
BEGIN
  -- 
  vTablaT := TablaT (FOR i in 2 .. 4 index i * 2 => i * 3); 
  --
END;

Resultado:

Indice=4    Valor=6
Indice=6    Valor=9
Indice=8    Valor=12

Lo que hace el iterador es recorrer la lista de valores declaradas en la cláusula FOR (en nuestro caso, del 2 al 4) y asignarle al indice el resultado de "i * 2", y almacenar el valor "i * 3" en la colección. Como vemos, ambas expresiones pueden usar el valor del iterando "i".

En nuestro ejemplo usamos un array asociativo con valores numéricos, pero los mismos podrían ser de cualquier tipo de datos (varchar, date, etc.). Este iterador acepta la mayoría de las mejoras que se incorporaron en Oracle 21c al iterador FOR .. LOOP, como ser saltear valores, detenerse al llegar a una condición dada, o iterar en saltos distintos de 1, como mostramos a continuación:


DECLARE 
  TYPE TablaT IS TABLE OF DATE INDEX BY PLS_INTEGER;;
  vTablaT    TablaT;
BEGIN
  -- 
  vTablaT := TablaT (FOR i IN 1..10 BY 2 WHEN i > 5 INDEX i => SYSDATE + i); 
  --
END;

En este caso, va a iterar del 1 al 10 de a dos en dos pero solo cuando i sea mayor a 5, por lo que los valores índices serían 6, 8 y 10 y los valores almacenados serían la fecha actual mas 6, 8 y 10 días.


Iterador por Secuencia

Este iterador permite popular una colección proporcionando expresiones para el calculo del valor a almacenar, mientras que el indice se popula en forma automática con valores secuenciales comenzando en 1.

Ejemplo:

DECLARE 
  TYPE TablaT IS TABLE OF PLS_INTEGER INDEX BY PLS_INTEGER;;
  vTablaT    TablaT;
BEGIN
  -- 
  vTablaT := TablaT (FOR i in 2 .. 4 SEQUENCE => i * 3); 
  --
END;

Resultado:

Indice=1    Valor=6
Indice=2    Valor=9
Indice=3    Valor=12

La diferencia con el ejemplo anterior es que los índices en este caso se calcularon en forma automática con valores secuenciales comenzando por el 1, mientras que usamos el valor del iterador en la expresión que calcula el valor a almacenar en la colección.

Al igual que el iterador por indice, el iterador por secuencia permite el uso de la mayoría de las mejoras de Oracle 21c realizadas a FOR .. LOOP.


Iterador Básico

Este iterador permite popular una colección proporcionando expresiones para el calculo del valor a almacenar, mientras que el indice se popula en forma automática con el valor del iterador..

Ejemplo:

DECLARE 
  TYPE TablaT IS TABLE OF PLS_INTEGER INDEX BY PLS_INTEGER;;
  vTablaT    TablaT;
BEGIN
  -- 
  vTablaT := TablaT (FOR i in 2 .. 4 => i * 3); 
  --
END;

Resultado:

Indice=2    Valor=6
Indice=3    Valor=9
Indice=4    Valor=12
La diferencia con el ejemplo anterior es que los índices en este caso se calcularon en forma automática con el valor del iterador, el cual también puede ser utilizado en la expresión que calcula el valor a almacenar en la colección.


Notación Posicional para Array Asociativo

Adicionalmente a estos nuevos iteradores, Oracle 21c simplifica aun mas la asignación de valores a un array asociativo cuando no usamos iterador. A continuación veremos la evolución en las últimas versiones de Oracle.


Previo a Oracle 18c

DECLARE 
  TYPE TablaT IS TABLE OF VARCHAR2(20) INDEX BY PLS_INTEGER;
vTablaT    TablaT; BEGIN -- vTablaT(1) := 'Diez';
vTablaT(2) := 'Veinte';
vTablaT(3) := 'Treinta';
END; /


Oracle 18c

DECLARE 
  TYPE TablaT IS TABLE OF VARCHAR2(20) INDEX BY PLS_INTEGER;
vTablaT    TablaT; BEGIN -- vTablaT := TablaT(1 => 'Diez', 2 => 'Veinte', 3 => 'Treinta'); END; /


Oracle 21c

DECLARE 
  TYPE TablaT IS TABLE OF VARCHAR2(20) INDEX BY PLS_INTEGER;
vTablaT    TablaT; BEGIN -- vTablaT := TablaT('Diez', 'Veinte', 'Treinta');
END; /

Como vemos, en Oracle 21c no necesitamos indicar el valor del indice usando la notación posicional, el mismo es generado en forma automática comenzando con 1.


Links

Recomendamos leer la documentación oficial de Oracle para tener un mejor conocimiento de estas nuevas características.

lunes, 8 de marzo de 2021

Oracle 21c - Mejoras al iterador FOR LOOP - Parte #2

En el articula anterior, "Oracle 21c - Mejoras al iterador FOR LOOP - Parte #1" vimos las mejoras que trae la version 21c de Oracle Database al iterador FOR .. LOOP. En este articulo veremos otras mejoras que permiten utilizar el mismo con colecciones de PL/SQL.


Control de Iteraciones de Colecciones

Existen otros tres nuevos modificadores del iterador FOR .. LOOP que permiten obtener valores en base a colecciones de PL/SQL en vez de rangos. Su funcionamiento es similar al uso de "VALUES OF" y de "INDICES OF" en las operaciones "FORALL" de PL/SQL.

Uso de VALUES OF 

Esta opción nos permite "recorrer" los distintos valores almacenados en un array asociativo de PL/SQL (conocidos como index-by-table) que posee tres elementos, pero cuyos valores de indices se encuentran dispersos (15, 40 y 68):

DECLARE 
  TYPE  TablaT IS TABLE OF PLS_INTEGER INDEX BY PLS_INTEGER;
  vTabla TablaT;
BEGIN
  vTabla(15) := 10;
vTabla(40) := 20;
vTabla(68) := 30;
FOR NumVar IN VALUES OF vTabla LOOP
dbms_output.put_line('Soy el numero ' || TO_CHAR(NumVar));
END LOOP; END; / Soy el numero 10 Soy el numero 20 Soy el numero 30 PL/SQL procedure successfully completed. SQL>

Uso de INDICES OF

Esta opción es similar a la opción que vimos anteriormente, pero en vez de obtener los valor asociados a un indice, obtenemos directamente el valor del indice. Usando los mismos datos del ejemplo anterior, podemos ver que obtenemos los valores 15, 40 y 68 que representan los indices del array asociativo que creamos:

DECLARE 
  TYPE  TablaT IS TABLE OF PLS_INTEGER INDEX BY PLS_INTEGER;
  vTabla TablaT;
BEGIN
  vTabla(15) := 10;
vTabla(40) := 20;
vTabla(68) := 30;
FOR NumVar IN INDICES OF vTabla LOOP
dbms_output.put_line('Soy el numero ' || TO_CHAR(NumVar));
END LOOP; END; / Soy el numero 15 Soy el numero 40 Soy el numero 68 PL/SQL procedure successfully completed. SQL>

Uso de PAIRS OF

Esta opción nos permite acceder tanto a los valores almacenados en un array asociativo como a los indices asociados a los mismo en un mismo paso. Podemos pensar a la misma como una combinación de las dos opciones que vimos anteriormente. Para comprender su uso, veamos un ejemplo donde almacenamos el valor alfanumérico de cada numero utilizado como índice, y accedemos a ambos en el iterador FOR .. LOOP:

DECLARE 
  TYPE  TablaT IS TABLE OF VARCHAR2(100) INDEX BY PLS_INTEGER;
  vTabla TablaT;
BEGIN
  vTabla(15) := 'Quince';
vTabla(40) := 'Cuarenta';
vTabla(68) := 'Sesenta y Ocho';
FOR NumVar, TextVar IN PAIRS OF vTabla LOOP
dbms_output.put_line('Soy el numero ' || TO_CHAR(NumVar) || ' que sería ' || TextVar); END LOOP; END; / Soy el numero 15 que sería Quince Soy el numero 40 que sería Cuarenta Soy el numero 68 que sería Sesenta y Ocho
PL/SQL procedure successfully completed. SQL>

viernes, 5 de marzo de 2021

Oracle 21c - Mejoras al iterador FOR LOOP - Parte #1

El lenguaje PL/SQL de Oracle posee desde sus comienzos una funcionalidad muy utilizada que es el iterador FOR..LOOP. El mismo permite en forma sencilla recorrer una serie de valores consecutivos dentro de un rango dado. Veamos un ejemplo de su funcionalidad:

BEGIN
  FOR NumVar IN 10 .. 12 LOOP
    dbms_output.put_line('Soy el numero ' || TO_CHAR(NumVar));
  END LOOP;
END;
/
Soy el numero 10
Soy el numero 11
Soy el numero 12


PL/SQL procedure successfully completed.

SQL>
No es necesario declarar previamente la variable "NumVar" ni aclarar el incremento deseado (siempre es de a uno). Es posible incluir la palabra clave "REVERSE" antes del rango para que el mismo lo haga en orden reverso, pero hasta Oracle 21c no era posible hacer mucho mas:

BEGIN
  FOR NumVar IN REVERSE 10 .. 12 LOOP
    dbms_output.put_line('Soy el numero ' || TO_CHAR(NumVar));
  END LOOP;
END;
/
Soy el numero 12
Soy el numero 11
Soy el numero 10


PL/SQL procedure successfully completed.

SQL>

Lo nuevo en Oracle 21c



Oracle 21c ofrece algunas opciones adicionales, las cuales vamos a analizar a continuación y en un segundo artículo.

Iteración de valor único

En Oracle 21c Podemos hacer iteraciones de un un solo valor, de la siguiente manera:

BEGIN
  FOR NumVar IN 20 LOOP
    dbms_output.put_line('Soy el numero ' || TO_CHAR(NumVar));
  END LOOP;
END;
/
Soy el numero 20

PL/SQL procedure successfully completed.

SQL>
En si, esta no parece una opción muy interesante, pero lo que le suma utilidad es la posibilidad de usar la clausula REPEAT con la opción WHILE para definir una lógica adicional, como mostramos a continuación:

BEGIN
  FOR NumVar IN 2, REPEAT NumVar * 3 WHILE NumVar < 100 LOOP
    dbms_output.put_line('Soy el numero ' || TO_CHAR(NumVar));
  END LOOP;
END;
/
Soy el numero 2
Soy el numero 6
Soy el numero 18
Soy el numero 54

PL/SQL procedure successfully completed.

Iteraciones Múltiples

Esta opción nos permite definir varios rangos para iterar, de forma tal que la lógica que vamos a ejecutar la definimos en un solo lugar (en nuestro ejemplo, es un simple DBMS_OUTPUT) sin necesidad de crear multiples bloques de iteración:

BEGIN
  FOR NumVar IN 2 .. 4, REVERSE 6 .. 8, 15 .. 16 LOOP
    dbms_output.put_line('Soy el numero ' || TO_CHAR(NumVar));
  END LOOP;
END;
/
Soy el numero 2
Soy el numero 3
Soy el numero 4
Soy el numero 8
Soy el numero 7
Soy el numero 6
Soy el numero 15
Soy el numero 16

PL/SQL procedure successfully completed.

Iteraciones por rango con salto definido por el usuario

Esta opción nos permite controlar el incremento entre cada iteración (antes de Oracle 21c era si o si el valor 1), como vemos a continuación:


BEGIN
  FOR NumVar IN 2 .. 6 BY 2, REVERSE 2 .. 6 BY 2 LOOP
    dbms_output.put_line('Soy el numero ' || TO_CHAR(NumVar));
  END LOOP;
END;
/
Soy el numero 2
Soy el numero 4
Soy el numero 6
Soy el numero 6
Soy el numero 4
Soy el numero 2

PL/SQL procedure successfully completed.

Iteraciones por rango con salto decimal definido por el usuario

Si bien la sintaxis permitía el uso de valores decimales para los valores de inicio y fin del rango, estos eran redondeados a un valor enter en forma automática. Oracle 21c permite definir tanto rangos decimales (manteniendo los incrementos en 1) como también incrementos decimales, como podemos ver en los siguientes ejemplos. 

En este ejemplo, el primer rango tiene comienzo y fin con decimales pero incremento entero:

BEGIN
  FOR NumVar NUMBER(4,1) IN 2.3 .. 4.3 LOOP
    dbms_output.put_line('Soy el numero ' || TO_CHAR(NumVar));
  END LOOP;
END;
/
Soy el numero 2.3
Soy el numero 3.3
Soy el numero 4.3

PL/SQL procedure successfully completed.
Este segundo ejemplo, tanto los valores de inicio / fin del rango como el incremento se define como valor decimal:

BEGIN
  FOR NumVar NUMBER(4,1) IN 2.3 .. 4.3 BY 0.4 LOOP
    dbms_output.put_line('Soy el numero ' || TO_CHAR(NumVar));
  END LOOP;
END;
/
Soy el numero 2.3
Soy el numero 2.7
Soy el numero 3.1
Soy el numero 3.5
Soy el numero 3.9
Soy el numero 4.3

PL/SQL procedure successfully completed.

Lo único que tenemos que tener en cuenta, es definir el tipo de datos de la variable (NumVar) que va a mantener los valores, con un tipo de datos apropiado, como se resalta en los ejemplos.

Salteando valores y deteniendo las iteraciones

La opción WHEN nos permite saltear valores no deseados, por ejemplo si queremos "procesar" solo los números pares en un rango en particular, podemos usar la siguiente sintaxis:

BEGIN
  FOR NumVar IN 3 .. 10 WHEN MOD(NumVar,2) = 0 LOOP
    dbms_output.put_line('Soy el numero ' || TO_CHAR(NumVar));
  END LOOP;
END;
/
Soy el numero 4
Soy el numero 6
Soy el numero 8
Soy el numero 10

PL/SQL procedure successfully completed.

Por otro lado, la opción WHILE (la cual vimos dentro del uso de REPEAT) nos permite definir una condición de salida que detendrá la ejecución del bloque, como podemos ver a continuación:

BEGIN
  FOR NumVar IN 4 .. 20 WHILE NumVar * NumVar < 100 LOOP
    dbms_output.put_line('Soy el numero ' || TO_CHAR(NumVar));
  END LOOP;
END;
/
Soy el numero 4
Soy el numero 5
Soy el numero 6
Soy el numero 7
Soy el numero 8
Soy el numero 9

PL/SQL procedure successfully completed.

Como podemos ver, al evaluar NumVar = 10, la operación NumVar * NumVar da como resultado 100, dejando de cumplir la condición definida en la cláusula WHILE, por lo que el valor 10 no se imprime y el bucle termina.

Lo que sigue

En el próximo artículo veremos en detalles otras mejorar al iterador FOR .. LOOP incluidas en Oracle 21c, que permiten usar el mismo con colecciones PL/SQL.



martes, 28 de julio de 2020

Curso Gratuito de SQL Performance Tuning por Chris Saxon



Este lunes, en el blog "All Things SQL" de Oracle se anunció un curso 100% online y gratuito, de SQL Performance dictado por Chris Saxon.

El contenido del mismo se divide en módulos que abarcan los siguientes temas:

  • Cómo leer un plan de ejecución
  • ¿Qué son las estadísticas de la base de datos?
  • ¿Cuánto trabajo está haciendo mi consulta?
  • Cómo crear índices
  • ¿Por qué mi consulta no utiliza un índice?
  • Cómo resumir datos rápidamente con vistas materializadas
  • ¿Cómo funcionan las uniones?
  • Cómo hacer inserciones, actualizaciones y eliminaciones más rápido
  • Cómo encontrar SQL lento
  • Revisión del curso

Cada uno de los módulos esta compuesto por:
  • Video donde se hace una presentación de los conocimientos.
  • Tutoriales interactivos en Live SQL donde se pueden probar los conocimientos de los videos.
  • Cuestionario Online, para validar los conocimientos adquiridos.

La registración al mismo puede hacerse en este LINK. El contenido estará disponible a partir del lunes 3 de agosto, aunque puede comenzarse en cualquier momento posterior, y una vez registrado el material estará disponible para ser visto y probado todas las veces que quieran 

Al culminar todos los cuestionarios, y dependiendo del puntaje promedio obtenido, obtendrá un certificado digital por haber completado el curso (o uno de Excelencia si supera el 90%!!!)

viernes, 3 de agosto de 2018

Agenda confirmada en ODC Latin America Tour 2018 Argentina

El próximo jueves 9 de Agosto llega la primera edición del Oracle Developer Community (ODC) Latin America Tour, la versión renovada del famoso OTN (Oracle Technology Network) Tour que desde hace varios años nos permite conocer características y detalles de las tecnologías de Oracle.

El evento se desarrollará entre las 08:30 y las 18:00 en la sede de la Fundación UADE, Lima 775, CABA. La inscripción al evento (requerida) se encuentra disponible en el siguiente link.

Al igual que en las ultimas ediciones, este año voy a participar con una charla titulada "What's new for developers in latest Oracle Database Releases?" en el Track #3 (Desarrollo PL/SQL), a las 11:45.

La agenda del completa del evento es la siguiente:


martes, 10 de julio de 2018

Voy a estar presentando en ODC Tour 2018 Argentina!



El próximo jueves 9 de Agosto voy a estar participando de  la primera edición del Oracle Developer Community, la versión renovada del famoso OTN (Oracle Technology Network) Tour que desde hace varios años nos permite conocer características y detalles de las tecnologías de Oracle. En este caso será una charla titulada "What's new for developers in latest Oracle Database Releases?" en el track de Desarrollo PL/SQL.





El evento se desarrollará entre las 08:30 y las 18:00 en la sede de la Fundación UADE, Lima 775, CABA. La lista de disertantes y agenda del evento se encuentra disponible aqui.


La inscripción al evento (requerida) se encuentra disponible en el siguiente link.

jueves, 8 de marzo de 2018

Serie de Artículos sobre Oracle 18c


Tal como lo hice el año pasado con el lanzamiento de Oracle 12c Release 2, este año voy a estar subiendo artículos técnicos sobre nuevas características de Oracle 18c.


Los primeros temas a tratar son:


A medida que publique un articulo, el link estará actualizado y disponible en esta misma nota.


miércoles, 15 de noviembre de 2017

Oracle Code Online en Diciembre



Nuevo evento gratuito Oracle Code Online en diciembre, cubriendo cinco tracks:

  • Database
  • Java
  • Emerging Technologies
  • DevOps/Containers
  • JavaScript


Como bonus, con la registración se obtienen U$S 300 para utilizar en Oracle Cloud






jueves, 19 de octubre de 2017

Oracle 12c R2 - Conversiones de Tipo de Datos y Manejo de Error

¿Como validar si un dato puede ser convertido? 

Cuando se deben realizar conversiones de datos, es frecuente encontrar valores que no pueden ser convertidos al tipo de datos deseado.

Tomemos el siguiente ejemplo (el texto en azul lo utilizaremos en todos los ejemplos del presente artículo):

WITH vDatos (ID, Fecha) AS
( SELECT 1, '20170101' FROM DUAL
  UNION ALL
  SELECT 2, '20171501' FROM DUAL
  UNION ALL
  SELECT 3, '20170115' FROM DUAL
  UNION ALL
  SELECT 4, '01-apr-2017' FROM DUAL
  UNION ALL
  SELECT 5, '01-abr-2017' FROM DUAL
  UNION ALL
  SELECT 6, '01/08/17' FROM DUAL
  UNION ALL
  SELECT 7, '30-Feb-17' FROM DUAL
)
SELECT ID, Fecha, TO_DATE(Fecha)
FROM vDatos v;

Según cómo tengamos configurados los parámetros NLS_DATE_FORMAT y NLS_DATE_LANGUAGE, al ejecutar la consulta anterior nos encontraremos con alguno de estos mensajes de error, los cuales impiden la ejecución de la consulta:

ORA-01861: literal does not match format string
ORA-01843: not a valid month
ORA-01858: a non-numeric character was found where a numeric was expected
ORA-01839: date not valid for month specified

Es por ello que es normal que quienes se han encontrado repetidamente con este problema, hayan creado funciones del estilo "es_una_fecha()" o "es_un_numero()" para poder identificar los datos que pueden ser convertidos, evitando los errores que cancelan la ejecución de la consulta.

En Oracle 12c Release 2, se introduce una nueva función "VALIDATE_CONVERSION" que recibe como entrada un dato y un tipo de datos al que deseamos convertir el dato original, y la función nos devuelve 1 cuando es posible realizar la conversión y 0 cuando no es posible.

Ejemplo (utilizando la clausula WITH del ejemplo anterior)

SELECT v.ID, v.Fecha, TO_DATE(v.Fecha)
FROM vDatos v
WHERE VALIDATE_CONVERSION(v.Fecha AS DATE) = 1;


También es posible (y recomendado) utilizar un formato específico para probar si el dato puede ser convertido, como vemos en este ejemplo (utilizando la clausula WITH del ejemplo anterior)

SELECT v.ID, v.Fecha, TO_DATE(v.Fecha,'YYYYMMDD')
FROM vDatos v
WHERE VALIDATE_CONVERSION(v.Fecha AS DATE, 'YYYYMMDD') = 1;


De esa forma, podemos identificar aquellos registros que pueden ser convertidos y aquellos que no pueden serlo. Esto resuelve muchos problemas de conversión de datos, permitiendo ignorar los registros que no poseen un dato que puede ser convertido.

¿Qué pasa cuando necesitamos procesar TODOS los registros, y utilizar algún valor especial cuando no es posible realizar la conversión?

En muchos escenarios, identificar que dato puede ser convertido y cual no puede no ser suficiente. Por eso Oracle 12C Release 2 introduce una nueva opción dentro de las funciones de conversión de datos que permite especificar un valor por defecto cuando no es posible realizar la conversión, en vez de generar un error.

Esta opcion es "DEFAULT {VALOR|NULL} ON CONVERSION ERROR" y puede ser utilizada tanto en la función CAST como en las funciones de conversión tradicionales ("TO_DATE", "TO_NUMBER", etc.).

A continuación podemos ver dos ejemplos de como utilizarlos (utilizando la clausula WITH del primer ejemplo para generar los datos)

SELECT v.ID, v.Fecha, TO_DATE(v.Fecha DEFAULT '19000101' ON CONVERSION ERROR,'YYYYMMDD')
FROM vDatos v



SELECT v.ID, v.Fecha, TO_DATE(v.Fecha DEFAULT NULL ON CONVERSION ERROR,'DD/MM/YY')
FROM vDatos v


martes, 10 de octubre de 2017

Oracle 12c R2 - Mejoras a APROXIMATE QUERY

APPROX_COUNT_DISTINCT en Oracle 12.1

En muchos escenarios de negocio, se requiere conocer información acumulada (total de ventas, cantidad de clientes nuevos, etc. etc.) con un grado de certeza importante, pero sin llegar a la necesidad de que el resultado sea 100% exacto.

Por ejemplo, la gerencia de una cadena de supermercados puede decidir dejar de abrir los días domingos, basándose en la cantidad de operaciones realizadas en dichos días y comparándolo con los otros días de la semana. En dicha situación, conocer el  número exacto de operaciones de venta por cada día de la semana no es una necesidad, sino que con un resultado aproximado es posible tomar una decisión.

En Oracle 12c, se introdujo la función de agregación aproximada APPROX_COUNT_DISTINCT que permite obtener resultados aproximados en forma mas rápida y consumiendo menos recursos.

El principal inconveniente de esta nueva funcionalidad radica en la necesidad de modificar el código existente para utilizar esta nueva función, como podemos ver en el siguiente ejemplo:

SELECT APPROX_COUNT_DISTINCT(manager_id) AS "Active Managers"
  FROM employees;

Nuevos parámetros de Oracle 12.2

Oracle 12c Release 2 introduce tres nuevos parámetros que permiten que las funciones de agregación "normales" (aquellas que venimos utilizando en nuestro código) realicen los cálculos por aproximación, sin necesidad de modificar las consultas existentes.

Los mismos son:

approx_for_aggregation
approx_for_count_distinct
approx_for_percentile

Ejemplo

Si ejecutamos una sentencia con COUNT(DISTINCT) en nuestra base de datos, sin habilitar el uso de funciones de aproximación, y consultamos el resultado y el plan de ejecución, obtenemos lo siguiente:

SQL> SELECT name, value
FROM   v$ses_optimizer_env
WHERE  sid = SYS_CONTEXT('USERENV','SID')
AND    name LIKE '%approx%';

NAME                                     VALUE
---------------------------------------- -------------------------
approx_for_aggregation                   false
approx_for_count_distinct                false
approx_for_percentile                    none

SQL>  SELECT COUNT(DISTINCT id) AS data_count
  2  FROM   t2;

DATA_COUNT
----------
   1010000

SQL> SET AUTOTRACE TRACE EXPLAIN
SQL> SELECT COUNT(DISTINCT id) AS data_count
FROM   t2;

Execution Plan
----------------------------------------------------------
Plan hash value: 4170058314

-----------------------------------------------------------------------------------------
| Id  | Operation            | Name     | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |          |     1 |    13 |       |  4472   (1)| 00:00:01 |
|   1 |  SORT AGGREGATE      |          |     1 |    13 |       |            |          |
|   2 |   VIEW               | VW_DAG_0 |  1010K|    12M|       |  4472   (1)| 00:00:01 |
|   3 |    HASH GROUP BY     |          |  1010K|  4931K|    11M|  4472   (1)| 00:00:01 |
|   4 |     TABLE ACCESS FULL| T2       |  1010K|  4931K|       |  1390   (1)| 00:00:01 |
-----------------------------------------------------------------------------------------

Analizando el plan de ejecución, podemos ver que que la consulta generó una vista agrupando por un HASH, para poder calcular el resultado acumulado, la cual consumió 11 Mb. de espacio temporal. El costo total de la consulta es de 4.472, correspondiendo 3.082 de ellos a dicha vista temporal.

Al habilitar el uso de funciones de agregación aproximadas por defecto (sin necesidad de cambiar la funcion COUNT() por la nueva función APROX_COUNT_DISTINCT(), la misma consulta devuelve otro resultado y nos genera el siguiente plan de ejecución:

SQL> ALTER SESSION SET approx_for_aggregation = TRUE;
Session altered.

SQL> SELECT name, value
FROM   v$ses_optimizer_env
WHERE  sid = SYS_CONTEXT('USERENV','SID')
AND    name LIKE '%approx%';

NAME                                     VALUE
---------------------------------------- -------------------------
approx_for_aggregation                   true
approx_for_count_distinct                true
approx_for_percentile                    all

SQL>  SELECT COUNT(DISTINCT id) AS data_count
  2  FROM   t2;

DATA_COUNT
----------
    979478

SQL> SET AUTOTRACE TRACE EXPLAIN
SQL> SELECT COUNT(DISTINCT id) AS data_count
FROM   t2;

Execution Plan
----------------------------------------------------------
Plan hash value: 3321871023

------------------------------------------------------------------------------
| Id  | Operation             | Name | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |     1 |     5 |  1390   (1)| 00:00:01 |
|   1 |  SORT AGGREGATE APPROX|      |     1 |     5 |            |          |
|   2 |   TABLE ACCESS FULL   | T2   |  1010K|  4931K|  1390   (1)| 00:00:01 |
------------------------------------------------------------------------------

Podemos ver que la segunda consulta devuelve un resultado (979.478) que difiere con el resultado exacto (1.010.000), con una diferencia del 3.02% respecto al resultado exacto.

Pero en este caso, el plan de ejecución nos muestra que Oracle no necesitó agrupar los datos en una vista, reduciendo el costo total en de 4.472 a 1.390 y sin consumir los 11 Mb. de espacio temporal.

En este ejemplo, se logra una reducción del 69% del costo de la consulta sacrificando un 3% de exactitud, lo cual puede ser un valor aceptable para muchos casos.




lunes, 2 de octubre de 2017

Oracle 12c R2 - LISTAGG y OVERFLOW

En Oracle 11gR2 se introdujo una nueva función analítica, LISTAGG, la cual ordena los datos dentro de cada grupo especificado en la cláusula ORDER BY y luego concatena los valores de la columna o expresión seleccionados.

Por ejemplo, si queremos obtener una lista de las tablas a las que tenemos acceso, junto con una lista de todas las columnas, ordenadas por nombre de columna, de cada una de tablas, podemos ejecutar la siguiente consulta:

SELECT table_name,
       LISTAGG(column_name, ',') WITHIN GROUP 
                  (ORDER BY column_name) Columns
FROM all_tab_cols
GROUP BY table_name;

Lo cual produce el siguiente resultado:


Pero el problema surge cuando la lista concatenada de valores supera el tamaño máximo que el lenguaje SQL soporta en Oracle, el cual es 4000 bytes. En el ejemplo, siguiente, repetimos 50 veces los datos para aumentar el tamaño del resultado de LISTAGG, obteniendo el siguiente error:

SELECT table_name,
       LISTAGG(column_name, ',') WITHIN GROUP 
                  (ORDER BY column_name) Columns
FROM all_tab_cols
    CROSS JOIN (SELECT level FROM dual CONNECT BY level <=50)
GROUP BY table_name;

ORA-01489: result of string concatenation is too long
01489. 00000 -  "result of string concatenation is too long"
*Cause:    String concatenation result is more than the maximum size.
*Action:   Make sure that the result is less than the maximum size

Oracle 12cR2 introduce una sintaxis ampliada para la función LISTAGG, la cual nos permite obviar el error, mostrando sólo los primeros 4000 bytes que la función obtiene, sin producir un error, como podemos ver a continuación (aquí "cortamos' la cadena concatenada en dos pedazos para poder ver el principio y fin de la misma):

SELECT x.table_name,
       SUBSTR(LISTAGG(x.column_name, ',' ON OVERFLOW TRUNCATE) 
            WITHIN GROUP (ORDER BY x.column_name),1,30) AS "Comienza con...",
       SUBSTR(LISTAGG(x.column_name, ',' ON OVERFLOW TRUNCATE)
            WITHIN GROUP (ORDER BY x.column_name),-30) AS "Termina con..."
FROM all_tab_cols x
    CROSS JOIN (SELECT level FROM dual CONNECT BY level <=50)
GROUP BY x.table_name;




Como vimos en el ejemplo anterior, la opción "ON OVERFLOW TRUNCATE" por defecto corta el resultado hasta el valor anterior cuyo tamaño no exceda los 4000 bytes, agrega "..." a continuación del último valor que puede ser mostrado, y luego muestra entre paréntesis la cantidad de caracteres que fueron truncados.

Podemos reemplazar los puntos suspensivos con cualquier cadena que deseemos utilizar como indicador para informar que el texto ha sido truncado, de la siguiente forma:

ON OVERFLOW TRUNCATE '///'

Tambien podemos indicarle que no deseamos saber el numero de caracteres truncados, de la siguiente forma:

ON OVERFLOW TRUNCATE WITHOUT COUNT


La sintaxis completa de la función LISTAGG en oracle 12c Release 2 puede encontrarse aqui.

lunes, 25 de septiembre de 2017

Oracle 12c R2 - Utilizar Constantes para definir el tamaño de Variables

Es común encontrar en nuestras aplicaciones, lugares donde accedemos a tablas o vistas diccionario de datos de Oracle para obtener cierta información, por ejemplo:

SELECT t.table_name
INTO vTabla
FROM user_tables t
WHERE t.table_name like ‘T_%’

El tamaño máximo de los indicadores era, hasta Oracle 12c R2, de 30 caracteres, por lo que es frecuente encontrar que se definía a la variable “vTabla” de la siguiente forma:

DECLARE
    vTabla    VARCHAR2(30);

Pero, como ya vimos en el artículo anterior del blog, "Oracle 12c R2 - Identificadores de 128 Bytes", la Release 2 de Oracle 12c nos permite definir indicadores de hasta 128 caracteres de largo, por lo que es probable que nuestras aplicaciones que definían variables para almacenar indicadores con un largo fijo de 30 caracteres muy pronto comiencen a fallar con el siguiente error:

ORA-06502: PL/SQL: numeric or value error: character string buffer too small

Afortunadamente, el cambio  en el tamaño de los indicadores viene acompañado en Oracle 12c R2 de la posibilidad de declarar el largo de variables usando constantes. Por ejemplo, podemos definir un paquete de constantes y especificar en el mismo los valores que deseamos utilizar:

CREATE OR REPLACE PACKAGE pkg_constantes
AS
  --
  cn_largo_identificadores  constant pls_integer := 128;
  cn_largo_codigos          constant pls_integer := 20;
  cn_largo_cuit             constant pls_integer := 11;
  --
END pkg_constantes;
/

Y luego utilizar los mismos al momento de definir variables:

declare
  vNombreTabla     VARCHAR2(pkg_constantes.cn_largo_identificadores );
  vCUIT           NUMBER(pkg_constantes.cn_largo_cuit);
  vCodigoCliente  VARCHAR2(pkg_constantes.cn_largo_codigos);

Para simplificar nuestra tarea, la versión 12c R2 ya provee una constante declarada en el paquete “DBMS_STANDARD” llamada “ORA_MAX_NAME_LEN” la cual representa un valor de 128, por lo que nuestro primer ejemplo puede ser reformulado de la siguiente forma:

DECLARE
    vTabla   VARCHAR2(ORA_MAX_NAME_LEN);

Ejemplo y documentación adicional 



lunes, 18 de septiembre de 2017

Oracle 12c R2 - Identificadores de 128 Bytes

Seguramente, mas de una vez cada desarrollador o administrador de Oracle sufrió un dolor de cabeza al tener que crear un objeto y encontrarse con la limitación de treinta caracteres para los nombres de objetos (identificadores).

Por ejemplo, si queríamos correr el siguiente script:

CREATE TABLE t_EstadoComprobante
( ID_EstadoComprobante      NUMBER(4)       NOT NULL,
  EstadoComprobante         VARCHAR(50)     NOT NULL
);
--
ALTER TABLE t_EstadoComprobante
    ADD CONSTRAINT PK_EstadoComprobante
        PRIMARY KEY (ID_EstadoComprobante);
--
CREATE TABLE t_Comprobante
( ID_Comprobante            NUMBER(4)       NOT NULL,
  Numero_Comprobante        NUMBER(8)       NOT NULL,
  Fecha_Comprobante         DATE            NOT NULL,
  Monto_Comprobante         NUMBER(12,2)    NOT NULL,
  ID_EstadoComprobante      NUMBER(4)       NOT NULL
);
--
ALTER TABLE t_Comprobante
    ADD CONSTRAINT PK_Comprobante
        PRIMARY KEY (ID_Comprobante);
--
ALTER TABLE t_Comprobante 
    ADD CONSTRAINT FK_Comprobante_EstadoComprobante 
        FOREIGN KEY (ID_EstadoComprobante)
        REFERENCES t_EstadoComprobante (ID_EstadoComprobante);
--

Nos encontrábamos con el siguiente mensaje de error.

ORA-00972: identifier is too long
00972. 00000 -  "identifier is too long"
*Cause:    An identifier with more than 30 characters was specified.
*Action:   Specify at most 30 characters.

La limitación de 30 caracteres obligaba a utilizar abreviaturas para nombrar a identificadores, haciendo poco legibles (y hasta inentendibles) ciertos nombres.

Identificadores Largos (hasta 128 bytes) en Oracle 12c R2

La nueva versión de Oracle soporta identificadores de hasta 128 bytes, por lo que de ejecutar el script anterior en una base de datos 12c R2, obtendremos el siguiente resultado:

Table created.
Table altered.
Table created.
Table altered.
Table altered.
Si consultamos la estructura de las tablas del diccionario de datos, podemos ver que la mayoria de las columnas de las vistas del diccionario de datos que contienen identificadores ahora tienen un tamaño de 128 bytes:

SQL> desc all_tables
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 OWNER                                     NOT NULL VARCHAR2(128)
 TABLE_NAME                                NOT NULL VARCHAR2(128)
 TABLESPACE_NAME                                    VARCHAR2(30)
 CLUSTER_NAME                                       VARCHAR2(128)
 ...
 ...

Restricciones y Consideraciones

Los siguientes objetos mantienen sus identificadores con el largo como en versiones anteriores.
  • 8 Bytes
    • Nombre de Base de Datos
  • 30 Bytes
    • Tablespace
    • Disk Group
    • PDBs
    • Rollback Segment
Otro aspecto a tener en cuenta es que el nuevo tamaño se mide en Bytes, por lo que si nuestra base de datos tiene un set de caracteres UTF8, cada caracter puede ocupar entre 1 y 3 bytes, por lo que el tamaño máximo en caracteres del identificador puede ser menor, como vemos a continuación:

create table tablááááááááááááááááááááááááááááááááááááááááááááááááááááááááááááááá 
(
  Son_67_Caracteres_pero_ocupan_130_bytes int
);

ORA-00972: identifier is too long


sábado, 9 de septiembre de 2017

Oracle 12c R2 - PRAGMA DEPRECATE

¿Qué es "PRAGMA DEPRECATE"?


La opción de compilador "PRAGMA DEPRECATE" permite marcar a un elemento PL/SQL como obsoleto. El compilador emite advertencias para usos del pragma DEPRECATE o el de elementos obsoletos.

Las advertencias le indican a los usuarios que utilizan un elemento obsoleto que es necesario cambiar el código para tener en cuenta la depreciación.


¿Qué objetos se pueden marcar como obsoletos con "PRAGMA DEPRECATE"?


Se puede marcar como obsoleto objetos de los siguientes tipos:

  • Subprograms
  • Packages
  • Variables
  • Constants
  • Types
  • Subtypes
  • Exceptions
  • Cursors

¿Cómo se utiliza?

Habilitar las advertencias de compilador

Como primer medida, tenemos que habilitar las advertencias de compilador para poder utilizar este nuevo Pragma. Se puede hacer para las advertencias relacionadas con "PRAGMA DEPRECATE" o bien se puede habilitar para todas las advertencias de compilación.

ALTER SESSION SET PLSQL_WARNINGS='ENABLE:(6019,6020,6021,6022)';
ALTER SESSION SET PLSQL_WARNINGS='ENABLE:ALL'; 

Compilar una unidad de programación utilizando ""PRAGMA DEPRECATE"

En el siguiente ejemplo, compilamos un paquete e indicamos que el mismo se encuentra obsoleto al incluir la línea "PRAGMA DEPRECATE" seguida del nombre del paquete.

CREATE OR REPLACE PACKAGE pkg_Deprecado AUTHID DEFINER 
AS
   PRAGMA DEPRECATE (pkg_Deprecado);
   --
   PROCEDURE Procedimiento1;
   --
   FUNCTION Funcion1 RETURN NUMBER;
END pkg_Deprecado;

SP2-0808: Package created with compilation warnings

Dependiendo de la IDE que utilicemos para desarrollar, la advertencia puede mostrarse en forma automática o tendremos que consultar que tipo de advertencia se ha generado:

SQL> SHOW ERRORS
Errors for PACKAGE PKG_DEPRECADO:

LINE/COL ERROR
-------- -------------------------------------------------------
3/4      PLW-06019: entity PKG_DEPRECADO is deprecated

Lo mismo si probamos de marcar como obsoleto sólo a un procedimiento de un paquete:

CREATE OR REPLACE PACKAGE pkg_Codigo AUTHID DEFINER
AS
  PROCEDURE Procedimiento1;
  PRAGMA DEPRECATE (Procedimiento1, 
      'pkg_Codigo.Procedimiento1 deprecado. Utilice pkg_Codigo.Procedimiento2');
  --
  PROCEDURE Procedimiento2;
END;

SP2-0808: Package created with compilation warnings

El mensaje de error ahora nos indica que la entidad que se encuentra marcada como obsoleta es el procedimiento "PROCEDIMIENTO1" y no el paquete completo.

SQL> SHOW ERRORS
Errors for PACKAGE PKG_DEPRECADO:

LINE/COL ERROR
-------- -------------------------------------------------------
4/3      PLW-06019: entity PROCEDIMIENTO1 is deprecated

¿Qué significa cada mensaje de Advertencia?

A continuación tenemos la definición oficial de cada uno de los cuatro mensajes de advertencia o error relacionados con "PRAGMA DEPRECATE"


Nro. de AdvertenciaSignificado de la Advertencia
6019La entidad ha sido marcada como obsoleta y puede ser removida en una versión futura. No utilice esta entidad.
6020La entidad referenciada ha sido marcada como obsoleta y puede ser removida en una versión futura. No utilice esa entidad. Siga las instrucciones especificadas en la advertencia si están disponibles
6021Pragma mal ubicado. El pragma DEPRECATE debe seguir en forma inmediata a la declaración de la entidad que esta siendo marcada como obsoleta.Ubique al pragma inmediatamente debajo de la declaración de la entidad que esta siendo marcada como obsoleta
6022Esta entidad no puede ser marcada como obsoleta. Esta funcionalidad sólo aplica a entidades que se declaran en un Paquete, a especificaciones de tipos o a Procedimientos y Funciones. Elimine el pragma.

Advertencia 6019
La advertencia (o error) 6019, como vimos al compilar los dos paquetes anteriores, ocurrirá cada vez que compilemos un objeto que se ecuentra "marcado" como obsoleto con la instrucción "PRAGMA DEPRECATE".

Advertencia 6020
La advertencia (o error) 6020 ocurrirá cada vez que compilemos un objeto que referencia a otro que se encuentra "marcado" como obsoleto con la instrucción "PRAGMA DEPRECATE".

Siguiendo con el ejemplo anterior, si intentamos compilar un procedimiento que referencia al procedimiento "Procedimiento1" del paquete "pkg_Codigo", obtendremos la advertencia 6020:

CREATE OR REPLACE PROCEDURE Usar_Procedimiento_Deprecado
IS
BEGIN
   pkg_codigo.procedimiento1;
END;
/

SP2-0804: Procedure created with compilation warnings

SQL> SHOW ERRORS
Errors for PROCEDURE USAR_PROCEDIMIENTO_DEPRECADO:

LINE/COL ERROR
-------- -----------------------------------------------------------------
4/4      PLW-06020: reference to a deprecated entity: PROCEDIMIENTO1
         declared in unit PKG_CODIGO[3,13].  pkg_Codigo.Procedimiento1
         deprecado. Utilice pkg_Codigo.Procedimiento2

Este escenario es el mas útil, ya que una vez que un objeto sea marcado como obsoleto, cualquier usuario que compile cualquier unidad de programación que haga referencia al mismo recibirá la advertencia, pudiendo en ese caso modificar su código para utilizar el código vigente en vez del objeto obsoleto.

Advertencia 6021
La advertencia (o error) 6021 ocurrirá si tratamos de compilar un objeto con una sentencia "PRAGMA DEPRECATE" ubicada en un lugar incorrecto, como podemos ver en el siguiente ejemplo:

CREATE OR REPLACE PACKAGE MiPaquete AUTHID DEFINER AS
  --
  PRAGMA DEPRECATE(CalculaTotal);
  --
  CURSOR c_cursor IS
    SELECT dummy FROM dual;
  --
  PROCEDURE CalculaTotal;
  --
END MiPaquete;
/

SP2-0808: Package created with compilation warnings

SQL> SHOW ERRORS
Errors for PACKAGE MIPAQUETE:

LINE/COL ERROR
-------- -----------------------------------------------------------------
3/10     PLW-06021: PRAGMA DEPRECATE on CALCULATOTAL is misplaced


Advertencia 6022
La advertencia (o error) 6022 indica que estamos tratando de marcar como obsoleto un objeto que no puede ser marcado como obsoleta. Esto puede ocurrir si marcamos como obsoleto un procedimiento o función interna (declarada dentro de otro procedimiento o función), como podemos ver en el siguiente ejemplo:

CREATE OR REPLACE PROCEDURE Error6022
IS
   PROCEDURE Error6022_Interno
   IS
   PRAGMA DEPRECATE (Error6022_Interno, 'Error6022_Interno es Obsoleto');
   BEGIN
     DBMS_OUTPUT.PUT_LINE('Probando Error6022_Interno');
   END;
BEGIN
  --
  DBMS_OUTPUT.PUT_LINE('Probando Error6022');
  --
  Error6022_Interno;
  --
END;

Warning: PROCEDURE ERROR6022
Line: 5 PLW-06022: cannot use PRAGMA DEPRECATE on this entity


Documentación y ejemplos en Oracle Live SQL