Почему скалярным функциям нужно разрешение на выполнение, а не выбор?

15

Мне интересно, почему для скалярной функции я должен предоставить пользователю выполнение, а не только выбор?

в то же время табличные функции прекрасно работают только с разрешением select или db_datareaderчленством.

Чтобы быть более ясным, вот мой пример: мне нужен пользователь, который имеет разрешение только на чтение для базы данных. поэтому я создал пользователя testUserи назвал его db_datareaderчленом. Затем я создал табличную функцию с именем fn_InlineTable. И все отлично. testUserзапускает этот SQL весь день

select * from dbo.fn_InlineTable

тогда мне нужна скалярная функция, поэтому я создал скалярную функцию с именем fn_ScalarTest. testUserне может запустить этот SQL

Select dbo.fn_ScalarTest(1) 

Понятно: это потому, что я не дал разрешение «testUser» на выполнение fn_ScalarTest.

Мой вопрос: на основе этой ссылки /programming/6150888/insert-update-delete-with-function-in-sql-server , который говорит, что FUNCTIONнельзя использовать для выполнения действий, которые изменяют состояние базы данных , Так почему бы не разрешить использовать скалярную функцию с тем же разрешением «SELECT» вместо разрешения «Выполнить» ??

Я надеюсь, что мой вопрос имеет смысл. Спасибо.

BobNoobGuy
источник

Ответы:

15

Скорее всего, основная причина в том, что Табличные Функции возвращают Результирующий Набор, точно так же как Таблицы и Представления. Это означает , что они могут быть использованы в FROMпункте ( в том числе JOINс и APPLYс, и т.д.) из 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;
Соломон Руцкий
источник
4

Я думаю, что разница в разрешениях заключается в том, что вы можете на самом деле вызывать пользовательские функции со скалярными значениями с помощью EXEC, как хранимые процедуры (чего я не осознавал, пока не начал копаться в электронной документации по SQL Server 2000, где они вводили пользовательские функции) Тем не менее, вы не можете выбрать из них в качестве источника таблицы. Например:

DECLARE @date datetime
EXEC @date = dbo.first_day_of_month '8/14/2015'
SELECT @date

В этом случае dbo.first_day_of_month является пользовательской функцией. Я не знаю, почему вы когда-либо вызывали бы функцию таким образом, но я бы предположил, что им требуется разрешение EXECUTE, а не SELECT для поддержания согласованности. В настоящее время это, вероятно, просто держится как совместимый багаж.

db2
источник