Да.
Неспособность указать 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
Исправление для этого примера включает в себя два этапа:
- Добавлять
WITH SCHEMABINDING
- Сделайте функцию детерминированной
Первый шаг тривиален. Второй включает в себя удаление недетерминированного неявного приведения из строки в 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;
Свойства функции теперь:
Когда оптимизатор отключен, все примеры теперь дают желаемый план поиска .
Обратите внимание, что использование CAST
to datetime
в функции не сработает, поскольку невозможно указать стиль преобразования в этом синтаксисе:
ALTER FUNCTION dbo.F
(
@i integer
)
RETURNS datetime
WITH SCHEMABINDING
AS
BEGIN
-- Convert with a deterministic style
RETURN CAST('19000101' AS datetime);
END;
Это определение функции создает план сканирования, а свойства показывают, что он остается недетерминированным: