SQL

Tutorial SQL: Creación y uso de funciones

Aprende a crear y utilizar funciones definidas por el usuario (UDF) en SQL para mejorar la lógica y reutilización en bases de datos MySQL y PostgreSQL.

Aprende SQL y certifícate

Funciones UDF

Las funciones definidas por el usuario (User-Defined Functions o UDF) son objetos de base de datos que encapsulan lógica personalizada y reutilizable dentro del entorno SQL. Estas funciones permiten extender las capacidades nativas del lenguaje SQL para resolver problemas específicos de negocio o simplificar operaciones complejas.

Concepto y propósito

Las funciones UDF actúan como bloques de código que aceptan parámetros, ejecutan una serie de instrucciones y devuelven un resultado. A diferencia de las funciones integradas de SQL (como COUNT, SUM o UPPER), las UDF son creadas por los desarrolladores para satisfacer necesidades particulares que no están cubiertas por la funcionalidad estándar.

Los principales beneficios de utilizar funciones UDF incluyen:

  • Reutilización de código: Escribir una vez, usar muchas veces
  • Encapsulación: Ocultar la complejidad detrás de una interfaz simple
  • Mantenimiento: Centralizar la lógica para facilitar actualizaciones
  • Legibilidad: Hacer que las consultas sean más claras y expresivas

Sintaxis básica para crear funciones UDF

La sintaxis para crear una función UDF varía ligeramente entre MySQL y PostgreSQL, pero mantiene una estructura general similar:

En MySQL:

CREATE FUNCTION nombre_funcion(parametro1 tipo1, parametro2 tipo2, ...)
RETURNS tipo_retorno
[características]
BEGIN
    -- Cuerpo de la función
    -- Lógica y operaciones
    RETURN valor;
END;

En PostgreSQL:

CREATE OR REPLACE FUNCTION nombre_funcion(parametro1 tipo1, parametro2 tipo2, ...)
RETURNS tipo_retorno AS
$$
BEGIN
    -- Cuerpo de la función
    -- Lógica y operaciones
    RETURN valor;
END;
$$ LANGUAGE plpgsql;

Tipos de funciones UDF

Dependiendo de su propósito y comportamiento, las funciones UDF se pueden clasificar en diferentes categorías:

  • Funciones escalares: Devuelven un único valor por cada fila de entrada
  • Funciones de tabla: Retornan un conjunto de resultados (tabla)
  • Funciones de agregado: Operan sobre múltiples filas y devuelven un único resultado

Ejemplos prácticos

Función escalar simple en MySQL

Veamos una función que calcula el precio con descuento:

DELIMITER //
CREATE FUNCTION calcular_precio_descuento(precio DECIMAL(10,2), porcentaje_descuento DECIMAL(5,2))
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
    DECLARE precio_final DECIMAL(10,2);
    SET precio_final = precio - (precio * porcentaje_descuento / 100);
    RETURN precio_final;
END //
DELIMITER ;

Para usar esta función:

SELECT 
    nombre_producto, 
    precio_original,
    calcular_precio_descuento(precio_original, 15) AS precio_con_descuento
FROM productos;

Función escalar en PostgreSQL

Una función que convierte una cadena a formato de título (primera letra de cada palabra en mayúscula):

CREATE OR REPLACE FUNCTION formato_titulo(texto text)
RETURNS text AS
$$
DECLARE
    resultado text := '';
    palabra text;
    palabras text[];
BEGIN
    palabras := string_to_array(lower(texto), ' ');
    
    FOREACH palabra IN ARRAY palabras LOOP
        IF length(palabra) > 0 THEN
            resultado := resultado || ' ' || upper(left(palabra, 1)) || right(palabra, -1);
        END IF;
    END LOOP;
    
    RETURN trim(resultado);
END;
$$ LANGUAGE plpgsql;

Uso de la función:

SELECT 
    formato_titulo('el rápido zorro marrón') AS titulo_formateado;
-- Resultado: "El Rápido Zorro Marrón"

Funciones de tabla

Las funciones que retornan conjuntos de resultados son especialmente útiles para operaciones complejas:

En MySQL:

DELIMITER //
CREATE FUNCTION obtener_productos_categoria(cat_id INT)
RETURNS TABLE (
    id INT,
    nombre VARCHAR(100),
    precio DECIMAL(10,2)
)
READS SQL DATA
BEGIN
    RETURN TABLE (
        SELECT producto_id, nombre_producto, precio
        FROM productos
        WHERE categoria_id = cat_id
    );
END //
DELIMITER ;

En PostgreSQL:

CREATE OR REPLACE FUNCTION productos_por_rango_precio(precio_min DECIMAL, precio_max DECIMAL)
RETURNS TABLE (
    id INTEGER,
    nombre VARCHAR(100),
    precio DECIMAL(10,2),
    categoria VARCHAR(50)
) AS
$$
BEGIN
    RETURN QUERY
    SELECT 
        p.producto_id,
        p.nombre_producto,
        p.precio,
        c.nombre_categoria
    FROM 
        productos p
    JOIN 
        categorias c ON p.categoria_id = c.categoria_id
    WHERE 
        p.precio BETWEEN precio_min AND precio_max
    ORDER BY 
        p.precio;
END;
$$ LANGUAGE plpgsql;

Para usar esta función:

SELECT * FROM productos_por_rango_precio(10.00, 50.00);

Variables y control de flujo

Las funciones UDF permiten declarar variables locales y utilizar estructuras de control como condicionales y bucles:

DELIMITER //
CREATE FUNCTION calcular_categoria_cliente(total_compras DECIMAL(10,2))
RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
    DECLARE categoria VARCHAR(20);
    
    IF total_compras > 10000 THEN
        SET categoria = 'Premium';
    ELSEIF total_compras > 5000 THEN
        SET categoria = 'Gold';
    ELSEIF total_compras > 1000 THEN
        SET categoria = 'Silver';
    ELSE
        SET categoria = 'Regular';
    END IF;
    
    RETURN categoria;
END //
DELIMITER ;

Consideraciones de seguridad y rendimiento

Al trabajar con funciones UDF, es importante tener en cuenta:

  • Características de seguridad: Especificar si la función accede a datos (READS SQL DATA) o los modifica (MODIFIES SQL DATA)
  • Determinismo: Indicar si la función siempre devuelve el mismo resultado para los mismos parámetros (DETERMINISTIC)
  • Índices: Las funciones en cláusulas WHERE pueden afectar el uso de índices
  • Transacciones: Considerar el comportamiento dentro de transacciones

Gestión de funciones UDF

Para administrar las funciones UDF existentes:

Listar funciones en MySQL:

SHOW FUNCTION STATUS WHERE Db = 'nombre_base_datos';

Listar funciones en PostgreSQL:

SELECT routine_name 
FROM information_schema.routines 
WHERE routine_type = 'FUNCTION' 
AND routine_schema = 'public';

Eliminar una función:

-- MySQL y PostgreSQL
DROP FUNCTION IF EXISTS nombre_funcion;

Modificar una función existente: En MySQL, es necesario eliminar y recrear la función. En PostgreSQL, se puede usar CREATE OR REPLACE FUNCTION.

Depuración de funciones UDF

Para facilitar la depuración durante el desarrollo, se pueden implementar técnicas como:

CREATE OR REPLACE FUNCTION funcion_con_debug(param1 INT)
RETURNS INT AS
$$
DECLARE
    resultado INT;
BEGIN
    -- Registrar valores para depuración
    RAISE NOTICE 'Parámetro recibido: %', param1;
    
    resultado := param1 * 2;
    
    RAISE NOTICE 'Resultado calculado: %', resultado;
    RETURN resultado;
END;
$$ LANGUAGE plpgsql;

Las funciones UDF son herramientas fundamentales para los desarrolladores SQL que necesitan implementar lógica de negocio personalizada directamente en la base de datos. Permiten encapsular operaciones complejas y reutilizar código, mejorando tanto la productividad como el rendimiento de las aplicaciones que interactúan con la base de datos.

Retorno de valores

El retorno de valores es un aspecto fundamental en las funciones definidas por el usuario (UDF) en SQL. Cuando creamos una función, necesitamos especificar explícitamente qué tipo de datos devolverá y asegurarnos de que la función efectivamente retorne un valor compatible con ese tipo.

Declaración del tipo de retorno

Al crear una función UDF, debemos indicar el tipo de dato que devolverá mediante la cláusula RETURNS. Esta declaración es obligatoria y determina el tipo de valor que los usuarios de la función pueden esperar recibir:

