Skip to content

12. Objetos Avanzados de Base de Datos

Las secuencias son objetos de base de datos que generan números enteros únicos en orden ascendente o descendente. Son comúnmente utilizadas para crear valores de clave primaria de forma automática.

Una secuencia es un objeto de esquema que genera valores numéricos secuenciales según una especificación definida. Las secuencias simplifican la programación de aplicaciones al generar automáticamente valores únicos, eliminando la necesidad de escribir código para esta tarea.

La sintaxis básica para crear una secuencia es:

CREATE SEQUENCE nombre_secuencia
[INCREMENT BY n]
[START WITH n]
[MAXVALUE n | NOMAXVALUE]
[MINVALUE n | NOMINVALUE]
[CYCLE | NOCYCLE]
[CACHE n | NOCACHE]
[ORDER | NOORDER];

Define el incremento entre números consecutivos. Puede ser positivo (secuencia ascendente) o negativo (secuencia descendente).

-- Secuencia que incrementa de 10 en 10
CREATE SEQUENCE seq_decenas
INCREMENT BY 10
START WITH 10;

Para obtener el siguiente valor de una secuencia, se utiliza la pseudocolumna NEXTVAL. Para obtener el valor actual (el último valor generado en la sesión actual), se utiliza CURRVAL.

-- Obtener el siguiente valor
SELECT seq_empleados.NEXTVAL FROM dual;
-- Obtener el valor actual
SELECT seq_empleados.CURRVAL FROM dual;
-- Insertar un nuevo empleado con ID generado automáticamente
INSERT INTO empleados (empleado_id, nombre, salario)
VALUES (seq_empleados.NEXTVAL, 'Juan Pérez', 3000);
-- Asignar números secuenciales a resultados de consulta
SELECT seq_numeracion.NEXTVAL AS num, nombre
FROM empleados;
-- Ver todas las secuencias del esquema actual
SELECT sequence_name, min_value, max_value, increment_by, last_number, cache_size, cycle_flag
FROM user_sequences;
-- Modificar una secuencia existente
ALTER SEQUENCE seq_empleados
INCREMENT BY 5
MAXVALUE 10000
NOCACHE;
-- Eliminar una secuencia
DROP SEQUENCE seq_empleados;
  1. Huecos en la secuencia: Pueden producirse huecos en los valores generados debido a rollbacks de transacciones, fallos del sistema o valores almacenados en caché que no se utilizan.

  2. Rendimiento: El uso de CACHE mejora significativamente el rendimiento al reducir los accesos al diccionario de datos.

  3. Concurrencia: Las secuencias están diseñadas para entornos de alta concurrencia, ya que no requieren bloqueos para generar nuevos valores.

  4. Valores no recuperables: Una vez que se genera un valor de secuencia, no se puede recuperar, incluso si la transacción que lo utilizó se revierte.

Ejemplo práctico: Sistema de facturación

Section titled “Ejemplo práctico: Sistema de facturación”
-- Crear una secuencia para números de factura
CREATE SEQUENCE seq_num_factura
START WITH 10000
INCREMENT BY 1
MINVALUE 10000
MAXVALUE 999999
NOCYCLE
CACHE 20;
-- Procedimiento para generar una nueva factura
CREATE OR REPLACE PROCEDURE crear_factura (
p_cliente_id IN NUMBER,
p_fecha IN DATE,
p_factura_id OUT NUMBER
) AS
BEGIN
-- Obtener nuevo número de factura
SELECT seq_num_factura.NEXTVAL INTO p_factura_id FROM dual;
-- Crear la factura con el ID generado
INSERT INTO facturas (
factura_id,
cliente_id,
fecha,
estado
) VALUES (
p_factura_id,
p_cliente_id,
p_fecha,
'PENDIENTE'
);
-- Registrar la creación de la factura
INSERT INTO log_sistema (mensaje)
VALUES ('Factura ' || p_factura_id || ' creada para cliente ' || p_cliente_id);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
/
-- Ejemplo de uso del procedimiento
DECLARE
v_factura_id NUMBER;
BEGIN
crear_factura(101, SYSDATE, v_factura_id);
DBMS_OUTPUT.PUT_LINE('Factura creada con ID: ' || v_factura_id);
END;
/

Desde Oracle 12c, se introdujo la funcionalidad de columnas de identidad (IDENTITY COLUMNS) como una alternativa a las secuencias tradicionales.

