[SQL 15] Procedimientos, funciones y triggers
- Leer y escribir un procedimiento almacenado en MySQL (
DELIMITER, CREATE PROCEDURE, IN, CALL) y su equivalente en Oracle PL/SQL. - Distinguir procedimiento, función y trigger.
- Crear triggers
BEFORE / AFTER INSERT / UPDATE / DELETEusandoNEWyOLD. - Implementar auditoría y validaciones automáticas con triggers.
Las bases de datos permiten guardar lógica junto a los datos:
- Procedimiento almacenado: un bloque de sentencias con nombre y parámetros que se ejecuta con
CALL(MySQL) oEXEC. Ideal para operaciones repetitivas del negocio: «actualiza el precio del SKU 105 a 5000». - Función: como un procedimiento, pero devuelve un valor y se usa dentro de un SELECT.
- Trigger (disparador): se ejecuta automáticamente cuando ocurre un INSERT, UPDATE o DELETE sobre una
tabla. Dentro del trigger,
NEWes la fila nueva yOLDla fila anterior.
Sobre este motor: SQLite no tiene procedimientos almacenados (está pensado para ir incrustado en una aplicación), pero sí tiene triggers. Por eso los procedimientos se muestran con la sintaxis de MySQL y Oracle, la que usas en Workbench y en la evaluación, y los triggers se practican aquí con código que se ejecuta de verdad.
-- Trigger (SQLite / estándar) CREATE TRIGGER nombre AFTER UPDATE OF columna ON tabla FOR EACH ROW WHEN NEW.columna <> OLD.columna BEGIN INSERT INTO auditoria VALUES (OLD.id, OLD.columna, NEW.columna); END;
IN / OUT / INOUT; se ejecuta con CALL.; dentro del cuerpo.RAISE(ABORT, 'msg') cancela; RAISE(IGNORE) descarta la fila. MySQL: SIGNAL.1 · Ejemplos guiados · ejecuta y modifica cada bloque
sqlite_master guarda la definición de cada objeto (en MySQL: SHOW TRIGGERS).Sintaxis en otros motores (referencia, no se ejecuta aquí)
DELIMITER //
CREATE PROCEDURE ActualizarPrecioSimple(
IN codigo_sku_buscado INT,
IN nuevo_precio INT
)
BEGIN
UPDATE Producto
SET Precio = nuevo_precio
WHERE CodigoSku = codigo_sku_buscado;
END //
DELIMITER ;
CALL ActualizarPrecioSimple(105, 5000);
DELIMITER //
CREATE PROCEDURE AumentarStock(IN sku_producto INT, IN cantidad_nueva INT)
BEGIN
UPDATE Producto
SET Cantidad = Cantidad + cantidad_nueva
WHERE CodigoSku = sku_producto;
END //
DELIMITER ;
CALL AumentarStock(200, 5);
OUT devuelve un resultado a quien llama; @total es una variable de sesión.DELIMITER //
CREATE PROCEDURE ContarEmpleadosDepto(IN p_depto INT, OUT p_total INT)
BEGIN
SELECT COUNT(*) INTO p_total
FROM employees
WHERE department_id = p_depto;
END //
DELIMITER ;
CALL ContarEmpleadosDepto(60, @total);
SELECT @total;
CREATE OR REPLACE PROCEDURE reajustar_depto (
p_depto IN NUMBER,
p_pct IN NUMBER
) AS
BEGIN
UPDATE employees
SET salary = salary * (1 + p_pct / 100)
WHERE department_id = p_depto;
COMMIT;
END;
/
EXEC reajustar_depto(60, 10);
DELIMITER //
CREATE FUNCTION salario_anual(p_salary DECIMAL(10,2))
RETURNS DECIMAL(12,2) DETERMINISTIC
BEGIN
RETURN p_salary * 12;
END //
DELIMITER ;
SELECT first_name, salario_anual(salary) FROM employees;
⚠ Errores frecuentes
- Olvidar
DELIMITERen MySQL: el primer;del cuerpo corta la sentencia CREATE PROCEDURE. - Un trigger que modifica la misma tabla que lo dispara puede encadenarse sin fin: acota con WHEN y WHERE.
- Esconder demasiada lógica de negocio en triggers: se vuelve difícil de seguir y de depurar. Documenta cada uno.
- Confundir
NEWyOLD: en un DELETE no existe NEW; en un INSERT no existe OLD.
2 · Retos con corrección automática
Escribe tu solución y pulsa comprobar (Ctrl + Enter). Tu consulta y la solución se ejecutan en bases limpias y se comparan los resultados: no importan los nombres de las columnas, sí los datos (y el orden, cuando el reto lo pide). Si te atascas, usa la pista o mira el resultado esperado.
3 · Preguntas tipo solemne
DELIMITER // antes de crear un procedimiento?AumentarStock para sumar 5 unidades al SKU 200?BEFORE UPDATE, ¿qué contiene OLD.salary?