Есть ли какая-либо польза от SCHEMABINDING помимо функции Halloween Protection?

52

Общеизвестно, что SCHEMABINDINGфункция может избежать ненужной буферизации в планах обновления:

Если вы используете простые пользовательские функции T-SQL, которые не касаются каких-либо таблиц (т. Е. Не обращаются к данным), убедитесь, что вы указали эту SCHEMABINDINGопцию во время создания пользовательских функций. Это сделает привязку к схеме UDF и обеспечит, чтобы оптимизатор запросов не генерировал ненужных операторов буферизации для планов запросов, включающих эти UDF.

Есть ли другие преимущества SCHEMABINDINGфункции, даже если она не имеет доступа к данным?

Пол Уайт
источник

Ответы:

78

Да.

Неспособность указать WITH SCHEMABINDINGозначает, что SQL Server пропускает подробные проверки, которые он обычно делает в теле функции. Он просто помечает функцию как доступ к данным (как указано в ссылке, приведенной в вопросе).

Это оптимизация производительности. Если бы он не делал этого предположения, SQL Server должен был бы выполнять подробные проверки при каждом вызове функции (поскольку несвязанная функция может измениться в любое время).

Есть пять важных свойств функции:

  • Детерминизм
  • точность
  • Доступ к данным
  • Доступ к системным данным
  • Проверка системы

Например, возьмите следующую несвязанную скалярную функцию:

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

Мы можем посмотреть на пять свойств, используя функцию метаданных:

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);

Результат

Два свойства доступа к данным были установлены в true, а остальные три установлены в false .

Это имеет последствия, выходящие за рамки ожидаемых (например, использование в индексированных представлениях или индексированных вычисляемых столбцах).

Влияние на оптимизатор запросов

В частности, свойство Determinism влияет на оптимизатор запросов. В нем есть подробные правила, касающиеся типов переписывания и манипуляций, которые ему разрешено выполнять, и они очень ограничены для недетерминированных элементов. Побочные эффекты могут быть довольно тонкими.

Например, рассмотрим следующие две таблицы:

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

... и запрос, который использует функцию (как определено ранее):

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

План запроса, как и ожидалось, включает поиск в таблице T2:

Искать план

Однако, если тот же логический запрос написан с использованием производной таблицы или выражения общей таблицы:

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;

План выполнения теперь включает сканирование с предикатом, включающим функцию, застрявшую в фильтре:

План сканирования

Это также происходит, если производная таблица или общее табличное выражение заменяется представлением или встроенной функцией. FORCESEEKНамек (и другие подобные попытки) не удастся:

Сообщение об ошибке

Основная проблема заключается в том, что оптимизатор запросов не может переупорядочивать недетерминированные элементы запроса так свободно .

Чтобы произвести поиск, предикат Filter должен быть перемещен вниз по плану до доступа к данным T2. Это движение предотвращается, когда функция недетерминирована.

Fix

Исправление для этого примера включает в себя два этапа:

  1. Добавлять WITH SCHEMABINDING
  2. Сделайте функцию детерминированной

Первый шаг тривиален. Второй включает в себя удаление недетерминированного неявного приведения из строки в datetime; заменив его детерминированным CONVERT. Ни одного не достаточно само по себе .

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

Свойства функции теперь:

Новые свойства

Когда оптимизатор отключен, все примеры теперь дают желаемый план поиска .


Обратите внимание, что использование CASTto datetimeв функции не сработает, поскольку невозможно указать стиль преобразования в этом синтаксисе:

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

Это определение функции создает план сканирования, а свойства показывают, что он остается недетерминированным:

Свойства функции CAST

Пол Уайт
источник