¿Hay algún beneficio en SCHEMABINDING una función más allá de la protección de Halloween?

52

Es bien sabido que SCHEMABINDINGuna función puede evitar una cola innecesaria en los planes de actualización:

Si está utilizando UDF T-SQL simples que no tocan ninguna tabla (es decir, no acceden a los datos), asegúrese de especificar la SCHEMABINDINGopción durante la creación de las UDF. Esto hará que las UDF estén unidas al esquema y garantizará que el optimizador de consultas no genere operadores de spool innecesarios para planes de consulta que involucren estas UDF.

¿Existen otras ventajas de SCHEMABINDINGuna función, incluso si no accede a los datos?

Paul White
fuente

Respuestas:

78

Si.

No especificar WITH SCHEMABINDINGsignifica que SQL Server omite las verificaciones detalladas que normalmente realiza en el cuerpo de la función. Simplemente marca la función como datos de acceso (como se menciona en el enlace dado en la pregunta).

Esta es una optimización de rendimiento. Si no hizo esta suposición, SQL Server tendría que realizar las verificaciones detalladas de cada invocación de función (ya que la función independiente podría cambiar en cualquier momento).

Hay cinco propiedades de función importantes:

  • Determinismo
  • Precisión
  • Acceso a los datos
  • Acceso a datos del sistema
  • Verificación del sistema

Por ejemplo, tome la siguiente función escalar independiente:

CREATE FUNCTION dbo.F
(
    @i integer
)
RETURNS datetime
AS
BEGIN
    RETURN '19000101';
END;

Podemos mirar las cinco propiedades usando una función de metadatos:

SELECT 
    IsDeterministic = OBJECTPROPERTYEX(Func.ID, 'IsDeterministic'),
    IsPrecise = OBJECTPROPERTYEX(Func.ID, 'IsPrecise'),
    IsSystemVerified = OBJECTPROPERTYEX(Func.ID, 'IsSystemVerified'),
    UserDataAccess = OBJECTPROPERTYEX(Func.ID, 'UserDataAccess'),
    SystemDataAccess = OBJECTPROPERTYEX(Func.ID, 'SystemDataAccess')
FROM (VALUES(OBJECT_ID(N'dbo.F', N'FN'))) AS Func (ID);

Resultado

Las dos propiedades de acceso a datos se han establecido como verdaderas y las otras tres se han establecido como falsas .

Esto tiene implicaciones más allá de las que podrían esperarse (uso en vistas indexadas o columnas calculadas indexadas, por ejemplo).

Efectos en el optimizador de consultas

La propiedad de determinismo en particular afecta al optimizador de consultas. Tiene reglas detalladas sobre los tipos de reescrituras y manipulaciones que puede realizar, y estas están muy restringidas para elementos no deterministas. Los efectos secundarios pueden ser bastante sutiles.

Por ejemplo, considere las siguientes dos tablas:

CREATE TABLE dbo.T1
(
    SomeInteger integer PRIMARY KEY
);
GO
CREATE TABLE dbo.T2
(
    SomeDate datetime PRIMARY KEY
);

... y una consulta que usa la función (como se definió anteriormente):

SELECT * 
FROM dbo.T1 AS T1
JOIN dbo.T2 AS T2
    ON T2.SomeDate = dbo.F(T1.SomeInteger);

El plan de consulta es el esperado, con una búsqueda en la tabla T2:

Buscar plan

Sin embargo, si la misma consulta lógica se escribe utilizando una tabla derivada o una expresión de tabla común:

WITH CTE AS
(
    SELECT *, dt = dbo.F(T1.SomeInteger) 
    FROM dbo.T1 AS T1
)
SELECT * 
FROM CTE
JOIN dbo.T2 AS T2
    ON T2.SomeDate = CTE.dt;

-- Derived table
SELECT
    *
FROM 
(
    SELECT *, dt = dbo.F(T1.SomeInteger)
    FROM dbo.T1 AS T1
) AS T1
JOIN dbo.T2 AS T2
    ON T2.SomeDate = T1.dt;

El plan de ejecución ahora presenta una exploración, con el predicado que involucra la función atascada en un filtro:

Plan de escaneo

Esto también sucede si la tabla derivada o la expresión de tabla común se reemplaza por una vista o una función en línea. Una FORCESEEKpista (y otros intentos similares) no tendrá éxito:

Mensaje de error

El problema fundamental es que el optimizador de consultas no puede reordenar elementos de consulta no deterministas tan libremente .

Para producir una búsqueda, el predicado de filtro necesitaría moverse hacia abajo del plan al acceso de datos T2. Este movimiento se evita cuando la función no es determinista.

Reparar

La solución para este ejemplo implica dos pasos:

  1. Añadir WITH SCHEMABINDING
  2. Hacer la función determinista

El primer paso es trivial. El segundo implica eliminar la conversión implícita no determinista de la cadena a datetime; reemplazándolo con un determinista CONVERT. Ninguno de los dos es suficiente por sí solo .

ALTER FUNCTION dbo.F
(
    @i integer
)
RETURNS datetime
WITH SCHEMABINDING
AS
BEGIN
    -- Convert with a deterministic style
    RETURN CONVERT(datetime, '19000101', 112);
END;

Las propiedades de la función son ahora:

Nuevas propiedades

Con el optimizador liberado, todos los ejemplos ahora producen el plan de búsqueda deseado .


Tenga en cuenta que el uso de CASTa datetimeen la función no funcionaría, porque no es posible especificar un estilo de conversión en esa sintaxis:

ALTER FUNCTION dbo.F
(
    @i integer
)
RETURNS datetime
WITH SCHEMABINDING
AS
BEGIN
    -- Convert with a deterministic style
    RETURN CAST('19000101' AS datetime);
END;

Esta definición de función produce el plan de exploración, y las propiedades muestran que sigue siendo no determinista:

Propiedades de la función CAST

Paul White
fuente