web-dev-qa-db-fra.com

Exécuter une procédure stockée à partir d'une fonction

Je sais que cela a été demandé à mort et je sais pourquoi SQL Server ne vous permet pas de le faire.

Mais existe-t-il une solution de contournement pour cela, autre que l'utilisation de procédures stockées étendues?

Et s'il vous plaît ne me dites pas de convertir ma fonction en procédure ...

Donc, ce que je demande vraiment, c'est: Y a-t-il AUCUN moyen d'exécuter une procédure stockée depuis une fonction?

MODIFIER:

Point prouvé: il y a un moyen de le contourner, mais c'est tellementFAUXje ne le ferais pas Je vais le changer en procédure stockée et l'exécuter ailleurs.

43
Smur

EDIT: Je n'ai pas essayé cela, donc je ne peux pas en garantir! Et vous savez déjà que vous ne devriez pas le faire, alors ne le faites pas s'il vous plaît. MAIS...

Essayez de chercher ici: http://sqlblog.com/blogs/denis_gobo/archive/2008/05/08/6703.aspx

Le bit clé est ce bit que j'ai essayé de modifier pour vos besoins:

DECLARE @SQL varchar(500)

SELECT @SQL = 'osql -S' +@@servername +' -E -q "exec dbName..sprocName "'

EXEC master..xp_cmdshell @SQL
26
Tom Chantler

Les fonctions ne sont pas autorisées à avoir des effets secondaires tels que la modification du contenu du tableau.

Les procédures stockées sont.

Si une fonction appelle une procédure stockée, elle pourrait avoir des effets secondaires.


Donc, désolé, mais non, vous ne pouvez pas appeler une procédure stockée à partir d'une fonction.

11
MatBailie

Une autre option, en plus d'utiliser OPENQUERY et xp_cmdshell, consiste à utiliser SQLCLR (fonctionnalité "Intégration CLR" de SQL Server). Non seulement l’option SQLCLR est plus sécurisée que les deux autres méthodes, mais l’avantage potentiel de pouvoir appeler la procédure stockée dans la session en coursobjets ou paramètres, tels que:

  • tables temporaires
  • procédures stockées temporaires
  • CONTEXT_INFO

Ceci peut être réalisé en utilisant "context connection = true;" en tant que ConnectionString. N'oubliez pas que toutes les autres restrictions imposées aux fonctions définies par l'utilisateur de T-SQL seront appliquées (c'est-à-dire qu'elles ne peuvent avoir aucun effet secondaire).

Si vous utilisez une connexion classique (c’est-à-dire sans connexion contextuelle), elle fonctionnera alors comme un appel indépendant, comme c’est le cas lorsqu’on utilise les méthodes OPENQUERY et xp_cmdshell.

CEPENDANT, n'oubliez pas que si vous souhaitez utiliser une fonction qui appelle une procédure stockée (quelle que soit la méthode notée parmi les 3 que vous utilisez) dans une instruction affectant plus d'une ligne, le comportement ne peut pas être prévu pour exécuter une fois par ligne. Comme @MartinSmith l'a mentionné dans un commentaire sur la réponse de @ MatBailie, Query Optimizer ne garantit pas le timing ni le nombre d'exécutions de fonctions. Mais si vous l'utilisez dans une instruction SET @Variable = function(); ou une requête SELECT * FROM function();, alors ça devrait aller.

Un exemple d'utilisation d'une fonction .NET/C # SQLCLR définie par l'utilisateur pour exécuter une procédure stockée est présenté dans l'article suivant (que j'ai écrit):

Escalier vers SQLCLR niveau 2: exemple de procédure stockée et de fonction

1
Solomon Rutzky

J'ai trouvé une solution à ce problème. Nous pouvons construire une fonction ou une vue avec "rendu" SQL dans une procédure stockée qui peut ensuite être exécutée normalement.

1.Créez un autre sproc

CREATE PROCEDURE [dbo].[usp_FunctionBuilder]
DECLARE @outerSql VARCHAR(MAX)
DECLARE @innerSql VARCHAR(MAX)

2.Construisez le SQL dynamique que vous voulez exécuter dans votre fonction (Exemple: vous pouvez utiliser une boucle et une union, vous pouvez lire un autre sproc, utiliser des instructions if et des paramètres pour un SQL conditionnel, etc.)

SET @innerSql = 'your sql'

3.Enveloppez le @innerSql dans une instruction de fonction create et définissez tous les paramètres externes que vous avez utilisés dans le @innerSql afin qu'ils puissent être transmis à la fonction générée.

SET @outerSql = 'CREATE FUNCTION [dbo].[fn_GeneratedFunction] ( @Param varchar(10))
RETURNS TABLE
AS
RETURN
' + @innerSql;


EXEC(@outerSql)

Il ne s’agit que d’un pseudo-code, mais la solution résout de nombreux problèmes tels que les limitations du serveur lié, les paramètres, la fonction dynamique SQL, le nom dynamique du serveur/de la base de données/de la table, les boucles, etc.

Vous aurez besoin d'ajuster à vos besoins, (Exemple: modifier le retour dans la fonction)

0
C Rudolph

Voici une autre solution de contournement possible:

if exists (select * from master..sysservers where srvname = 'loopback')
    exec sp_dropserver 'loopback'
go
exec sp_addlinkedserver @server = N'loopback', @srvproduct = N'', @provider = N'SQLOLEDB', @datasrc = @@servername
go

create function testit()
    returns int
as
begin
    declare @res int;
    select @res=count(*) from openquery(loopback, 'exec sp_who');
    return @res
end
go

select dbo.testit()

Ce n'est pas si effrayant que xp_cmdshell mais cela a aussi trop d'implications pour une utilisation pratique.

0
Vadzim