PL‐SQL - Irene-Frias/1DAM GitHub Wiki

1. Introducción

PL/SQL (Procedural Language/SQL) es una extensión de SQL desarrollada por Oracle que permite incorporar estructuras propias de un lenguaje de programación, como variables, bucles, condicionales y subprogramas, directamente en el motor de base de datos.

Esto permite:

  • Automatizar operaciones complejas.
  • Encapsular lógica de negocio dentro de la base de datos.
  • Reutilizar código mediante procedimientos, funciones y paquetes.
  • Mejorar la seguridad y el mantenimiento del sistema.

PL/SQL se utiliza lo siguiente:

  • Procedimientos almacenados
  • Funciones definidas por el usuario
  • Triggers (disparadores)
  • Paquetes
  • Bloques anónimos

Uso del usuario y permisos siguientes:

  • Oracle no crea múltiples bases de datos dentro de una misma instancia como lo hace MySQL. En su lugar, Oracle tiene una base de datos por instancia, y dentro de esa base de datos se crean usuarios (también llamados "schemas") que contienen objetos como tablas, vistas, procedimientos,...

  • Los permisos que le asignaremos al usuario serán todos, pero le quitaremos los siguientes permisos ADMINISTER RESOURCE MANAGER, KEEP DATE TIME, KEEP SYSGUID, SYSBACKUP, SYSDBA, SYSDG, SYSKM, SYSOPER, SYSRAC

  • Por tanto, si quieres aislar conjuntos de objetos y datos, lo correcto es crear un usuario por "esquema" o conjunto funcional de objetos.

ALTER SESSION SET "_ORACLE_SCRIPT" = true;

2. Lenguaje de programación

2.1 Tipos de guiones

  • Bloques anónimos: fragmentos de código que se ejecutan directamente en herramientas como SQL*Plus o SQL Developer. No se almacenan en la base de datos.
BEGIN
   DBMS_OUTPUT.PUT_LINE('Hola mundo');
END;
  • Subprogramas almacenados: guardados en la base de datos como procedimientos, funciones o eventos. De esta forma se guardan en la base de datos y se pueden reutilizar.

2.2 Características generales del lenguaje

  • Sintaxis basada en bloques: DECLAREBEGINEXCEPTIONEND.
  • Permite la declaración de variables locales y cursores.
  • Soporta estructuras de control como IF, WHILE, FOR.
  • Permite el tratamiento de errores mediante bloques EXCEPTION.
  • Puede combinar SQL estándar con lógica procedural.

2.3 Tipos de datos

PL/SQL ofrece tipos de datos similares a SQL, y también permite heredar tipos directamente de las columnas de una tabla con %TYPE Para definir las variables se declaran indicando el nombre seguido del tipo de dato, que puede ser:

  • Numéricos: INT, FLOAT, DECIMAL
  • Texto: CHAR, VARCHAR, TEXT
  • Fechas y tiempos: DATE, DATETIME, TIME, TIMESTAMP
  • Otros: BOOLEAN, BLOB
DECLARE
   edad NUMBER(3);
   nombre VARCHAR2(100);
   fecha_nac DATE := SYSDATE;
   activo BOOLEAN := TRUE;
BEGIN
   -- Bloque de código
END;

2.4 Identificadores

  • Nombres válidos para variables, funciones, etc.
  • No deben comenzar por números ni contener caracteres especiales (excepto guion bajo).
  • No se permiten caracteres especiales ni espacios.
    • Ejemplos válidos: v_total, cliente_id
    • Ejemplo inválido: 1nombre, cliente-id

2.5 Declaración de variables

Las variables deben declararse al inicio de un bloque. Se declaran en la sección DECLARE antes del BEGIN.

DECLARE total INT DEFAULT 0;
DECLARE mensaje VARCHAR(255);

2.6 Constantes y literales

  • Números: 100, 3.14
  • Cadenas: 'Texto'
  • Fechas: '2025-05-08'
  • Booleanos: TRUE, FALSE

2.7 Operadores

  • Aritméticos: +, -, *, /, %
  • Comparación: =, !=, <, >, <=, >=
  • Lógicos: AND, OR, NOT
  • Otros: IS NULL, BETWEEN, LIKE, IN

2.8 Funciones predefinidas

  • Cadena: LENGTH(), CONCAT(), SUBSTRING()
  • Numéricas: ROUND(), FLOOR(), ABS()
  • Fecha/Hora: NOW(), DATE_ADD(), CURDATE()
SELECT CONCAT('Hola ', 'mundo');

3. Estructuras de control

3.1 Configuración inicial

Al comenzar un fichero PL/SQL, es recomendable habilitar la visualización de mensajes enviados mediante DBMS_OUTPUT.PUT_LINE. Para ello, activamos la opción con:

SET SERVEROUTPUT ON;

Esto permite que los mensajes generados por el programa se muestren en pantalla.

También es útil desactivar la verificación de sustitución de variables, especialmente cuando se utilizan variables con el símbolo &. Al hacerlo, evitamos que se muestren mensajes como “old” y “new” durante la ejecución. Esto se logra con:

SET VERIFY OFF; 

A continuación se muestran una serie de estructuras en las que estará comprendido en el bloque de BEGIN y END. Además de un DECLARE o la creación de un procedimiento o función correspondiente, esto siendo opcional.

IF

IF total > 100 THEN
  SET descuento = 10;
ELSE
  SET descuento = 0;
END IF;

CASE

CASE estado
  WHEN 'A' THEN mensaje := 'Activo';
  WHEN 'I' THEN mensaje := 'Inactivo';
  ELSE mensaje := 'Desconocido';
END CASE;

WHILE

WHILE contador < 10 DO
  SET contador = contador + 1;
END WHILE;

REPEAT ... UNTIL

REPEAT
  SET intentos = intentos + 1;
UNTIL intentos = 3
END REPEAT;

LOOP

LOOP
  SET contador = contador + 1;
  IF contador >= 5 THEN
    LEAVE label;
  END IF;
END LOOP label;

4 Subprogramas

4.1 Procedimientos

Reutilizan lógica sin devolver valores, es decir que realizan acciones.

CREATE OR REPLACE PROCEDURE saludar 
IS
BEGIN
   DBMS_OUTPUT.PUT_LINE('Hola desde un procedimiento');
END;

EXECUTE IMMEDIATE se combina con las funciones básicas de las sentencias, en las que puede utilizarse para preparar y ejecutar sentencias de SQL que no contengan variables del lenguaje. Pero no es una sentencia de consulta, devuelven datos después de ejecutar las sentencias. Los ejemplos de uso de los datos son los siguientes:

  • Crear tablas dinámicamente con el nombre de una tabla basado en un valor y luego con la sintaxis CREATE TABLE [nombre_tabla]
  • Actualizar datos basados en criterios dinámicos en los que puede construir una sentencia UPDATE con un WHERE que dependa de los datos de entrada.
  • Ejecutar los procedimientos almacenados dinámicamente para construir una llamada al procedimiento almacenado como parámetro.
EXECUTE IMMEDIATE 'CREATE TABLE clienteBackup AS (SELECT * FROM cliente)';
EXECUTE IMMEDIATE 'CREATE TABLE empleadoBackup AS (SELECT * FROM empleado)';

4.2 Funciones

Devuelven un valor y se usan dentro de expresiones.

CREATE OR REPLACE FUNCTION doble(x IN NUMBER) 
    RETURN NUMBER -- No tiene porqué devolver algún valor
AS
BEGIN
   RETURN x * 2;
END;

5. Manejo de errores (excepciones)

Permite controlar errores mediante handlers:

BEGIN
   -- operación
EXCEPTION
   WHEN NO_DATA_FOUND THEN
      DBMS_OUTPUT.PUT_LINE('No se encontraron datos');
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
END;

Tipos de manejo:

  • CONTINUE: continúa la ejecución tras el error.
  • EXIT: termina el bloque actual al detectar el error.

6. Cursores

Permiten recorrer resultados de consultas SELECT: Cuando la consulta devuelve múltiples filas, para obtener la información sobre la ejecución del mismo o sobre los datos estos atributos pueden ser usados en PL/SQL, pero no en SQL.

  • %FOUND después de que el cursor esté abierto y antes de el primer FETCH en el que devuelve NULL. Después devolverá TRUE si en el último FETCH se ha devuelto una fila, y FALSE en caso contrario. Para cursores implícitos %FOUND devuelve TRUE si es un INSERT, UPDATE o DELETE, que afectan a una o más filas como puede ser un SELECT ... INTO ... que devuelve una o más.
  • %NOTFOUND es lo contrario a %FOUND
  • %ISOPEN evalúa la sentencia TRUE si el cursor está abierto y FALSE en caso contrario. Sin embargo para los cursores implícitos cierra automáticamente con %ISOPEN en el que evalúa siempre a FALSE
  • %ROWCOUNT para un cursor abierto y antes del primer FETCH, es el que evalúa la condición a 0. Después de cada FETCH %ROWCOUNT es incrementado y evalúa al número de filas afectadas por un INSERT, UPDATE o DELETE o el número de filas devueltas por un SELECT ... INTO ....

6.1 Cursor explícito

DECLARE
   CURSOR c IS SELECT nombre FROM empleados;
   v_nombre empleados.nombre%TYPE;
BEGIN
   OPEN c;
   LOOP
      FETCH c INTO v_nombre;
      EXIT WHEN c%NOTFOUND;
      DBMS_OUTPUT.PUT_LINE(v_nombre);
   END LOOP;
   CLOSE c;
END;

6.2 Cursor implícito

FOR emp IN (SELECT nombre FROM empleados) LOOP
   DBMS_OUTPUT.PUT_LINE(emp.nombre);
END LOOP;

7. Disparadores (triggers)

El código se ejecuta automáticamente ante los eventos INSERT, UPDATE, DELETE.

CREATE OR REPLACE TRIGGER trg_auditoria
BEFORE INSERT ON empleados
FOR EACH ROW
BEGIN
   :NEW.fecha_registro := SYSDATE;
END;
⚠️ **GitHub.com Fallback** ⚠️