-- Crear secuencia
CREATE SEQUENCE seq_productos START WITH 1;
-- Crear tabla que usa la secuencia
CREATE TABLE productos (
producto_id NUMBER PRIMARY KEY,
nombre VARCHAR2(100),
precio NUMBER(10,2)
);
-- Insertar usando la secuencia
INSERT INTO productos (producto_id, nombre, precio)
VALUES (seq_productos.NEXTVAL, 'Laptop', 1200);
CaracterísticaSecuenciasColumnas de identidad
IndependenciaObjeto independiente de la tablaVinculada directamente a la tabla
ReutilizaciónPuede usarse en múltiples tablasEspecífica para una columna
ControlMayor control sobre la generación de valoresConfiguración más simple
Sintaxis INSERTRequiere NEXTVAL explícitoNo requiere especificar la columna
PersonalizaciónAltamente personalizableOpciones más limitadas

Los triggers o disparadores son objetos de base de datos que se ejecutan automáticamente en respuesta a ciertos eventos sobre una tabla o vista. Permiten implementar reglas de negocio complejas, mantener la integridad de los datos y automatizar tareas.

Un trigger es un bloque de código PL/SQL asociado a una tabla o vista que se activa automáticamente cuando ocurre un evento específico, como una operación INSERT, UPDATE o DELETE.

Se ejecuta una vez por cada fila afectada por la operación desencadenante.

CREATE OR REPLACE TRIGGER tr_actualizar_stock
AFTER INSERT ON pedidos_detalle
FOR EACH ROW -- Trigger de fila
BEGIN
-- Actualizar el stock por cada fila insertada
UPDATE productos
SET stock = stock - :NEW.cantidad
WHERE producto_id = :NEW.producto_id;
END;
/

Se ejecuta antes de que ocurra la operación desencadenante. Útil para validar o modificar datos antes de que se guarden.

CREATE OR REPLACE TRIGGER tr_validar_precio
BEFORE INSERT OR UPDATE OF precio ON productos
FOR EACH ROW
BEGIN
-- Validar que el precio no sea negativo
IF :NEW.precio < 0 THEN
:NEW.precio := 0; -- Corregir el valor antes de insertar/actualizar
END IF;
END;
/

Se activa cuando se insertan nuevas filas.

CREATE OR REPLACE TRIGGER tr_nuevo_cliente
AFTER INSERT ON clientes
FOR EACH ROW
BEGIN
-- Enviar mensaje de bienvenida al nuevo cliente
INSERT INTO mensajes_clientes (cliente_id, mensaje, fecha)
VALUES (:NEW.cliente_id, 'Bienvenido a nuestra plataforma', SYSDATE);
END;
/

En triggers que manejan múltiples eventos, se pueden usar los predicados INSERTING, UPDATING y DELETING para determinar qué evento activó el trigger.

CREATE OR REPLACE TRIGGER tr_control_inventario
AFTER INSERT OR UPDATE OR DELETE ON pedidos_detalle
FOR EACH ROW
BEGIN
IF INSERTING THEN
-- Reducir stock al crear un nuevo detalle de pedido
UPDATE productos
SET stock = stock - :NEW.cantidad
WHERE producto_id = :NEW.producto_id;
ELSIF UPDATING THEN
-- Ajustar stock al modificar un detalle de pedido
UPDATE productos
SET stock = stock + :OLD.cantidad - :NEW.cantidad
WHERE producto_id = :NEW.producto_id;
ELSIF DELETING THEN
-- Aumentar stock al eliminar un detalle de pedido
UPDATE productos
SET stock = stock + :OLD.cantidad
WHERE producto_id = :OLD.producto_id;
END IF;
END;
/

En los triggers de fila, se pueden usar las variables de correlación :NEW y :OLD para acceder a los valores de la fila antes y después de la operación.

VariableDescripciónINSERTUPDATEDELETE
:NEWNuevos valores de la filaDisponibleDisponibleNo disponible
:OLDValores anteriores de la filaNo disponibleDisponibleDisponible

Permite especificar una condición adicional para la activación del trigger.

CREATE OR REPLACE TRIGGER tr_alerta_stock_bajo
AFTER UPDATE OF stock ON productos
FOR EACH ROW
WHEN (NEW.stock < NEW.stock_minimo)
BEGIN
-- Generar alerta solo cuando el stock cae por debajo del mínimo
INSERT INTO alertas (producto_id, mensaje, fecha)
VALUES (:NEW.producto_id, 'Stock por debajo del mínimo', SYSDATE);
END;
/
--- Ver todos los triggers del esquema actual
SELECT trigger_name, trigger_type, triggering_event, table_name, status
FROM user_triggers;
--- Deshabilitar un trigger
ALTER TRIGGER tr_actualizar_stock DISABLE;
--- Habilitar un trigger
ALTER TRIGGER tr_actualizar_stock ENABLE;
--- Deshabilitar todos los triggers de una tabla
ALTER TABLE pedidos_detalle DISABLE ALL TRIGGERS;
--- Habilitar todos los triggers de una tabla
ALTER TABLE pedidos_detalle ENABLE ALL TRIGGERS;
--- Eliminar un trigger
DROP TRIGGER tr_actualizar_stock;

Mejores prácticas para el uso de triggers

Section titled “Mejores prácticas para el uso de triggers”
  1. Mantener los triggers simples: Evitar lógica compleja que pueda afectar el rendimiento.
  2. Documentar claramente: Incluir comentarios que expliquen el propósito y comportamiento del trigger.
  3. Evitar triggers en cascada: Limitar situaciones donde un trigger activa otro trigger.
  4. Manejar excepciones: Incluir manejo de errores adecuado para evitar fallos en la transacción.
  5. Considerar el rendimiento: Recordar que los triggers se ejecutan por cada operación, lo que puede afectar el rendimiento en operaciones masivas.
  6. Evitar operaciones DML recursivas: Tener cuidado con triggers que realizan operaciones DML sobre la misma tabla que los activó.
  1. Auditoría: Registrar quién realizó cambios en los datos y cuándo.
  2. Validación de datos: Aplicar reglas de negocio complejas que no pueden implementarse con restricciones estándar.
  3. Valores derivados: Calcular automáticamente valores basados en otros campos.
  4. Integridad referencial: Implementar reglas de integridad más complejas que las restricciones de clave foránea estándar.
  5. Replicación: Mantener datos sincronizados entre tablas o bases de datos.
  6. Notificaciones: Generar alertas o notificaciones basadas en cambios en los datos.
--- Tabla para almacenar registros de auditoría
CREATE TABLE auditoria_datos (
auditoria_id NUMBER PRIMARY KEY,
tabla VARCHAR2(30),
operacion VARCHAR2(10),
id_registro NUMBER,
columna_modificada VARCHAR2(30),
valor_antiguo VARCHAR2(4000),
valor_nuevo VARCHAR2(4000),
usuario VARCHAR2(30),
fecha_hora TIMESTAMP,
terminal VARCHAR2(50)
);
--- Secuencia para la tabla de auditoría
CREATE SEQUENCE seq_auditoria_id START WITH 1 INCREMENT BY 1;
--- Trigger para auditar cambios en la tabla EMPLEADOS
CREATE OR REPLACE TRIGGER tr_auditoria_empleados
AFTER INSERT OR UPDATE OR DELETE ON empleados
FOR EACH ROW
DECLARE
v_operacion VARCHAR2(10);
v_terminal VARCHAR2(50);
BEGIN
-- Determinar la operación realizada
IF INSERTING THEN
v_operacion := 'INSERT';
ELSIF UPDATING THEN
v_operacion := 'UPDATE';
ELSIF DELETING THEN
v_operacion := 'DELETE';
END IF;
-- Obtener información del terminal
SELECT SYS_CONTEXT('USERENV', 'TERMINAL') INTO v_terminal FROM dual;
-- Auditar cambios en columnas específicas
IF INSERTING OR UPDATING THEN
-- Auditar cambios en el nombre
IF UPDATING AND :OLD.nombre != :NEW.nombre OR INSERTING THEN
INSERT INTO auditoria_datos VALUES (
seq_auditoria_id.NEXTVAL,
'EMPLEADOS',
v_operacion,
:NEW.empleado_id,
'NOMBRE',
CASE WHEN UPDATING THEN :OLD.nombre ELSE NULL END,
:NEW.nombre,
USER,
SYSTIMESTAMP,
v_terminal
);
END IF;
-- Auditar cambios en el salario
IF UPDATING AND :OLD.salario != :NEW.salario OR INSERTING THEN
INSERT INTO auditoria_datos VALUES (
seq_auditoria_id.NEXTVAL,
'EMPLEADOS',
v_operacion,
:NEW.empleado_id,
'SALARIO',
CASE WHEN UPDATING THEN TO_CHAR(:OLD.salario) ELSE NULL END,
TO_CHAR(:NEW.salario),
USER,
SYSTIMESTAMP,
v_terminal
);
END IF;
ELSIF DELETING THEN
-- Registrar eliminación de empleado
INSERT INTO auditoria_datos VALUES (
seq_auditoria_id.NEXTVAL,
'EMPLEADOS',
v_operacion,
:OLD.empleado_id,
'REGISTRO',
'EMPLEADO COMPLETO',
NULL,
USER,
SYSTIMESTAMP,
v_terminal
);
END IF;
END;
🐝