Existe algum benefício em SCHEMABINDING uma função além da proteção de Halloween?

52

É sabido que SCHEMABINDINGuma função pode evitar um spool desnecessário nos planos de atualização:

Se você estiver usando UDFs T-SQL simples que não tocam em nenhuma tabela (ou seja, não acessam dados), certifique-se de especificar a SCHEMABINDINGopção durante a criação dos UDFs. Isso tornará as UDFs vinculadas ao esquema e garantirá que o otimizador de consulta não gere nenhum operador de spool desnecessário para os planos de consulta que envolvem essas UDFs.

Existem outras vantagens de SCHEMABINDINGuma função, mesmo que ela não acesse dados?

Paul White
fonte

Respostas:

78

Sim.

Deixar de especificar WITH SCHEMABINDINGsignifica que o SQL Server ignora as verificações detalhadas que normalmente faz no corpo da função. Simplesmente marca a função como acessando dados (como mencionado no link fornecido na pergunta).

Esta é uma otimização de desempenho. Se não fizesse essa suposição, o SQL Server teria que executar as verificações detalhadas de cada chamada de função (já que a função não acoplada pode ser alterada a qualquer momento).

Existem cinco propriedades importantes da função:

  • Determinismo
  • Precisão
  • Acesso de dados
  • Acesso aos dados do sistema
  • Verificação do sistema

Por exemplo, considere a seguinte função escalar não acoplada:

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

Podemos observar as cinco propriedades usando uma função de metadados:

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

As duas propriedades de acesso a dados foram definidas como verdadeiras e as outras três são definidas como falsas .

Isso tem implicações além daquelas que podem ser esperadas (uso em visualizações indexadas ou colunas computadas indexadas, por exemplo).

Efeitos no otimizador de consultas

A propriedade Determinism , em particular, afeta o otimizador de consulta. Possui regras detalhadas sobre os tipos de reescritas e manipulações que é permitido executar e são muito restritas a elementos não determinísticos. Os efeitos colaterais podem ser bastante sutis.

Por exemplo, considere as duas tabelas a seguir:

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

... e uma consulta que usa a função (conforme definido anteriormente):

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

O plano de consulta é conforme o esperado, apresentando uma pesquisa na tabela T2:

Buscar plano

No entanto, se a mesma consulta lógica for gravada usando uma tabela derivada ou expressão de tabela comum:

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;

O plano de execução agora apresenta uma varredura, com o predicado envolvendo a função travada em um filtro:

Plano de digitalização

Isso também acontece se a tabela derivada ou a expressão comum da tabela for substituída por uma função de exibição ou em linha. Uma FORCESEEKdica (e outras tentativas semelhantes) não terá êxito:

Mensagem de erro

A questão fundamental é que o otimizador de consulta não pode reordenar elementos de consulta não determinísticos tão livremente .

Para produzir uma busca, o predicado Filtro precisa ser movido para baixo do plano para o acesso a dados T2. Este movimento é evitado quando a função é não determinística.

Consertar

A correção para este exemplo envolve duas etapas:

  1. Adicionar, acrescentar WITH SCHEMABINDING
  2. Tornar a função determinística

O primeiro passo é trivial. O segundo envolve remover a conversão implícita não determinística da string para datetime; substituindo-o por um determinístico CONVERT. Nem é suficiente por si só .

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

As propriedades da função são agora:

Novas propriedades

Com o otimizador liberado, todos os exemplos agora produzem o plano de busca desejado .


Observe que o uso de CASTa datetimena função não funcionaria, porque não é possível especificar um estilo de conversão nessa sintaxe:

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

Essa definição de função produz o plano de varredura e as propriedades mostram que ele permanece não determinístico:

Propriedades da função CAST

Paul White
fonte