Inlining table-valued functions in SQL Server

Las funciones con valor de tabla en SQL Server son estupendas para escribir código SQL DRY encapsulando fragmentos de lógica de base de datos de uso común. Sin embargo, en algunos casos pueden ser la causa de graves problemas de rendimiento. Veamos un ejemplo de esto. Digamos que tenemos una tabla con el siguiente aspecto.

CREATE TABLE Products(ProductId INT,VendorId INT,Description NVARCHAR(1000))

Imagina ahora que en el contexto de un procedimiento almacenado tenemos la siguiente lógica recurrente.

SELECT *FROM ProductsWHERE Description LIKE @Description

Es posible que queramos refactorizar esto en una función para que nuestro procedimiento almacenado sea más legible y fácil de refactorizar. Muchos tutoriales en internet te dirán que crees una función con valor de tabla como la siguiente.

CREATE FUNCTION FN_Products_GetByDescription (@description NVARCHAR(1000))RETURNS @results TABLE(ProductId INT,VendorId INT,Description NVARCHAR(1000))ASBEGIN;INSERT INTO @resultsSELECT *FROM ProductsWHERE Description LIKE @description;RETURN;END;

El problema con este tipo de función es que actúa como una caja negra para el código que la llama. Imaginemos que llamamos a nuestra nueva función en el siguiente escenario.

SELECT *FROM FN_Products_GetByDescription('%book%')WHERE VendorId = 3;

Para ejecutar esto, SQL Server tiene que escanear cada producto de nuestra base de datos, comprobando la descripción del producto con nuestro texto de consulta, antes de devolver ese conjunto de resultados al código que lo llama, que luego los filtra por VendorId. En bases de datos grandes, esto es claramente muy ineficiente. ¿No sería mejor que SQL Server supiera desde el principio que queremos filtrar por el texto de la consulta y por el VendorId? De esta manera, podría filtrar primero por VendorId, y luego ejecutar el costoso filtrado LIKE en un conjunto de resultados más pequeño. Aquí es donde entra en juego la función inline. Para alinear nuestra función, simplemente la reescribimos de la siguiente manera.

CREATE FUNCTION FN_Products_GetByDescription (@description NVARCHAR(1000))RETURNS TABLEASRETURN(SELECT *FROM ProductsWHERE Description LIKE @description);

Ahora, cuando SQL Server ejecuta la consulta anterior, la función es tratada entre bastidores como una parte del código de llamada, en lugar de una caja negra que necesita devolver un valor antes de seguir adelante. Esto permite que el optimizador de consultas sea mucho más eficaz a la hora de optimizar las consultas, porque sabe desde el principio exactamente qué datos necesitamos.

Entonces, ¿cuándo no querríamos inlinear funciones valoradas en tablas? Bueno, las funciones sólo pueden ser inline cuando consisten en una única sentencia SELECT que devuelve un conjunto de resultados. Las funciones que contienen múltiples subconsultas que llenan una tabla de resultados, así como las que contienen sentencias adicionales como IFs no son inlineables. Sin embargo, si puede hacer que una consulta sea inlineable, ya sea directamente o reescribiéndola ligeramente, suele ser una muy buena idea. Las ganancias de rendimiento que puede obtener de la inlining de funciones pueden ser dramáticas, y le permiten la flexibilidad y legibilidad del código DRY, sin un gran coste de rendimiento.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *