web-dev-qa-db-fra.com

Pourquoi les fonctions à valeur scalaire ont besoin d'une autorisation d'exécution plutôt que d'une sélection?

Je me demande pourquoi, pour une fonction à valeur scalaire, je dois autoriser l'utilisateur à exécuter plutôt qu'une simple sélection?

pendant ce temps, une fonction de valeur de table fonctionne très bien avec seulement une autorisation de sélection ou db_datareader adhésion.

pour être plus clair, voici mon exemple: j'ai besoin d'un utilisateur qui dispose d'une autorisation en lecture seule sur la base de données. j'ai donc créé un utilisateur appelé testUser et je lui ai donné db_datareader adhésion. puis j'ai créé une fonction de valeur de table appelée fn_InlineTable. Et tout va bien. testUser exécute ce SQL toute la journée

select * from dbo.fn_InlineTable

alors j'ai besoin d'une fonction scalaire, j'ai donc créé une fonction scalaire appelée fn_ScalarTest. testUser ne peut pas exécuter ce SQL

Select dbo.fn_ScalarTest(1) 

Bien entendu: c'est parce que je n'ai pas donné à "testUser" la permission d'exécuter fn_ScalarTest.

Ma question est: basée sur ce lien https://stackoverflow.com/questions/6150888/insert-update-delete-with-function-in-sql-server , qui dit un FUNCTION ne peut pas être utilisé pour effectuer des actions qui modifient l'état de la base de données. Alors pourquoi ne pas laisser une fonction scalaire être utilisée avec la même permission "SELECT" plutôt que d'exécuter la permission ??

J'espère que ma question a un sens. Je vous remercie.

15
BobNoobGuy

La raison principale est probablement que les fonctions table renvoient un ensemble de résultats, tout comme les tables et les vues. Cela signifie qu'ils peuvent être utilisés dans la clause FROM (y compris JOINs et APPLYs, etc.) de SELECT, UPDATE, et DELETE requêtes. Cependant, vous ne pouvez pas utiliser une FDU Scalar dans aucun de ces contextes.

Secondairement, vous pouvez également EXECUTE une UDF scalaire. Cette syntaxe est très pratique lorsque des valeurs par défaut sont spécifiées pour les paramètres d'entrée. Prenez par exemple l'UDF suivant:

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

Si vous souhaitez traiter l'un des paramètres d'entrée comme "facultatif", vous devez toujours passer le mot clé DEFAULT lorsque vous l'appelez comme une fonction car la signature est fixe:

DECLARE @Bob1 INT;

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

SELECT @Bob1;
-- Returns: 102

D'un autre côté, si vous EXECUTE la fonction, vous pouvez traiter tous les paramètres avec une valeur par défaut comme vraiment optionnels, tout comme vous le pouvez avec les procédures stockées. Vous pouvez passer les premiers n paramètres sans spécifier de noms de paramètres:

DECLARE @Bob2 INT;

EXEC @Bob2 = dbo.OptionalParameterTest 50;

SELECT @Bob2;
-- Returns: 52

Vous pouvez même ignorer le premier paramètre en spécifiant des noms de paramètre, à nouveau, comme avec les procédures stockées:

DECLARE @Bob3 INT;

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

SELECT @Bob3;
-- Returns: 51

MISE À JOUR

Pourquoi voudriez-vous utiliser la syntaxe EXEC pour appeler une UDF scalaire comme une procédure stockée? Parfois, il y a des UDF qui sont géniaux d'avoir comme UDF car ils peuvent être ajoutés à une requête et opérer sur l'ensemble des lignes retournées, alors que si le code était dans une procédure stockée, il devrait être placé dans un curseur afin de itérer sur un ensemble de lignes. Mais il y a des moments où vous voulez appeler cette fonction sur une seule valeur, peut-être à partir d'un autre UDF. L'appel d'un UDF pour une valeur unique peut se faire comme suit:

SELECT dbo.UDF('some value');

dans ce cas, vous obtenez une valeur de retour dans un jeu de résultats (un jeu de résultats ne fonctionnera pas). Ou cela pourrait être fait comme suit:

DECLARE @Dummy INT;

SET @Dummy = dbo.UDF('some value');

auquel cas vous devez déclarer le @Dummy variable;

TOUTEFOIS, avec la syntaxe EXEC, vous pouvez éviter ces deux désagréments:

EXEC dbo.UDF 'some value';

AUSSI, les FDU scalaires ont leurs plans d'exécution mis en cache. Cela signifie qu'il est possible de rencontrer des problèmes de détection de paramètres s'il existe des requêtes dans l'UDF qui ont des plans d'exécution. Pour les scénarios où il est possible d'utiliser la syntaxe EXEC, il est possible d'utiliser également la WITH RECOMPILE option pour ignorer la valeur compilée des plans pour cette exécution . Par exemple:

INSTALLER:

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

TESTER:

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;
15
Solomon Rutzky

Je pense que la différence dans les autorisations est parce que vous pouvez réellement invoquer des fonctions définies par l'utilisateur à valeur scalaire avec EXEC, tout comme les procédures stockées (ce que je n'avais pas réalisé avant de creuser dans la documentation en ligne de SQL Server 2000, où ils ont introduit des fonctions définies par l'utilisateur) , mais vous ne pouvez pas réellement les sélectionner comme source de table. Par exemple:

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

Dans ce cas, dbo.first_day_of_month est une fonction définie par l'utilisateur. Je ne sais pas pourquoi vous invoqueriez une fonction de cette façon, mais je suppose qu'ils avaient besoin de l'autorisation EXECUTE plutôt que SELECT pour maintenir la cohérence. De nos jours, il s'agit probablement d'un bagage de compatibilité.

4
db2