Answers:
是。
未能指定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。
这具有超出预期范围的含义(例如,在索引视图或索引计算列中使用)。
该确定性特别属性会影响查询优化器。它具有有关允许执行的重写和操作类型的详细规则,并且这些规则对于非确定性元素非常受限制。副作用可能非常微妙。
例如,考虑以下两个表:
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数据访问。当功能不确定时,可以防止这种运动。
此示例的修复包括两个步骤:
WITH SCHEMABINDING
第一步很简单。第二个步骤涉及从字符串to删除不确定的隐式强制转换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;
此函数定义产生扫描计划,并且属性显示它仍然不确定: