Skip to content

11. Programación con PL/SQL

PL/SQL (Procedural Language/SQL) es el lenguaje de programación procedural de Oracle que extiende SQL con estructuras de control, variables, manejo de excepciones y otras características de los lenguajes de programación tradicionales. Permite desarrollar aplicaciones complejas y robustas directamente en la base de datos.

PL/SQL es un lenguaje de programación de Oracle que combina:

  • La potencia de SQL para manipular datos
  • Estructuras procedurales como condiciones, bucles y subprogramas
  • Manejo de excepciones para control de errores
  • Capacidades de programación modular
  • Integración con SQL: Permite ejecutar múltiples sentencias SQL como una sola unidad
  • Rendimiento optimizado: Reduce el tráfico de red al procesar la lógica en el servidor
  • Portabilidad: El código PL/SQL funciona en cualquier sistema operativo donde se ejecute Oracle
  • Modularidad: Permite organizar el código en procedimientos, funciones y paquetes reutilizables
  • Manejo de excepciones: Proporciona un mecanismo robusto para el tratamiento de errores
  • Seguridad: Permite implementar lógica de negocio y validaciones a nivel de base de datos

Un bloque PL/SQL tiene la siguiente estructura:

[DECLARE -- Sección de declaración (opcional)
-- Declaración de variables, constantes, cursores, etc.
variable1 tipo1;
variable2 tipo2 := valor_inicial;]
BEGIN -- Sección ejecutable (obligatoria)
-- Instrucciones PL/SQL y SQL
-- Sentencias de control, asignaciones, etc.
[EXCEPTION -- Sección de manejo de excepciones (opcional)
-- Manejo de errores
WHEN excepcion1 THEN
-- Acciones para manejar excepcion1
WHEN OTHERS THEN
-- Acciones para manejar otras excepciones]
END; -- Fin del bloque (obligatorio)
/
-- Bloque anónimo (no se guarda en la base de datos)
BEGIN
DBMS_OUTPUT.PUT_LINE('Hola, mundo!');
END;
/

Para ejecutar código PL/SQL en Oracle, puede utilizar:

  • SQL*Plus: Cliente de línea de comandos de Oracle
  • SQL Developer: Entorno de desarrollo integrado (IDE) de Oracle
  • Otras herramientas: Toad, PL/SQL Developer, DBeaver, etc.
-- Configurar la salida en SQL*Plus o SQL Developer
SET SERVEROUTPUT ON;
-- Ejecutar un bloque anónimo
BEGIN
DBMS_OUTPUT.PUT_LINE('Ejecutando PL/SQL...');
END;
/
-- Ejecutar un procedimiento
EXECUTE saludar('Juan');
-- o
BEGIN
saludar('Juan');
END;
/
-- Ejecutar una función
SELECT obtener_saludo('María') FROM dual;
-- o
DECLARE
v_saludo VARCHAR2(100);
BEGIN
v_saludo := obtener_saludo('María');
DBMS_OUTPUT.PUT_LINE(v_saludo);
END;
/

Las variables en PL/SQL se declaran en la sección DECLARE y pueden ser inicializadas en el momento de la declaración.

