使用递归标量 UDF 进行架构绑定

Cha*_*ace 8 sql-server functions recursive

TL;博士; SQL Server 允许标量 UDF 在架构绑定时递归调用自身,但仅在使用语法进行更改时才这样做,这是否是一个错误CREATE OR ALTER?或者是其他语法被禁止的错误?


一个简单的递归标量 UDF 可以构造如下

CREATE FUNCTION dbo.Try1 (@i int)
RETURNS int
AS BEGIN
  RETURN IIF(@i = 0, 0, @i + dbo.Try1(@i - 1));
END;
Run Code Online (Sandbox Code Playgroud)

只要这不是架构绑定的,那么这是允许的。


让我们尝试模式绑定它,我们会做

CREATE FUNCTION dbo.Try2 (@i int)
RETURNS int
WITH SCHEMABINDING
AS BEGIN
  RETURN IIF(@i = 0, 0, @i + dbo.Try2(@i - 1));
END;
Run Code Online (Sandbox Code Playgroud)

没有。

Cannot find either column "dbo" or the user-defined function
or aggregate "dbo.Try2", or the name is ambiguous
Run Code Online (Sandbox Code Playgroud)

不使用递归创建它然后更改它

CREATE OR ALTER FUNCTION dbo.Try3 (@i int)
RETURNS int
WITH SCHEMABINDING
AS BEGIN RETURN NULL; END;
Run Code Online (Sandbox Code Playgroud)
ALTER FUNCTION dbo.Try3 (@i int)
RETURNS int
WITH SCHEMABINDING
AS BEGIN
  RETURN IIF(@i = 0, 0, @i + dbo.Try3(@i - 1));
END;
Run Code Online (Sandbox Code Playgroud)

这次出现了不同的错误:

Cannot schema bind function 'dbo.Try3' because name 'dbo.Try3' is invalid
for schema binding. Names must be in two-part format
and an object cannot reference itself.
Run Code Online (Sandbox Code Playgroud)

嗯,an object cannot reference itself谁发明了这条规则?它不在文档中。


让我们尝试一下CREATE OR ALTER

CREATE OR ALTER FUNCTION dbo.Try4 (@i int)
RETURNS int
WITH SCHEMABINDING
AS BEGIN
  RETURN IIF(@i = 0, 0, @i + dbo.Try4(@i - 1));
END;
Run Code Online (Sandbox Code Playgroud)

还没。


但是如果我们首先CREATE 使用递归,然后使用CREATE OR ALTER ALTER)使用递归,那么它就可以工作

CREATE OR ALTER FUNCTION dbo.Try5 (@i int)
RETURNS int
WITH SCHEMABINDING
AS BEGIN RETURN NULL; END;
Run Code Online (Sandbox Code Playgroud)
CREATE OR ALTER FUNCTION dbo.Try5 (@i int)
RETURNS int
WITH SCHEMABINDING
AS BEGIN
  RETURN IIF(@i = 0, 0, @i + dbo.Try5(@i - 1));
END;
Run Code Online (Sandbox Code Playgroud)

奇怪的是:无论哪种方式CREATE OR ALTER在底层工作,这都是没有意义的。如果它确实删除并重新创建,那么为什么我们没有得到第一个错误呢?如果它改变了,那么为什么最后一个选项会起作用呢?

最有趣的是:如果我们首先在没有模式绑定的情况下创建它,那么CREATE OR ALTER WITH SCHEMABINDING我们会得到

Cannot schema bind function 'dbo.Try6'. 'dbo.Try6' is not schema bound.
Run Code Online (Sandbox Code Playgroud)

这完全是无稽之谈。

数据库<>小提琴

Pau*_*ite 7

一般而言,自引用函数没有很好的文档记录,但对此类函数的模式绑定的限制一直存在。父语法页面中并未提及所有错误情况。

问题中概述的导致模式绑定自引用函数的特定事件顺序不应允许(它也适用于多语句表值函数)。

允许自引用函数的模式绑定引入了许多必须考虑和测试的新可能性。例如,对函数的以下使用会导致DBCC CHECKTABLE失败:

CREATE TABLE dbo.Test 
(
    n integer NOT NULL,
    -- Can be persisted because the function is deterministic
    -- Checks for determinism require schemabinding
    sn AS dbo.Try5(n) PERSISTED, 
        CHECK (sn > 0)
);

-- Succeeds
INSERT dbo.Test (n) 
VALUES (31);

DBCC CHECKTABLE (N'dbo.Test') 
    WITH EXTENDED_LOGICAL_CHECKS;
Run Code Online (Sandbox Code Playgroud)
CREATE TABLE dbo.Test 
(
    n integer NOT NULL,
    -- Can be persisted because the function is deterministic
    -- Checks for determinism require schemabinding
    sn AS dbo.Try5(n) PERSISTED, 
        CHECK (sn > 0)
);

-- Succeeds
INSERT dbo.Test (n) 
VALUES (31);

DBCC CHECKTABLE (N'dbo.Test') 
    WITH EXTENDED_LOGICAL_CHECKS;
Run Code Online (Sandbox Code Playgroud)

我没有调查此失败的原因,但我认为CHECKTABLE验证计算列值的活动最终超出了最大嵌套级别 32。

许多事情需要测试和/或调整以支持模式绑定自引用功能。这种用法还不够流行,不足以证明投资的合理性。SQL Server 中存在递归的替代方案。