En MySQL:

CREATE FUNCTION calcular_impuesto(precio DECIMAL(10,2))
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
    RETURN precio * 0.21;
END;

En PostgreSQL:

CREATE OR REPLACE FUNCTION calcular_impuesto(precio DECIMAL)
RETURNS DECIMAL AS
$$
BEGIN
    RETURN precio * 0.21;
END;
$$ LANGUAGE plpgsql;

Tipos de datos que pueden retornarse

Las funciones UDF pueden devolver prácticamente cualquier tipo de dato soportado por el sistema de gestión de bases de datos:

  • Tipos escalares: Enteros (INT, BIGINT), números decimales (DECIMAL, FLOAT), cadenas (VARCHAR, TEXT), fechas (DATE, TIMESTAMP), booleanos (BOOLEAN), etc.
  • Tipos compuestos: Registros o filas completas
  • Conjuntos de resultados: Tablas completas (solo en ciertos contextos)
  • Tipos definidos por el usuario: Tipos personalizados creados con CREATE TYPE (principalmente en PostgreSQL)

La sentencia RETURN

La sentencia RETURN es la que efectivamente devuelve un valor desde la función. Existen varias consideraciones importantes:

  • Una función debe contener al menos una sentencia RETURN
  • El valor retornado debe ser compatible con el tipo declarado
  • La ejecución de la función termina inmediatamente cuando se encuentra una sentencia RETURN
CREATE FUNCTION clasificar_temperatura(temp DECIMAL)
RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
    IF temp < 0 THEN
        RETURN 'Congelado';
    ELSEIF temp < 10 THEN
        RETURN 'Muy frío';
    ELSEIF temp < 20 THEN
        RETURN 'Frío';
    ELSEIF temp < 30 THEN
        RETURN 'Templado';
    ELSE
        RETURN 'Caluroso';
    END IF;
END;

Conversión implícita y explícita de tipos

En ocasiones, el valor que queremos retornar no coincide exactamente con el tipo declarado. SQL intentará realizar conversiones implícitas cuando sea posible:

CREATE FUNCTION sumar_uno(num INT)
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
    -- Conversión implícita de INT a DECIMAL
    RETURN num + 1;
END;

Sin embargo, es recomendable realizar conversiones explícitas para evitar comportamientos inesperados:

CREATE FUNCTION formatear_precio(precio DECIMAL(10,2))
RETURNS VARCHAR(50)
DETERMINISTIC
BEGIN
    -- Conversión explícita de DECIMAL a VARCHAR
    RETURN CONCAT(CAST(precio AS CHAR), ' €');
END;

Retorno de valores NULL

Las funciones UDF pueden devolver valores NULL, lo que resulta útil para manejar casos especiales o errores:

CREATE FUNCTION dividir_seguro(numerador DECIMAL, denominador DECIMAL)
RETURNS DECIMAL
DETERMINISTIC
BEGIN
    IF denominador = 0 THEN
        RETURN NULL;
    ELSE
        RETURN numerador / denominador;
    END IF;
END;

Retorno de múltiples valores mediante tipos compuestos

En PostgreSQL, podemos devolver múltiples valores utilizando tipos compuestos:

CREATE TYPE info_producto AS (
    nombre VARCHAR(100),
    precio DECIMAL(10,2),
    stock INT
);

CREATE OR REPLACE FUNCTION obtener_info_producto(prod_id INT)
RETURNS info_producto AS
$$
DECLARE
    resultado info_producto;
BEGIN
    SELECT nombre_producto, precio, cantidad_stock
    INTO resultado.nombre, resultado.precio, resultado.stock
    FROM productos
    WHERE producto_id = prod_id;
    
    RETURN resultado;
END;
$$ LANGUAGE plpgsql;

Para usar esta función:

SELECT * FROM obtener_info_producto(123);

Retorno de conjuntos de resultados (table functions)

Las funciones también pueden devolver conjuntos completos de resultados, lo que las convierte en funciones de tabla:

En PostgreSQL:

CREATE OR REPLACE FUNCTION productos_por_categoria(cat_id INT)
RETURNS TABLE (
    id INT,
    nombre VARCHAR(100),
    precio DECIMAL(10,2)
) AS
$$
BEGIN
    RETURN QUERY
    SELECT producto_id, nombre_producto, precio
    FROM productos
    WHERE categoria_id = cat_id;
END;
$$ LANGUAGE plpgsql;

En MySQL:

CREATE FUNCTION productos_categoria(cat_id INT)
RETURNS TEXT
DETERMINISTIC
BEGIN
    DECLARE resultado TEXT DEFAULT '';
    DECLARE prod_nombre VARCHAR(100);
    DECLARE prod_precio DECIMAL(10,2);
    DECLARE fin_cursor BOOLEAN DEFAULT FALSE;
    
    DECLARE cursor_productos CURSOR FOR
        SELECT nombre_producto, precio
        FROM productos
        WHERE categoria_id = cat_id;
    
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET fin_cursor = TRUE;
    
    OPEN cursor_productos;
    
    recorrer: LOOP
        FETCH cursor_productos INTO prod_nombre, prod_precio;
        IF fin_cursor THEN
            LEAVE recorrer;
        END IF;
        
        SET resultado = CONCAT(resultado, prod_nombre, ': ', prod_precio, '€; ');
    END LOOP;
    
    CLOSE cursor_productos;
    
    RETURN resultado;
END;

Variables de salida vs. valores de retorno

Es importante distinguir entre el valor de retorno de una función y los parámetros de salida que pueden tener los procedimientos almacenados:

-- Función con valor de retorno
CREATE FUNCTION calcular_total(subtotal DECIMAL, impuesto DECIMAL)
RETURNS DECIMAL
DETERMINISTIC
BEGIN
    RETURN subtotal + (subtotal * impuesto / 100);
END;

-- Procedimiento con parámetros de salida (no es una función)
CREATE PROCEDURE calcular_totales(
    IN subtotal DECIMAL,
    IN impuesto DECIMAL,
    OUT total DECIMAL,
    OUT solo_impuesto DECIMAL
)
BEGIN
    SET solo_impuesto = subtotal * impuesto / 100;
    SET total = subtotal + solo_impuesto;
END;

Optimización del retorno de valores

Para mejorar el rendimiento, especialmente en funciones que devuelven grandes conjuntos de datos, podemos aplicar algunas técnicas:

  • Retorno temprano: Devolver resultados tan pronto como sea posible
  • Filtrado eficiente: Aplicar filtros antes de procesar grandes volúmenes de datos
  • Uso de índices: Asegurar que las consultas dentro de la función utilicen índices
CREATE FUNCTION buscar_cliente(id_cliente INT)
RETURNS VARCHAR(100)
READS SQL DATA
BEGIN
    DECLARE nombre_cliente VARCHAR(100);
    
    -- Retorno temprano si el ID es inválido
    IF id_cliente <= 0 THEN
        RETURN NULL;
    END IF;
    
    SELECT nombre INTO nombre_cliente
    FROM clientes
    WHERE cliente_id = id_cliente
    LIMIT 1;  -- Optimización: solo necesitamos un registro
    
    RETURN nombre_cliente;
END;

Captura y manejo de errores en el retorno

Podemos implementar mecanismos para capturar errores y devolver valores apropiados:

CREATE FUNCTION calcular_ratio_seguro(valor1 INT, valor2 INT)
RETURNS DECIMAL(10,2)
BEGIN
    DECLARE resultado DECIMAL(10,2);
    
    -- Manejo de error para división por cero
    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
    BEGIN
        RETURN -1;  -- Código de error
    END;
    
    IF valor2 = 0 THEN
        SIGNAL SQLSTATE '22012'  -- Estado SQL para división por cero
            SET MESSAGE_TEXT = 'División por cero no permitida';
    END IF;
    
    SET resultado = valor1 / valor2;
    RETURN resultado;
END;

El manejo adecuado del retorno de valores es esencial para crear funciones UDF robustas y útiles. Una función bien diseñada debe devolver resultados predecibles y manejar correctamente los casos especiales, proporcionando así una interfaz confiable para los desarrolladores que la utilizan en sus consultas SQL.

Funciones vs Procedimientos almacenados

Las bases de datos modernas ofrecen dos mecanismos principales para encapsular lógica de programación: funciones y procedimientos almacenados. Aunque ambos permiten ejecutar código SQL dentro del motor de la base de datos, existen diferencias fundamentales en su propósito, estructura y uso.

Diferencias estructurales