DECLARE
-- Declaración simple
v_nombre VARCHAR2(100);
-- Declaración con inicialización
v_edad NUMBER(3) := 25;
-- Declaración usando %TYPE (hereda el tipo de una columna)
v_salario empleados.salario%TYPE;
-- Declaración usando %ROWTYPE (hereda la estructura de una fila)
v_empleado empleados%ROWTYPE;
-- Constantes
c_pi CONSTANT NUMBER := 3.14159;
-- Variables de tipo registro personalizado
TYPE t_persona IS RECORD (
nombre VARCHAR2(100),
edad NUMBER,
ciudad VARCHAR2(50)
);
v_persona t_persona;
-- Arrays asociativos (colecciones indexadas)
TYPE t_numeros IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
v_numeros t_numeros;
BEGIN
-- Asignación de valores
v_nombre := 'Juan Pérez';
v_salario := 5000.50;
-- Asignación a un registro
v_persona.nombre := 'María López';
v_persona.edad := 30;
v_persona.ciudad := 'Madrid';
-- Asignación a un array asociativo
v_numeros(1) := 100;
v_numeros(2) := 200;
-- Asignación a un %ROWTYPE
SELECT * INTO v_empleado
FROM empleados
WHERE empleado_id = 101;
-- Mostrar valores
DBMS_OUTPUT.PUT_LINE('Nombre: ' || v_nombre);
DBMS_OUTPUT.PUT_LINE('Salario: ' || v_salario);
DBMS_OUTPUT.PUT_LINE('Persona: ' || v_persona.nombre || ', ' || v_persona.edad || ' años');
DBMS_OUTPUT.PUT_LINE('Número 1: ' || v_numeros(1));
DBMS_OUTPUT.PUT_LINE('Empleado: ' || v_empleado.nombre || ', Departamento: ' || v_empleado.departamento_id);
END;
/
Tipo de datoDescripciónEjemplo
VARCHAR2(n)Cadena de caracteres de longitud variablev_nombre VARCHAR2(100) := ‘Juan’;
CHAR(n)Cadena de caracteres de longitud fijav_codigo CHAR(5) := ‘ABC12’;
NUMBER(p,s)Número con precisión p y escala sv_salario NUMBER(8,2) := 1250.75;
DATEFecha y horav_fecha DATE := SYSDATE;
TIMESTAMPFecha y hora con fracciones de segundov_ts TIMESTAMP := SYSTIMESTAMP;
BOOLEANValor lógico (TRUE, FALSE, NULL)v_activo BOOLEAN := TRUE;
CLOBCharacter Large Objectv_texto_largo CLOB;
BLOBBinary Large Objectv_datos_binarios BLOB;
%TYPEHereda el tipo de una columna o variablev_sal empleados.salario%TYPE;
%ROWTYPEHereda la estructura de una tabla o cursorv_emp empleados%ROWTYPE;
DECLARE
v_nota NUMBER := 75;
v_resultado VARCHAR2(20);
BEGIN
IF v_nota >= 90 THEN
v_resultado := 'Sobresaliente';
ELSIF v_nota >= 70 THEN
v_resultado := 'Notable';
ELSIF v_nota >= 50 THEN
v_resultado := 'Aprobado';
ELSE
v_resultado := 'Suspenso';
END IF;
DBMS_OUTPUT.PUT_LINE('Calificación: ' || v_resultado);
END;
/
DECLARE
v_contador NUMBER := 1;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE('Iteración: ' || v_contador);
v_contador := v_contador + 1;
EXIT WHEN v_contador > 5;
END LOOP;
END;
/
OperadorDescripciónEjemplo
+Sumaa + b
-Restaa - b
*Multiplicacióna * b
/Divisióna / b
**Potenciaa ** b
OperadorDescripciónEjemplo
=Iguala = b
<> o !=Distintoa <> b
<Menor quea < b
>Mayor quea > b
<=Menor o igual quea <= b
>=Mayor o igual quea >= b
IS NULLEs nuloa IS NULL
IS NOT NULLNo es nuloa IS NOT NULL
LIKECoincide con patróna LIKE ‘B%‘
BETWEENEntre dos valoresa BETWEEN 10 AND 20
INEn un conjunto de valoresa IN (1, 2, 3)
OperadorDescripciónEjemplo
ANDY lógicoa > 0 AND b > 0
ORO lógicoa > 0 OR b > 0
NOTNegaciónNOT (a = b)

El manejo de excepciones en PL/SQL permite capturar y gestionar errores durante la ejecución, evitando que el programa termine abruptamente y proporcionando mecanismos para responder adecuadamente a situaciones inesperadas.

