为什么标量值函数需要执行权限而不是选择?

Bob*_*Guy 18 sql-server permissions t-sql functions

我想知道为什么对于标量值函数,我必须授予用户执行而不仅仅是选择?

同时,表值函数只需选择权限或db_datareader成员资格即可正常工作。

更清楚的是,这里是我的示例:我需要一个对数据库具有只读权限的用户。所以我创建了一个名为的用户testUser并授予它db_datareader成员资格。然后我创建了一个名为fn_InlineTable. 一切都很棒。testUser整天运行这个 SQL

select * from dbo.fn_InlineTable
Run Code Online (Sandbox Code Playgroud)

然后我需要一个标量函数,所以我创建了一个名为fn_ScalarTest. testUser无法运行此 SQL

Select dbo.fn_ScalarTest(1) 
Run Code Online (Sandbox Code Playgroud)

很好理解:这是因为我没有授予“testUser”权限来执行fn_ScalarTest.

我的问题是:基于此链接/sf/ask/430562191/,表示 aFUNCTION不能用于执行修改数据库状态的操作. 那么为什么不让标量函数使用相同的“SELECT”权限而不是执行权限?

我希望我的问题是有道理的。谢谢你。

Sol*_*zky 16

最有可能的主要原因是表值函数返回结果集,就像表和视图一样。这意味着它们可以在中使用FROM条款(包括JOINS和APPLYS,等)SELECTUPDATEDELETE查询。但是,您不能在任何这些上下文中使用标量 UDF。

其次,您还可以EXECUTE使用标量 UDF。当您为输入参数指定了默认值时,此语法非常方便。以下面的UDF为例:

CREATE FUNCTION dbo.OptionalParameterTest (@Param1 INT = 1, @Param2 INT = 2)
RETURNS INT
AS
BEGIN
    RETURN @Param1 + @Param2;
END;
Run Code Online (Sandbox Code Playgroud)

如果要将任何输入参数视为“可选”,则在DEFAULT像函数一样调用它时仍然需要传入关键字,因为签名是固定的:

DECLARE @Bob1 INT;

SET @Bob1 = dbo.OptionalParameterTest(100, DEFAULT);

SELECT @Bob1;
-- Returns: 102
Run Code Online (Sandbox Code Playgroud)

另一方面,如果您使用EXECUTE该函数,那么您可以将任何具有默认值的参数视为真正可选的,就像您可以使用存储过程一样。您可以在不指定参数名称的情况下传入前n 个参数:

DECLARE @Bob2 INT;

EXEC @Bob2 = dbo.OptionalParameterTest 50;

SELECT @Bob2;
-- Returns: 52
Run Code Online (Sandbox Code Playgroud)

您甚至可以通过指定参数名称来跳过第一个参数,同样,就像存储过程一样:

DECLARE @Bob3 INT;

EXEC @Bob3 = dbo.OptionalParameterTest @Param2 = 50;

SELECT @Bob3;
-- Returns: 51
Run Code Online (Sandbox Code Playgroud)

更新

为什么您可能想使用EXEC语法来像存储过程一样调用标量 UDF?有时,UDF 非常适合用作 UDF,因为它们可以添加到查询中并对返回的行集进行操作,而如果代码在存储过程中,则需要将其放入游标中迭代一组行。但有时您想在单个值上调用该函数,可能来自另一个 UDF。可以通过以下任一方式为单个值调用 UDF:

SELECT dbo.UDF('some value');
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您会在结果集中获得一个返回值(结果集将不起作用)。或者可以按如下方式完成:

DECLARE @Dummy INT;

SET @Dummy = dbo.UDF('some value');
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您需要声明@Dummy变量;

但是,使用EXEC语法,您可以避免这两种烦恼:

EXEC dbo.UDF 'some value';
Run Code Online (Sandbox Code Playgroud)

此外,标量 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
Run Code Online (Sandbox Code Playgroud)

测试:

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;
Run Code Online (Sandbox Code Playgroud)


db2*_*db2 6

我认为权限的不同是因为您实际上可以像存储过程一样使用 EXEC 调用标量值的用户定义函数(直到我深入研究 SQL Server 2000 联机丛书,他们介绍了用户定义函数时,我才意识到这一点) ,但您实际上无法从中选择作为表源。例如:

DECLARE @date datetime
EXEC @date = dbo.first_day_of_month '8/14/2015'
SELECT @date
Run Code Online (Sandbox Code Playgroud)

在这种情况下, dbo.first_day_of_month 是用户定义的函数。我不知道为什么您会以这种方式调用函数,但我推测它们需要 EXECUTE 权限而不是 SELECT 来保持一致性。如今,它可能只是作为兼容性包袱而存在。