La distinción más evidente entre funciones y procedimientos almacenados radica en su estructura y comportamiento:

  • Funciones: Están diseñadas para calcular y devolver un valor (o conjunto de valores) que puede utilizarse directamente en una expresión SQL.
  • Procedimientos: Están orientados a ejecutar una serie de operaciones que pueden incluir modificaciones en la base de datos, sin necesidad de devolver un valor.

Veamos cómo se refleja esta diferencia en la sintaxis:

Función en MySQL:

CREATE FUNCTION calcular_edad(fecha_nacimiento DATE)
RETURNS INT
DETERMINISTIC
BEGIN
    RETURN TIMESTAMPDIFF(YEAR, fecha_nacimiento, CURDATE());
END;

Procedimiento en MySQL:

CREATE PROCEDURE actualizar_inventario(
    IN producto_id INT,
    IN cantidad INT
)
BEGIN
    UPDATE productos
    SET stock = stock - cantidad
    WHERE id = producto_id;
    
    -- No hay sentencia RETURN
END;

Contexto de uso

El contexto en el que se pueden utilizar marca otra diferencia significativa:

  • Funciones: Pueden utilizarse en consultas SELECT, dentro de expresiones, cláusulas WHERE, ORDER BY, etc.
  • Procedimientos: Se ejecutan mediante la sentencia CALL y no pueden formar parte de expresiones SQL.

Ejemplos de uso:

-- Uso de función en una consulta
SELECT 
    nombre,
    fecha_nacimiento,
    calcular_edad(fecha_nacimiento) AS edad
FROM clientes
WHERE calcular_edad(fecha_nacimiento) >= 18;

-- Llamada a un procedimiento
CALL actualizar_inventario(101, 5);

Parámetros y valores de retorno

La gestión de parámetros y valores de retorno constituye otra diferencia clave:

  • Funciones:

  • Deben retornar un valor mediante la sentencia RETURN

  • Solo admiten parámetros de entrada (IN)

  • El tipo de retorno debe declararse explícitamente

  • Procedimientos:

  • No retornan valores (aunque pueden usar parámetros de salida)

  • Admiten tres tipos de parámetros: entrada (IN), salida (OUT) y entrada-salida (INOUT)

  • No requieren declaración de tipo de retorno

Ejemplo de procedimiento con parámetros de salida:

CREATE PROCEDURE calcular_estadisticas_ventas(
    IN fecha_inicio DATE,
    IN fecha_fin DATE,
    OUT total_ventas DECIMAL(10,2),
    OUT num_transacciones INT
)
BEGIN
    SELECT 
        SUM(monto), 
        COUNT(*)
    INTO 
        total_ventas, 
        num_transacciones
    FROM ventas
    WHERE fecha BETWEEN fecha_inicio AND fecha_fin;
END;

Para usar este procedimiento:

CALL calcular_estadisticas_ventas('2023-01-01', '2023-12-31', @total, @num);
SELECT @total AS total_ventas, @num AS transacciones;

Transacciones y control de flujo

Tanto funciones como procedimientos pueden contener estructuras de control de flujo (IF, CASE, WHILE, etc.), pero existen diferencias en cuanto al manejo de transacciones:

  • Procedimientos: Tienen mayor flexibilidad para manejar transacciones. Pueden iniciar, confirmar o revertir transacciones explícitamente.
  • Funciones: Generalmente tienen restricciones más estrictas respecto a las operaciones que pueden realizar dentro de transacciones.

Ejemplo de procedimiento con manejo de transacciones:

CREATE PROCEDURE transferir_fondos(
    IN cuenta_origen INT,
    IN cuenta_destino INT,
    IN monto DECIMAL(10,2),
    OUT resultado VARCHAR(100)
)
BEGIN
    DECLARE saldo_actual DECIMAL(10,2);
    
    -- Iniciar transacción
    START TRANSACTION;
    
    -- Verificar saldo suficiente
    SELECT saldo INTO saldo_actual 
    FROM cuentas 
    WHERE cuenta_id = cuenta_origen;
    
    IF saldo_actual < monto THEN
        SET resultado = 'Saldo insuficiente';
        ROLLBACK;
    ELSE
        -- Restar de cuenta origen
        UPDATE cuentas 
        SET saldo = saldo - monto 
        WHERE cuenta_id = cuenta_origen;
        
        -- Sumar a cuenta destino
        UPDATE cuentas 
        SET saldo = saldo + monto 
        WHERE cuenta_id = cuenta_destino;
        
        SET resultado = 'Transferencia exitosa';
        COMMIT;
    END IF;
END;

Operaciones permitidas

Las operaciones que pueden realizar también difieren:

  • Funciones: Están más limitadas en cuanto a las operaciones que pueden ejecutar:

  • En MySQL, por defecto no pueden modificar datos (INSERT, UPDATE, DELETE)

  • Deben declararse con características específicas (como DETERMINISTIC, NO SQL, etc.)

  • No pueden ejecutar sentencias de control de transacciones

  • Procedimientos: Tienen menos restricciones:

  • Pueden ejecutar cualquier tipo de sentencia SQL

  • Pueden modificar datos libremente

  • Pueden controlar transacciones

Rendimiento y optimización

Desde el punto de vista del rendimiento:

  • Funciones: Al poder utilizarse en consultas, el optimizador de consultas puede trabajar con ellas, pero pueden afectar negativamente al rendimiento si no están bien diseñadas.
  • Procedimientos: Al ejecutarse de forma independiente, tienen menos impacto en la optimización de consultas, pero pueden ser más eficientes para operaciones por lotes.

Casos de uso típicos

Cada uno tiene escenarios donde resulta más apropiado:

Funciones son ideales para:

  • Cálculos reutilizables (impuestos, descuentos, edades)
  • Transformaciones de datos (formateo, conversiones)
  • Validaciones que devuelven un resultado
  • Lógica de negocio que se usa en consultas
-- Función para calcular precio con descuento
CREATE FUNCTION precio_con_descuento(
    precio DECIMAL(10,2),
    descuento_porcentaje DECIMAL(5,2)
)
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
    RETURN precio - (precio * descuento_porcentaje / 100);
END;

-- Uso en una consulta
SELECT 
    nombre_producto,
    precio,
    precio_con_descuento(precio, 15) AS precio_oferta
FROM productos;

Procedimientos son ideales para:

  • Operaciones que modifican datos (actualizaciones masivas)
  • Procesos ETL (Extract, Transform, Load)
  • Operaciones que requieren múltiples pasos
  • Tareas administrativas
-- Procedimiento para actualizar precios por categoría
CREATE PROCEDURE actualizar_precios_categoria(
    IN categoria_id INT,
    IN porcentaje_aumento DECIMAL(5,2)
)
BEGIN
    UPDATE productos
    SET precio = precio * (1 + porcentaje_aumento/100)
    WHERE categoria_id = categoria_id;
    
    SELECT CONCAT('Precios actualizados: ', ROW_COUNT(), ' productos') AS resultado;
END;

Diferencias específicas en MySQL y PostgreSQL

Existen algunas diferencias en la implementación entre estos sistemas:

MySQL:

  • Las funciones requieren especificar características como DETERMINISTIC, NO SQL, etc.
  • Los procedimientos utilizan delimitadores alternativos durante su creación

PostgreSQL:

  • Utiliza el mismo comando CREATE FUNCTION tanto para funciones como para procedimientos
  • Desde PostgreSQL 11, introduce CREATE PROCEDURE específicamente
  • Usa el lenguaje de procedimientos PL/pgSQL por defecto

Ejemplo en PostgreSQL:

-- Función en PostgreSQL
CREATE OR REPLACE FUNCTION calcular_impuesto(subtotal DECIMAL)
RETURNS DECIMAL AS
$$
BEGIN
    RETURN subtotal * 0.21;
END;
$$ LANGUAGE plpgsql;

-- Procedimiento en PostgreSQL (desde v11)
CREATE OR REPLACE PROCEDURE registrar_venta(
    cliente_id INT,
    producto_id INT,
    cantidad INT
)
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO ventas (cliente_id, producto_id, cantidad, fecha)
    VALUES (cliente_id, producto_id, cantidad, CURRENT_DATE);
    
    UPDATE inventario
    SET stock = stock - cantidad
    WHERE producto_id = registrar_venta.producto_id;
    
    COMMIT;
END;
$$;

Tabla comparativa