DECLARE
-- Declaraciones
BEGIN
-- Código ejecutable
EXCEPTION
WHEN excepcion1 THEN
-- Código para manejar excepcion1
WHEN excepcion2 THEN
-- Código para manejar excepcion2
WHEN OTHERS THEN
-- Código para manejar cualquier otra excepción
END;
/

Son excepciones que Oracle define automáticamente. Algunas de las más comunes:

ExcepciónCódigo OracleDescripción
NO_DATA_FOUNDORA-01403SELECT no devuelve filas
TOO_MANY_ROWSORA-01422SELECT devuelve más de una fila
ZERO_DIVIDEORA-01476División por cero
DUP_VAL_ON_INDEXORA-00001Violación de restricción única
VALUE_ERRORORA-06502Error aritmético, conversión o truncamiento
INVALID_NUMBERORA-01722Fallo al convertir cadena a número
CURSOR_ALREADY_OPENORA-06511Intento de abrir un cursor ya abierto
DECLARE
v_resultado NUMBER;
BEGIN
-- Intento de división por cero
v_resultado := 100 / 0;
DBMS_OUTPUT.PUT_LINE('Resultado: ' || v_resultado);
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('Error: División por cero');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error inesperado: ' || SQLERRM);
END;
/
FunciónDescripciónEjemplo
SQLCODEDevuelve el código de error OracleDBMS_OUTPUT.PUT_LINE(‘Código de error: ‘
SQLERRMDevuelve el mensaje de error asociadoDBMS_OUTPUT.PUT_LINE(‘Mensaje: ‘
DBMS_UTILITY.FORMAT_ERROR_STACKDevuelve la pila de errores completaDBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.FORMAT_ERROR_STACK);
DBMS_UTILITY.FORMAT_ERROR_BACKTRACEDevuelve la traza de la pila de llamadasDBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);

Las excepciones se propagan hacia arriba en la jerarquía de bloques PL/SQL si no son manejadas en el bloque donde se producen.

DECLARE
e_personalizada EXCEPTION;
-- Procedimiento anidado
PROCEDURE proc_interno IS
BEGIN
RAISE e_personalizada; -- Lanza la excepción
-- Esta línea nunca se ejecuta
DBMS_OUTPUT.PUT_LINE('Dentro del procedimiento interno');
EXCEPTION
WHEN NO_DATA_FOUND THEN -- No maneja e_personalizada
DBMS_OUTPUT.PUT_LINE('No se encontraron datos');
END;
BEGIN
-- Llamada al procedimiento
proc_interno;
-- Esta línea no se ejecuta si hay una excepción no manejada en proc_interno
DBMS_OUTPUT.PUT_LINE('Después de llamar al procedimiento');
EXCEPTION
WHEN e_personalizada THEN -- Maneja la excepción propagada
DBMS_OUTPUT.PUT_LINE('Excepción personalizada capturada en el bloque principal');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Otra excepción: ' || SQLERRM);
END;
/

Mejores prácticas para el manejo de excepciones

Section titled “Mejores prácticas para el manejo de excepciones”
  1. Especificidad: Maneje primero las excepciones específicas y luego las más generales.
  2. Registro: Registre información detallada sobre las excepciones para facilitar la depuración.
  3. Granularidad: Use bloques de excepción más pequeños para un manejo más preciso.
  4. Reutilización: Centralice el manejo de excepciones comunes en procedimientos o funciones.
  5. Información: Proporcione mensajes de error claros y útiles.
