Una Función Definida por el Usuario (User Defined Function o UDF) es una rutina que acepta parámetros, realiza una acción y devuelve el resultado de esa acción como un valor.
Nos permiten realizar cálculos, transformaciones y operaciones complejas de manera modular, lo que mejora la legibilidad y mantenibilidad nuestro código T-SQL.
Las funciones están diseñadas para ser integradas directamente dentro de otras consultas. La filosofía es “Escríbelo una vez, úsalo muchas”.
Existen principalmente dos tipos que debes dominar: las Escalares y las de Tabla.
Funciones escalares
Son idénticas en concepto a UPPER() o ABS(). Reciben uno o varios parámetros y devuelven un único valor (un número, una fecha, un texto…).
La sintaxis es la siguiente,
CREATE FUNCTION dbo.NombreFuncion
(
@Parametro TipoDato
)
RETURNS TipoDatoRetorno
AS
BEGIN
DECLARE @Resultado TipoDatoRetorno;
-- Lógica del cálculo
SET @Resultado = ...;
RETURN @Resultado;
END
Ejemplo Práctico: Precio con IVA
Imagina que tienes que calcular el precio con IVA en 50 reportes distintos. En lugar de escribir Precio * 1.21 en todos lados (y tener que cambiarlo si suben los impuestos), creas una función.
CREATE FUNCTION dbo.ufn_CalcularPrecioConIVA
(
@PrecioBase DECIMAL(10, 2)
)
RETURNS DECIMAL(10, 2)
AS
BEGIN
DECLARE @PrecioFinal DECIMAL(10, 2);
-- Supongamos un IVA del 21%
SET @PrecioFinal = @PrecioBase * 1.21;
RETURN @PrecioFinal;
END
Para usarla, es obligatorio usar el esquema (normalmente dbo.) delante del nombre:
SELECT
Nombre,
Precio,
dbo.ufn_CalcularPrecioConIVA(Precio) AS PrecioFinal
FROM Productos;
Las funciones escalares en versiones antiguas de SQL Server (y aún hoy si se abusa) pueden ser lentas porque se ejecutan fila a fila y evitan el paralelismo.
Úsalas para cálculos simples, pero tampoco abuses. Evita acceder a tablas (hacer SELECT) dentro de una función escalar si procesas millones de filas y puedes resolverlo de otra forma.
Funciones de tabla
Estas funciones no devuelven un dato suelto, sino una tabla completa. Son como una “Vista con Parámetros”.
Nos enfocaremos en las Inline TVF (Funciones de tabla en línea), que son las más rápidas.
La sintaxis es la siguiente,
CREATE FUNCTION dbo.ufn_ObtenerPedidosPorCliente
(
@ClienteID INT
)
RETURNS TABLE
AS
RETURN
(
SELECT PedidoID, Fecha, Total
FROM Pedidos
WHERE ClienteID = @ClienteID
);
No tienen BEGIN...END. Simplemente retornan el resultado de una consulta.
Para usarlas, como devuelven una tabla, las usamos en la cláusula FROM o JOIN, (no en el SELECT).
SELECT Pedidos.PedidoID, Pedidos.Total
FROM dbo.ufn_ObtenerPedidosPorCliente(5) AS Pedidos -- ¡Actúa como una tabla!
WHERE Pedidos.Total > 100;
Esto es mucho más limpio y modular que escribir el WHERE repetidamente en cada consulta.
Limitaciones de las UDF
Para mantener la integridad y el determinismo, SQL Server impone reglas estrictas a las funciones:
- Sin efectos secundarios: Una función no puede cambiar el estado de la base de datos. Nada de
INSERT,UPDATEoDELETEa tablas permanentes. - No determinismo: Antiguamente no podías usar
GETDATE()oNEWID()dentro de una función porque devuelven valores distintos cada vez. (En versiones modernas se ha relajado un poco, pero sigue siendo una limitación conceptual). - Gestión de errores: No puedes usar
TRY...CATCHdentro de una función. Si falla, la consulta entera se detiene.
¿Función o procedimiento almacenado?
Aunque se parecen, tienen propósitos y reglas muy diferentes.
| Característica | Procedimiento Almacenado (SP) | Función (UDF) |
|---|---|---|
| Objetivo | Ejecutar una lógica de negocio completa (Proceso). | Calcular y transformar valores. |
| Invocación | Con EXEC (independiente). | Dentro de un SELECT, WHERE, JOIN. |
| Retorno | Puede no devolver nada, devolver parámetros OUTPUT o múltiples tablas. | Debe devolver un valor (escalar o tabla). |
| Modificar Datos | SÍ. Puede hacer INSERT, UPDATE, DELETE. | NO. Solo puede leer. No puede modificar tablas base. |
| Try/Catch | Permitido. | No permitido dentro de la función. |
En resumen
- ¿Necesitas modificar datos (
INSERT/UPDATE)? Usa un Procedimiento. - ¿Necesitas calcular algo para mostrarlo en una columna o filtrar? Usa una Función.