Característica Funciones Procedimientos
Propósito principal Calcular y retornar valores Ejecutar operaciones
Uso en consultas Sí (SELECT, WHERE, etc.) No
Forma de ejecución Dentro de expresiones SQL Mediante CALL
Retorno de valores Obligatorio (RETURN) No retornan valores directamente
Parámetros Solo de entrada (IN) Entrada (IN), salida (OUT), entrada-salida (INOUT)
Modificación de datos Limitada (según configuración) Sin restricciones
Manejo de transacciones Limitado Completo
Uso típico Cálculos, transformaciones Procesos, actualizaciones masivas

¿Cuándo elegir uno u otro?

La elección entre función y procedimiento debe basarse en:

  • Propósito: ¿Necesitas calcular un valor o ejecutar una operación?
  • Contexto de uso: ¿Se utilizará dentro de consultas o como una operación independiente?
  • Complejidad: ¿Implica múltiples pasos, transacciones o modificaciones de datos?
  • Parámetros: ¿Necesitas parámetros de salida además del valor de retorno?
-- Ejemplo: Función para validación
CREATE FUNCTION es_email_valido(email VARCHAR(255))
RETURNS BOOLEAN
DETERMINISTIC
BEGIN
    RETURN email REGEXP '^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}$';
END;

-- Ejemplo: Procedimiento para proceso complejo
CREATE PROCEDURE procesar_pedido(
    IN pedido_id INT,
    OUT resultado VARCHAR(100)
)
BEGIN
    DECLARE items_disponibles BOOLEAN DEFAULT TRUE;
    
    -- Verificar stock
    SELECT NOT EXISTS (
        SELECT 1 FROM items_pedido ip
        JOIN productos p ON ip.producto_id = p.id
        WHERE ip.pedido_id = pedido_id
        AND ip.cantidad > p.stock
    ) INTO items_disponibles;
    
    IF items_disponibles THEN
        START TRANSACTION;
        
        -- Actualizar inventario
        UPDATE productos p
        JOIN items_pedido ip ON p.id = ip.producto_id
        SET p.stock = p.stock - ip.cantidad
        WHERE ip.pedido_id = pedido_id;
        
        -- Marcar pedido como procesado
        UPDATE pedidos
        SET estado = 'Procesado', fecha_proceso = NOW()
        WHERE id = pedido_id;
        
        COMMIT;
        SET resultado = 'Pedido procesado correctamente';
    ELSE
        SET resultado = 'Stock insuficiente para algunos productos';
    END IF;
END;

Entender las diferencias entre funciones y procedimientos almacenados permite a los desarrolladores SQL elegir la herramienta adecuada para cada tarea, mejorando tanto la estructura como el rendimiento de sus aplicaciones de base de datos.

Aprende SQL online

Otras lecciones de SQL

Accede a todas las lecciones de SQL y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A Sql

Introducción Y Entorno

Ddl Y Dml

Introducción Y Entorno

Instalación De Mysql

Introducción Y Entorno

Instalación De Postgresql

Introducción Y Entorno

Tipos De Datos

Introducción Y Entorno

Bases De Datos Y Tablas

Introducción Y Entorno

Sistemas De Gestión De Bases De Datos

Introducción Y Entorno

Tipos De Bases De Datos

Introducción Y Entorno

Creación De Bases De Datos Y Tablas: Create Database, Create Table

Sintaxis Dml Crud

Consultas Básicas De Selección: Select Y Where

Sintaxis Dml Crud

Inserción De Datos: Insert Into

Sintaxis Dml Crud

Actualización De Datos: Update

Sintaxis Dml Crud

Eliminación De Datos: Delete

Sintaxis Dml Crud

Introducción A Dml

Sintaxis Dml Crud

Consultar Datos: Select

Sintaxis Dml Crud

Clasificación De Resultados Con Order By

Filtros Y Clasificación

Filtrado De Valores Únicos Con Distinct

Filtros Y Clasificación

Paginación Con Limit Y Offset

Filtros Y Clasificación

Alterar La Estructura De Tablas Existentes: Alter Table

Sintaxis Ddl

Renombrar Tablas Y Bases De Datos: Rename

Sintaxis Ddl

Vaciar Tablas Y Bases De Datos: Drop

Sintaxis Ddl

Uso De Funciones Agregadas: Count, Sum, Avg, Max, Min

Funciones Y Agrupación

Agrupación De Resultados Con Group By

Funciones Y Agrupación

Filtrado De Grupos De Resultados Con Having

Funciones Y Agrupación