DECLARE
v_empleado_id empleados.empleado_id%TYPE := 999; -- ID que no existe
v_nombre empleados.nombre%TYPE;
-- Procedimiento para registrar errores
PROCEDURE registrar_error(
p_codigo IN NUMBER,
p_mensaje IN VARCHAR2,
p_procedimiento IN VARCHAR2
) IS
BEGIN
INSERT INTO log_errores (codigo, mensaje, procedimiento, fecha)
VALUES (p_codigo, p_mensaje, p_procedimiento, SYSDATE);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
-- Si falla el registro, al menos mostrar en consola
DBMS_OUTPUT.PUT_LINE('Error al registrar el error: ' || SQLERRM);
END;
BEGIN
-- Intentar obtener datos de un empleado
BEGIN
SELECT nombre INTO v_nombre
FROM empleados
WHERE empleado_id = v_empleado_id;
DBMS_OUTPUT.PUT_LINE('Nombre del empleado: ' || v_nombre);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No se encontró el empleado con ID ' || v_empleado_id);
registrar_error(-20001, 'Empleado no encontrado: ' || v_empleado_id, 'CONSULTA_EMPLEADO');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('Se encontraron múltiples empleados con el mismo ID');
registrar_error(-20002, 'Múltiples empleados con ID ' || v_empleado_id, 'CONSULTA_EMPLEADO');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error inesperado: ' || SQLERRM);
registrar_error(SQLCODE, SQLERRM, 'CONSULTA_EMPLEADO');
END;
-- Continuar con otras operaciones
DBMS_OUTPUT.PUT_LINE('Continuando con el proceso...');
END;
/

Los cursores en PL/SQL son estructuras que permiten procesar múltiples filas devueltas por una consulta SQL. Oracle proporciona dos tipos de cursores: implícitos y explícitos.

Oracle crea automáticamente un cursor implícito cada vez que se ejecuta una sentencia SQL que no devuelve explícitamente un conjunto de resultados a un cursor definido por el usuario.

Se accede a ellos mediante la palabra clave SQL:

AtributoDescripciónEjemplo
%FOUNDTRUE si la última sentencia SQL afectó al menos una filaIF SQL%FOUND THEN …
%NOTFOUNDTRUE si la última sentencia SQL no afectó ninguna filaIF SQL%NOTFOUND THEN …
%ROWCOUNTNúmero de filas afectadas por la última sentencia SQLDBMS_OUTPUT.PUT_LINE(‘Filas afectadas: ‘
%ISOPENSiempre FALSE para cursores implícitos (Oracle los cierra automáticamente)IF SQL%ISOPEN THEN … — Siempre FALSE
BEGIN
-- Actualizar salarios
UPDATE empleados
SET salario = salario * 1.10
WHERE departamento_id = 20;
-- Usar atributos de cursor implícito
IF SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE('Se actualizaron ' || SQL%ROWCOUNT || ' empleados');
ELSE
DBMS_OUTPUT.PUT_LINE('No se actualizó ningún empleado');
END IF;
-- Eliminar departamentos sin empleados
DELETE FROM departamentos
WHERE departamento_id NOT IN (SELECT departamento_id FROM empleados);
DBMS_OUTPUT.PUT_LINE('Departamentos eliminados: ' || SQL%ROWCOUNT);
COMMIT;
END;
/

Los cursores explícitos son declarados por el programador para procesar múltiples filas devueltas por una consulta. Permiten un mayor control sobre el procesamiento de los resultados.

  1. Declaración: Definir el cursor y su consulta SQL asociada
  2. Apertura: Ejecutar la consulta y preparar el conjunto de resultados
  3. Recuperación: Obtener las filas una por una
  4. Cierre: Liberar los recursos del cursor
DECLARE
-- 1. Declaración del cursor
CURSOR c_empleados IS
SELECT empleado_id, nombre, salario
FROM empleados
WHERE departamento_id = 10
ORDER BY salario DESC;
-- Variables para almacenar los datos del cursor
v_empleado_id empleados.empleado_id%TYPE;
v_nombre empleados.nombre%TYPE;
v_salario empleados.salario%TYPE;
BEGIN
-- 2. Apertura del cursor
OPEN c_empleados;
-- 3. Recuperación de datos
LOOP
FETCH c_empleados INTO v_empleado_id, v_nombre, v_salario;
EXIT WHEN c_empleados%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Empleado: ' || v_nombre || ', Salario: ' || v_salario);
END LOOP;
-- 4. Cierre del cursor
CLOSE c_empleados;
END;
/
AtributoDescripciónEjemplo
%FOUNDTRUE si la última operación FETCH recuperó una filaIF c_empleados%FOUND THEN …
%NOTFOUNDTRUE si la última operación FETCH no recuperó ninguna filaEXIT WHEN c_empleados%NOTFOUND;
%ROWCOUNTNúmero de filas recuperadas hasta el momentoDBMS_OUTPUT.PUT_LINE(‘Filas procesadas: ‘
%ISOPENTRUE si el cursor está abiertoIF NOT c_empleados%ISOPEN THEN OPEN c_empleados; END IF;

Permiten bloquear las filas seleccionadas para evitar que otros usuarios las modifiquen mientras se procesan.

DECLARE
CURSOR c_empleados_update IS
SELECT empleado_id, nombre, salario
FROM empleados
WHERE departamento_id = 20
FOR UPDATE;
v_aumento NUMBER := 1.15; -- 15% de aumento
BEGIN
FOR emp_rec IN c_empleados_update LOOP
-- Actualizar el salario de cada empleado
UPDATE empleados
SET salario = emp_rec.salario * v_aumento
WHERE CURRENT OF c_empleados_update;
DBMS_OUTPUT.PUT_LINE('Actualizado: ' || emp_rec.nombre || ', Nuevo salario: ' ||
(emp_rec.salario * v_aumento));
END LOOP;
COMMIT;
END;
/

Cursores con variables de enlace (bind variables)

Section titled “Cursores con variables de enlace (bind variables)”
DECLARE
-- Variables de enlace
v_dept_id NUMBER := 10;
v_min_sal NUMBER := 5000;
-- Cursor que utiliza las variables de enlace
CURSOR c_empleados(p_dept_id NUMBER, p_min_sal NUMBER) IS
SELECT empleado_id, nombre, salario
FROM empleados
WHERE departamento_id = p_dept_id
AND salario >= p_min_sal
ORDER BY salario DESC;
-- Registro para almacenar los datos del cursor
TYPE r_empleado IS RECORD (
empleado_id empleados.empleado_id%TYPE,
nombre empleados.nombre%TYPE,
salario empleados.salario%TYPE
);
v_empleado r_empleado;
v_contador NUMBER := 0;
BEGIN
OPEN c_empleados(v_dept_id, v_min_sal);
LOOP
FETCH c_empleados INTO v_empleado;
EXIT WHEN c_empleados%NOTFOUND;
v_contador := v_contador + 1;
DBMS_OUTPUT.PUT_LINE(v_contador || '. ' || v_empleado.nombre ||
' (ID: ' || v_empleado.empleado_id ||
', Salario: ' || v_empleado.salario || ')');
END LOOP;
IF v_contador = 0 THEN
DBMS_OUTPUT.PUT_LINE('No se encontraron empleados con salario >= ' ||
v_min_sal || ' en el departamento ' || v_dept_id);
ELSE
DBMS_OUTPUT.PUT_LINE('Total de empleados encontrados: ' || v_contador);
END IF;
CLOSE c_empleados;
END;
/

Mejores prácticas para el uso de cursores

Section titled “Mejores prácticas para el uso de cursores”
  1. Preferir cursores FOR LOOP cuando sea posible, ya que manejan automáticamente la apertura, recuperación y cierre.
  2. Limitar el conjunto de resultados con cláusulas WHERE adecuadas para mejorar el rendimiento.
  3. Usar parámetros en cursores para hacerlos más flexibles y reutilizables.
  4. Cerrar siempre los cursores explícitos cuando ya no se necesiten para liberar recursos.
  5. Considerar el uso de cursores con FOR UPDATE solo cuando sea necesario bloquear filas.
  6. Utilizar %ROWTYPE para simplificar la declaración de variables que almacenarán los resultados del cursor.

Los paquetes son objetos de esquema que agrupan tipos de datos, variables, constantes, cursores, excepciones, procedimientos y funciones relacionados en una única unidad lógica. Proporcionan modularidad, encapsulamiento, reutilización y un mejor rendimiento en aplicaciones PL/SQL.

Un paquete consta de dos partes:

  1. Especificación del paquete: Define la interfaz pública (elementos accesibles desde fuera del paquete)
  2. Cuerpo del paquete: Implementa los elementos definidos en la especificación y puede contener elementos privados adicionales
CREATE OR REPLACE PACKAGE gestion_empleados AS
-- Constantes públicas
c_aumento_minimo CONSTANT NUMBER := 0.05; -- 5%
c_aumento_maximo CONSTANT NUMBER := 0.20; -- 20%
-- Tipos públicos
TYPE t_registro_empleado IS RECORD (
id NUMBER,
nombre VARCHAR2(100),
salario NUMBER
);
-- Cursores públicos
CURSOR c_empleados_dept(p_dept_id NUMBER) IS
SELECT empleado_id, nombre, salario
FROM empleados
WHERE departamento_id = p_dept_id
ORDER BY nombre;
-- Excepciones públicas
e_salario_invalido EXCEPTION;
-- Funciones públicas
FUNCTION calcular_aumento(
p_salario IN NUMBER,
p_porcentaje IN NUMBER DEFAULT c_aumento_minimo
) RETURN NUMBER;
FUNCTION obtener_empleado(
p_empleado_id IN NUMBER
) RETURN t_registro_empleado;
-- Procedimientos públicos
PROCEDURE actualizar_salario(
p_empleado_id IN NUMBER,
p_porcentaje IN NUMBER,
p_comentario IN VARCHAR2 DEFAULT NULL
);
PROCEDURE transferir_empleado(
p_empleado_id IN NUMBER,
p_nuevo_dept_id IN NUMBER
);
END gestion_empleados;
/
-- Usar elementos del paquete
DECLARE
v_empleado gestion_empleados.t_registro_empleado;
v_nuevo_salario NUMBER;
BEGIN
-- Llamar a una función del paquete
v_empleado := gestion_empleados.obtener_empleado(101);
DBMS_OUTPUT.PUT_LINE('Empleado: ' || v_empleado.nombre);
DBMS_OUTPUT.PUT_LINE('Salario actual: ' || v_empleado.salario);
-- Calcular nuevo salario con un aumento del 10%
v_nuevo_salario := gestion_empleados.calcular_aumento(
v_empleado.salario, 0.10);
DBMS_OUTPUT.PUT_LINE('Nuevo salario: ' || v_nuevo_salario);
-- Llamar a un procedimiento del paquete
gestion_empleados.actualizar_salario(
v_empleado.id, 0.10, 'Aumento anual');
-- Usar un cursor del paquete
FOR emp_rec IN gestion_empleados.c_empleados_dept(20) LOOP
DBMS_OUTPUT.PUT_LINE('Empleado del dept 20: ' || emp_rec.nombre);
END LOOP;
-- Manejar una excepción del paquete
BEGIN
gestion_empleados.actualizar_salario(102, 0.30); -- Excede el máximo
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
END;
END;
/
  1. Modularidad: Agrupan lógicamente objetos relacionados, facilitando el diseño y mantenimiento.
  2. Encapsulamiento: Ocultan detalles de implementación y exponen solo la interfaz necesaria.
  3. Rendimiento: Los paquetes se cargan en memoria una sola vez y permanecen allí para todos los usuarios.
  4. Seguridad: Permiten controlar el acceso a los objetos mediante permisos granulares.
  5. Sobrecarga: Permiten definir múltiples subprogramas con el mismo nombre pero diferentes parámetros.
  6. Estado persistente: Mantienen variables y cursores en memoria entre llamadas a procedimientos.
  1. Agrupar funcionalidad relacionada: Diseñar paquetes con un propósito claro y coherente.
  2. Minimizar dependencias: Reducir las dependencias entre paquetes para facilitar el mantenimiento.
  3. Documentar la interfaz: Incluir comentarios detallados sobre el propósito y uso de cada elemento público.
  4. Gestionar el estado con cuidado: Ser consciente de que las variables de paquete mantienen su estado entre llamadas.
  5. Manejar errores adecuadamente: Implementar manejo de excepciones robusto en todos los subprogramas.
  6. Usar inicialización de paquetes: Aprovechar el bloque de inicialización para configurar el estado inicial.

Oracle proporciona numerosos paquetes del sistema que ofrecen funcionalidad útil para los desarrolladores:

PaqueteDescripciónEjemplo de uso
DBMS_OUTPUTMuestra mensajes desde PL/SQLDBMS_OUTPUT.PUT_LINE(‘Mensaje’);
DBMS_SQLPermite ejecutar SQL dinámicov_cursor := DBMS_SQL.OPEN_CURSOR;
UTL_FILEOperaciones de lectura/escritura de archivosUTL_FILE.FOPEN(‘DIR’, ‘archivo.txt’, ‘W’);
DBMS_SCHEDULERProgramación de tareasDBMS_SCHEDULER.CREATE_JOB(…);
DBMS_CRYPTOFunciones criptográficasv_hash := DBMS_CRYPTO.HASH(v_input, DBMS_CRYPTO.HASH_SH1);
UTL_MAILEnvío de correos electrónicosUTL_MAIL.SEND(sender, recipients, subject, message);
DBMS_LOBManipulación de objetos grandes (LOBs)DBMS_LOB.WRITE(v_clob, v_amount, v_offset, v_buffer);
-- Ejemplo de uso de paquetes del sistema
BEGIN
-- DBMS_OUTPUT para mostrar mensajes
DBMS_OUTPUT.PUT_LINE('Inicio del proceso');
-- UTL_FILE para escribir en un archivo
DECLARE
v_file UTL_FILE.FILE_TYPE;
BEGIN
v_file := UTL_FILE.FOPEN('MI_DIRECTORIO', 'reporte.txt', 'W');
UTL_FILE.PUT_LINE(v_file, 'Reporte generado el ' || TO_CHAR(SYSDATE, 'DD-MON-YYYY'));
UTL_FILE.FCLOSE(v_file);
END;
-- DBMS_SCHEDULER para programar una tarea
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'ACTUALIZAR_ESTADISTICAS',
job_type => 'STORED_PROCEDURE',
job_action => 'mi_paquete.actualizar_estadisticas',
start_date => SYSTIMESTAMP,
repeat_interval => 'FREQ=DAILY; BYHOUR=2',
enabled => TRUE,
comments => 'Actualiza estadísticas diariamente a las 2 AM'
);
DBMS_OUTPUT.PUT_LINE('Fin del proceso');
END;
/

En este capítulo hemos explorado los fundamentos de la programación con PL/SQL en Oracle, incluyendo:

  1. Introducción a PL/SQL: Estructura básica, tipos de bloques y ejecución.
  2. Variables y estructuras de control: Declaración de variables, tipos de datos, estructuras condicionales e iterativas.
  3. Manejo de excepciones: Tipos de excepciones, propagación y mejores prácticas.
  4. Cursores explícitos e implícitos: Ciclo de vida, atributos y técnicas de uso.
  5. Paquetes: Estructura, ventajas y mejores prácticas.

PL/SQL es una herramienta poderosa para desarrollar lógica de negocio compleja directamente en la base de datos Oracle, aprovechando la proximidad a los datos para lograr un mejor rendimiento y seguridad.

🐝