Reutilizar lógica de negocio dentro de una consulta SQL es una de las ventajas más potentes que ofrece SQL Server a través de sus funciones definidas por el usuario. A diferencia de los procedimientos almacenados, que se ejecutan con EXEC, las funciones pueden invocarse directamente dentro de un SELECT, lo que cambia por completo dónde y cómo se aplican. Comprender esta diferencia evita uno de los errores de diseño más frecuentes en bases de datos.
¿Qué diferencia a una función de un procedimiento almacenado?
La respuesta más directa es: puedes usar una función dentro de un SELECT y no puedes usar un procedimiento almacenado dentro de un SELECT [0:08]. Un procedimiento almacenado ejecuta lógica y se invoca con EXEC; una función devuelve un valor y se integra como parte de la consulta. Son herramientas distintas para propósitos distintos.
Esta distinción define cuándo conviene cada una. Si necesitas que el resultado forme parte de una consulta, usa una función. Si necesitas ejecutar operaciones complejas con efectos secundarios, un SP es la opción correcta.
¿Cómo funcionan las funciones escalares en SQL Server?
Una función escalar devuelve un único valor cada vez que se invoca [1:05]. El ejemplo que se trabaja es fn_calcular_margen, que recibe dos parámetros de tipo decimal: precio y costo.
sql CREATE OR ALTER FUNCTION fn_calcular_margen( @precio DECIMAL(10,2), @costo DECIMAL(10,2) ) RETURNS DECIMAL(5,2) AS BEGIN IF @precio IS NULL OR @precio = 0 OR @costo IS NULL RETURN NULL; RETURN (@precio - @costo) / @precio * 100; END;
Usar CREATE OR ALTER es una buena práctica: si la función existe, la modifica; si no, la crea [1:15]. La lógica valida que precio y costo no sean nulos ni cero antes de calcular el margen porcentual.
Para probarla basta con incluirla en un SELECT sobre la tabla de productos [1:55]:
sql SELECT nombre_producto, precio, costo, dbo.fn_calcular_margen(precio, costo) AS margen_porcentaje FROM productos ORDER BY costo DESC;
El resultado muestra cada producto con su precio, costo y margen en porcentaje, todo calculado en línea gracias a la función escalar.
¿Qué son las funciones de tabla y cuándo conviene usarlas?
A diferencia de las escalares, una función de tabla (inline table-valued function) devuelve un conjunto de filas completo [3:05]. Se define con RETURNS TABLE y contiene un único SELECT.
Cuando se invoca, el resultado se comporta como cualquier otra tabla, lo que permite combinarlo con otras fuentes de datos. Por ejemplo, al invocarla con el parámetro 'Argentina' y el año 2023, se obtiene una tabla con totales de pedidos y ventas [3:35].
¿Cómo combinar funciones de tabla con cross apply?
El operador CROSS APPLY permite aplicar la función de tabla fila por fila contra otra tabla [4:05]. Esto es especialmente útil cuando necesitas cruzar datos dinámicos con registros existentes:
sql SELECT p.nombre_pais, f.total_pedidos, f.ventas_totales FROM paises p CROSS APPLY fn_ventas_por_pais(p.nombre_pais, 2023) f;
Cada país se evalúa individualmente y la función devuelve los resultados correspondientes, integrándolos en una sola salida.
¿Qué es una multi-statement table-valued function?
Cuando la lógica es más compleja, se utilizan funciones multi-statement [5:05]. Estas funciones declaran una variable de tipo tabla, la pueblan con INSERT a partir de consultas internas y devuelven esa tabla como resultado.
sql CREATE OR ALTER FUNCTION fn_clientes_por_categoria( @nombre_categoria NVARCHAR(100) ) RETURNS @resultado TABLE ( cliente_id INT, nombre NVARCHAR(200), total_compra DECIMAL(10,2), ultima_compra DATE ) AS BEGIN INSERT INTO @resultado SELECT c.cliente_id, c.nombre, SUM(d.total), MAX(p.fecha) FROM clientes c JOIN pedidos p ON c.cliente_id = p.cliente_id JOIN detalle_pedidos d ON p.pedido_id = d.pedido_id JOIN categorias cat ON d.categoria_id = cat.categoria_id WHERE cat.nombre = @nombre_categoria GROUP BY c.cliente_id, c.nombre; RETURN; END;
Al invocarla con 'Electrónica' [6:00], devuelve todos los clientes que compraron en esa categoría junto con su última compra. Es ideal para reportes recurrentes: si el jefe de producto necesita conocer sus top clientes cada día, basta con invocar la función sin reescribir la consulta [6:45].
¿Cuándo elegir funciones frente a procedimientos almacenados?
- Las funciones escalares resuelven cálculos puntuales dentro de un
SELECT.
- Las funciones de tabla reemplazan subconsultas y se combinan con
CROSS APPLY.
- Las funciones multi-statement manejan lógica compleja y devuelven tablas construidas paso a paso.
- Los procedimientos almacenados se reservan para operaciones que no necesitan integrarse en consultas.
Como desafío práctico, se propone crear una función de tabla llamada ventas_mensuales_por_pais que reciba un año como parámetro y devuelva las ventas mensuales por país, para luego usarla con CROSS APPLY y mostrar los doce meses de ventas de Argentina en 2024 [7:40]. ¿Te animas a resolverlo y compartir tu solución?