Por que funções com valor escalar precisam executar permissão em vez de selecionar?

15

Estou me perguntando por que, para a função com valor escalar, tenho que conceder ao usuário que execute, em vez de apenas uma seleção?

enquanto isso, as funções com valor de tabela funcionam bem apenas com permissão ou db_datareaderassociação de seleção .

para ficar mais claro, aqui está o meu exemplo: preciso de um usuário que tenha permissão somente de leitura no banco de dados. então criei um usuário chamado testUsere associei-o db_datareader. então eu criei uma função com valor de tabela chamada fn_InlineTable. E tudo está ótimo. testUserexecuta esse SQL o dia todo

select * from dbo.fn_InlineTable

então eu preciso de uma função escalar, então criei uma função escalar chamada fn_ScalarTest. testUsernão pode executar este SQL

Select dbo.fn_ScalarTest(1) 

Bem, compreensivelmente: é porque eu não dei permissão ao "testUser" para executar fn_ScalarTest.

Minha pergunta é: com base neste link /programming/6150888/insert-update-delete-with-function-in-sql-server , que diz que um FUNCTIONnão pode ser usado para executar ações que modificam o estado do banco de dados . Então, por que não deixar que uma função escalar seja usada com a mesma permissão "SELECT" em vez de executar a permissão?

Espero que minha pergunta faça sentido. Obrigado.

BobNoobGuy
fonte

Respostas:

15

Provavelmente, o principal motivo é que as funções com valor de tabela retornam um conjunto de resultados, assim como tabelas e visualizações. Isto significa que eles podem ser utilizados na FROMcláusula (incluindo JOINs e APPLYs, etc.) de SELECT, UPDATEe DELETEconsultas. No entanto, você não pode usar um UDF escalar em nenhum desses contextos.

Secundariamente, você também pode EXECUTEum UDF escalar. Essa sintaxe é bastante útil quando você tem valores padrão especificados para parâmetros de entrada. Pegue a seguinte UDF, por exemplo:

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

Se você quiser tratar qualquer um dos parâmetros de entrada como "opcional", ainda precisará passar a DEFAULTpalavra - chave ao chamá-la como uma função, pois a assinatura foi corrigida:

DECLARE @Bob1 INT;

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

SELECT @Bob1;
-- Returns: 102

Por outro lado, se você EXECUTEusar a função, poderá tratar qualquer parâmetro com um valor padrão como verdadeiramente opcional, assim como em Procedimentos Armazenados. Você pode passar os primeiros n parâmetros sem especificar os nomes dos parâmetros:

DECLARE @Bob2 INT;

EXEC @Bob2 = dbo.OptionalParameterTest 50;

SELECT @Bob2;
-- Returns: 52

Você pode até pular o primeiro parâmetro especificando nomes de parâmetros novamente, como nos Procedimentos armazenados:

DECLARE @Bob3 INT;

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

SELECT @Bob3;
-- Returns: 51

ATUALIZAR

Por que você deseja usar a EXECsintaxe para chamar um UDF escalar como um Procedimento Armazenado? Ocasionalmente, existem UDFs que são ótimos para ter como UDFs, pois podem ser adicionados a uma consulta e operar sobre o conjunto de linhas retornadas, enquanto que, se o código estivesse em um Procedimento armazenado, seria necessário colocá-lo em um cursor para itere sobre um conjunto de linhas. Mas há momentos em que você deseja chamar essa função em um único valor, possivelmente de outro UDF. A chamada de um UDF para um único valor pode ser feita como:

SELECT dbo.UDF('some value');

nesse caso, você obtém um valor de retorno em um conjunto de resultados (um conjunto de resultados não funciona). Ou isso pode ser feito da seguinte maneira:

DECLARE @Dummy INT;

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

nesse caso, você precisa declarar a @Dummyvariável;

NO ENTANTO, com a EXECsintaxe, você pode evitar os dois aborrecimentos:

EXEC dbo.UDF 'some value';

Além disso, as UDFs escalares têm seus planos de execução em cache. Isso significa que é possível encontrar problemas de detecção de parâmetros se houver consultas no UDF que possuam planos de execução. Para cenários em que é possível usar a EXECsintaxe, também é possível usar a WITH RECOMPILEopção de ignorar o valor compilado dos planos para essa execução . Por exemplo:

CONFIGURAÇÃO:

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

TESTE:

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;
Solomon Rutzky
fonte
4

Eu acho que a diferença nas permissões é porque você pode realmente invocar funções definidas pelo usuário com valor escalar com o EXEC, assim como procedimentos armazenados (o que eu não havia percebido até pesquisar no SQL Server 2000 Books Online, onde eles introduziram funções definidas pelo usuário) , mas você não pode realmente selecioná-los como uma fonte de tabela. Por exemplo:

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

Nesse caso, dbo.first_day_of_month é uma função definida pelo usuário. Não sei por que você jamais chamaria uma função dessa maneira, mas especularia que eles exigissem permissão EXECUTE em vez de SELECT para manter a consistência. Hoje em dia provavelmente é apenas uma bagagem de compatibilidade.

db2
fonte