Funciones Numéricas Y Matemáticas

Funciones Y Agrupación

Funciones De Fecha Y Hora

Funciones Y Agrupación

Funciones De Texto

Funciones Y Agrupación

Many To One

Asociaciones Entre Tablas

One To Many

Asociaciones Entre Tablas

One To One

Asociaciones Entre Tablas

Many To Many

Asociaciones Entre Tablas

Relaciones Entre Tablas

Joins Y Subqueries

Uso De Inner Join, Left Join, Right Join, Full Join

Joins Y Subqueries

Creación Y Uso De Subqueries

Joins Y Subqueries

Left Join Y Right Join

Joins Y Subqueries

Full Join

Joins Y Subqueries

Cross Join Y Self Join

Joins Y Subqueries

Optimización De Consultas

Sintaxis Avanzada

Uso De Índices Y Particiones

Sintaxis Avanzada

Uso De Vistas

Sintaxis Avanzada

Triggers Y Eventos

Sintaxis Avanzada

Particiones

Sintaxis Avanzada

Restricciones E Integridad

Sintaxis Avanzada

Transacciones

Sintaxis Avanzada

Vistas Materializadas

Sintaxis Avanzada

Rollback

Sintaxis Avanzada

Vistas Con Create View

Sintaxis Avanzada

Principios Acid

Sintaxis Avanzada

Manejo De Errores Y Excepciones

Sintaxis Avanzada

Funciones Ventana

Sintaxis Avanzada

Índices

Sintaxis Avanzada

Expresiones De Tabla Comunes (Cte) Con With

Sintaxis Avanzada

Creación Y Uso De Funciones

Programación En Sql

Creación Y Uso De Procedimientos Almacenados

Programación En Sql

Variables Y Control De Flujo

Programación En Sql

Creación Y Manejo De Usuarios Y Roles

Seguridad Y Administración

Asignación Y Gestión De Permisos

Seguridad Y Administración

Copias De Seguridad Y Restauración De Bases De Datos

Seguridad Y Administración

Accede GRATIS a SQL y certifícate

Ejercicios de programación de SQL

Evalúa tus conocimientos de esta lección Creación y uso de funciones con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Tipos de datos

Test

Inserción de datos: INSERT INTO

Test

Filtrado de grupos de resultados con HAVING

Test

Uso de índices y particiones

Test

Renombrar tablas y bases de datos: RENAME

Test

Uso de vistas

Test

Uso de INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN

Test

Agrupación de resultados con GROUP BY

Test

Creación y uso de subqueries

Test

Sentencias INSERT

Código

Copias de seguridad y restauración de bases de datos

Test

Uso de INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN

Código

Instalación de MySQL

Test

Relaciones entre tablas

Código

Eliminación de datos: DELETE

Test

Creación de bases de datos y tablas: CREATE DATABASE, CREATE TABLE

Test

Creación y uso de funciones

Test

Creación de tablas e inserción de datos con SQL

Proyecto

Uso de funciones agregadas: COUNT, SUM, AVG, MAX, MIN

Test

Optimización de consultas

Test

Introducción a SQL

Test

Triggers y eventos

Test

Clasificación de resultados con ORDER BY

Test

Alterar la estructura de tablas existentes: ALTER TABLE

Test

Eliminación de datos: DELETE

Código

Instalación de PostgreSQL

Test

Creación y uso de procedimientos almacenados

Test

Consultas básicas de selección: SELECT y WHERE

Test

Vaciar tablas y bases de datos: DROP

Test

Actualización de datos: UPDATE

Test

Creación y manejo de usuarios y roles

Test

Consultas básicas de selección SELECT y WHERE

Código

Creación de bases de datos y tablas

Código

Bases de datos y tablas

Test

Actualización de datos: UPDATE

Código

Relaciones entre tablas

Test

Filtrado de valores únicos con DISTINCT

Test

Asignación y gestión de permisos

Test

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender qué son las funciones definidas por el usuario (UDF) y sus beneficios.
  • Aprender la sintaxis básica para crear funciones UDF en MySQL y PostgreSQL.
  • Diferenciar entre funciones escalares, de tabla y de agregado.
  • Entender cómo declarar y manejar el retorno de valores en funciones.
  • Conocer las diferencias estructurales y de uso entre funciones y procedimientos almacenados.