主要原因可能是表值函数返回结果集,就像表和视图一样。这意味着它们可以在中使用FROM
条款(包括JOIN
S和APPLY
S,等)SELECT
,UPDATE
和DELETE
查询。但是,您不能在任何这些上下文中使用Scalar UDF。
其次,您还可以EXECUTE
使用标量UDF。当为输入参数指定默认值时,此语法非常方便。以以下UDF为例:
CREATE FUNCTION dbo.OptionalParameterTest (@Param1 INT = 1, @Param2 INT = 2)
RETURNS INT
AS
BEGIN
RETURN @Param1 + @Param2;
END;
如果要将任何输入参数都视为“可选”,则DEFAULT
在调用它时就像函数一样,仍需要传递关键字,因为签名是固定的:
DECLARE @Bob1 INT;
SET @Bob1 = dbo.OptionalParameterTest(100, DEFAULT);
SELECT @Bob1;
-- Returns: 102
另一方面,如果您使用EXECUTE
该函数,则可以将具有默认值的任何参数视为真正的可选参数,就像使用存储过程一样。您可以传入前n个参数,而无需指定参数名称:
DECLARE @Bob2 INT;
EXEC @Bob2 = dbo.OptionalParameterTest 50;
SELECT @Bob2;
-- Returns: 52
您甚至可以通过指定参数名称来跳过第一个参数,就像存储过程一样:
DECLARE @Bob3 INT;
EXEC @Bob3 = dbo.OptionalParameterTest @Param2 = 50;
SELECT @Bob3;
-- Returns: 51
更新
为什么要EXEC
像存储过程一样使用语法来调用标量UDF?有时候,UDF非常适合作为UDF,因为可以将它们添加到查询中并对其返回的行进行操作,而如果代码在存储过程中,则需要将其放置在游标中,以便遍历一组行。但是有时候,您可能想从单个UDF内的单个值调用该函数。为单个值调用UDF可以通过以下任一方式完成:
SELECT dbo.UDF('some value');
在这种情况下,您会在结果集中获得一个返回值(结果集将不起作用)。或者可以按照以下步骤进行:
DECLARE @Dummy INT;
SET @Dummy = dbo.UDF('some value');
在这种情况下,您需要声明@Dummy
变量;
但是,通过EXEC
语法,您可以避免这两种烦恼:
EXEC dbo.UDF 'some value';
此外,标量UDF会缓存其执行计划。这意味着,如果UDF中存在具有执行计划的查询,则可能会遇到参数嗅探问题。对于使用该EXEC
语法可行的方案,则还可以使用该WITH RECOMPILE
选项来忽略该执行的计划编译值。例如:
设定:
GO
CREATE FUNCTION dbo.TestUDF (@Something INT)
RETURNS INT
AS
BEGIN
DECLARE @Ret INT;
SELECT @Ret = COUNT(*)
FROM sys.indexes si
WHERE si.[index_id] = @Something;
RETURN @Ret;
END;
GO
测试:
DECLARE @Val INT;
SET @Val = dbo.TestUDF(1);
SELECT @Val;
EXEC @Val = dbo.TestUDF 0 -- uses compiled value of (1)
SELECT @Val;
EXEC @Val = dbo.TestUDF 0 WITH RECOMPILE; -- uses compiled value of (0)
SELECT @Val;
EXEC @Val = dbo.TestUDF 3 -- uses compiled value of (1)
SELECT @Val;