En SQL se puede implementar la lógica de negocio a través de funciones definidas por el usuario (User Defined Functions, UDF), lo que permite trasladar parte de los cálculos y reglas de negocio a la base de datos.
Funciones escalares devuelven un único valor y se llaman en expresiones; por ejemplo, para calcular un total basado en parámetros:
CREATE FUNCTION dbo.GetDiscount(@price DECIMAL(10,2), @loyaltyLevel INT) RETURNS DECIMAL(10,2) AS BEGIN RETURN @price * CASE WHEN @loyaltyLevel = 1 THEN 0.95 WHEN @loyaltyLevel = 2 THEN 0.90 ELSE 1.0 END END; -- Ejemplo de uso: SELECT Name, dbo.GetDiscount(Price, LoyaltyLevel) AS DiscountPrice FROM Products;
Funciones tabulares devuelven un conjunto de filas (una tabla):
CREATE FUNCTION dbo.ActiveOrders(@userId INT) RETURNS TABLE AS RETURN ( SELECT * FROM Orders WHERE UserId = @userId AND Status = 'Active' ); -- Uso: SELECT * FROM dbo.ActiveOrders(123);
Diferencias, limitaciones y recomendaciones:
¿Cuál es la diferencia entre una función tabular INLINE y una función tabular multi-instrucción (multi-statement)? ¿Cómo afecta esta elección al rendimiento?
Respuesta y ejemplo:
-- INLINE CREATE FUNCTION dbo.InlineActiveOrders(@userId INT) RETURNS TABLE AS RETURN ( SELECT * FROM Orders WHERE UserId = @userId AND Status = 'Active' ); -- Multi-instrucción CREATE FUNCTION dbo.MultiActiveOrders(@userId INT) RETURNS @result TABLE (...) AS BEGIN INSERT INTO @result SELECT ... -- lógica RETURN END
Historia
Proyecto: Análisis Financiero.
Utilizamos UDF escalares en la parte SELECT de una consulta de informe a un gran conjunto de tablas: SELECT Amount, dbo.CalcTax(Amount, Type) FROM Transactions. La consulta tardaba de 5 a 10 minutos debido al procesamiento fila por fila de las UDF. Se reescribió utilizando CASE incorporado: el tiempo se redujo a segundos.
Historia
Proyecto: E-commerce. Para buscar los carritos de los usuarios utilizamos funciones tabulares multi-instrucción, donde había lógica de filtrado en varios pasos. Resultó que SQL no creaba un plan óptimo y realizaba un escaneo, incluso cuando se necesitaba un solo elemento. El cambio a una función INLINE aceleró las consultas 50 veces de inmediato.
Historia
Proyecto: CRM. Extrajimos la lógica de negocio para la asignación de bonificaciones a una UDF compleja. Después de un par de meses, nos dimos cuenta de que las fórmulas del negocio habían cambiado y que actualizar la función era complicado debido a la rígida vinculación con los datos. En un producto iterativo, la UDF resultó en altos costos de coordinación de cambios entre el back-end y la base de datos.