Puis-je créer une fonction à usage unique dans un script ou une procédure stockée?

109

Dans SQL Server 2005, existe-t-il un concept d'utilisation unique ou de fonction locale déclarée à l'intérieur d'un script SQL ou d'une procédure stockée? Je voudrais résumer une certaine complexité dans un script que j'écris, mais il faudrait être capable de déclarer une fonction.

Juste curieux.

Mark Carpenter
la source
il existe probablement une meilleure façon de faire ce que vous voulez sans fonction. peut-être devriez-vous publier un extrait du code que vous souhaitez transformer en fonction?
DForck42
générez-vous une fonction dynamiquement pour qu'elle soit différente à chaque fois? si votre fonction est toujours la même, laissez-la simplement dans la base de données
KM.
1
J'essayais de le faire pour rendre la requête plus lisible. L'idée de créer d'énormes requêtes est difficile à maintenir.
Jp_

Réponses:

65

Vous pouvez appeler CREATE Functionvers le début de votre script et DROP Functionvers la fin.

Joel Coehoorn
la source
6
J'allais suggérer ceci. Faites juste attention que votre script se termine; s'il abandonne, vous aurez toujours la fonction dans la base de données.
chocojosh
6
Vous pouvez effectuer une vérification IF EXISTS avant chaque exécution et supprimer si quelque chose est trouvé.
Adrian Godong le
7
@chocojosh, cela devrait être correct si vous l'enveloppez dans une transaction. La fonction ne devrait pas être dans la base de données si la transaction bombe.
Jeff LaFay
12
@JoelCoehoorn: cela nécessite toujours des privilèges d'écriture.
user2284570
2
Notez que cela ne fonctionnera pas à l'intérieur d'une fonction - les fonctions temporaires à l'intérieur des fonctions ne sont pas autorisées. Voir: technet.microsoft.com/en-us/library/ms191320.aspx#Restrictions
Daniel Neel
95

Vous pouvez créer des procédures stockées temporaires telles que:

create procedure #mytemp as
begin
   select getdate() into #mytemptable;
end

dans un script SQL, mais pas des fonctions. Vous pourriez cependant avoir le stockage de proc pour obtenir une table temporaire, puis utiliser ces informations plus tard dans le script.

Ron Savage
la source
7
Cela devrait être la réponse. Ceci est vraiment à usage unique si seule la connexion est temporaire (# unique), et présente l'avantage de contourner les restrictions utilisateur SQL.
Todd
Comment est-il utilisé alors? N'est-ce pas une faute de frappe dans le nom de la procédure utilisée dans l'expression select into?
jgomo3
Je peux obtenir les résultats de votre exemple de procédure stockée lorsque je supprime le BEGINmot - clé et que je remplace le ENDmot - clé par GO.
Joseph Dykstra
L'OP demandait une FONCTION temporaire et au moins SQL Server 2012 n'autorisera pas la # -syntaxe pour les fonctions. Seules les procédures.
Erk
Cela ne fonctionne pas dans un script et peut encore nécessiter des autorisations. Afin d'éviter les segments répétitifs, la seule option de SQL est l'instruction WITH.
alex.peter
25

Les expressions de table communes vous permettent de définir ce qui sont essentiellement des vues qui ne durent que dans le cadre de vos instructions de sélection, d'insertion, de mise à jour et de suppression. En fonction de ce que vous devez faire, ils peuvent être extrêmement utiles.

Welbog
la source
5
Cela devrait être accepté comme une bonne réponse. La réponse acceptée n'est pas thread-safe.
kalyan
11
Cela dépend de ce que vous essayez de faire. J'ai trouvé cette question parce que j'écris un semoir de données et que je ne veux pas répéter 10 lignes de MERGE INTO 30 fois. Je me fiche de threadsafe et les CTE ne fonctionneront pas pour moi.
solipsicle
16
Je pense que cette réponse, et les affirmations selon lesquelles c'est la bonne réponse, manquent que la question recherche une FONCTION temporaire, pas une table temporaire. Sauf si je manque quelque chose (pas rare), les CTE sont comparables aux tables temporaires.
JD Long
8
Une fonction peut prendre des arguments alors qu'un CTE ne le peut pas.
Răzvan Flavius ​​Panda
4
Il existe de nombreuses différences entre un CTE et une procédure stockée temporaire (qui est la bonne réponse ici IMO). Pour commencer, les CTE n'existent que pour une seule instruction, tandis que les variables temporaires peuvent être utilisées dans un script. D'autres différences incluent: (1) les CTE ne peuvent pas héberger la même logique qu'un SP peut, (2) les CTE ne peuvent pas accepter de variables. Un CTE est juste un sucre syntaxique pour vous permettre de créer plus facilement des expressions de table imbriquées à utiliser dans une instruction. Même dans ce cas, ils peuvent être dangereux en termes de performances si vous ne connaissez pas les mises en garde.
Déséquilibré le
12

Je sais que je pourrais être critiqué pour avoir suggéré du SQL dynamique, mais c'est parfois une bonne solution. Assurez-vous simplement de comprendre les implications en matière de sécurité avant d'envisager cela.

DECLARE @add_a_b_func nvarchar(4000) = N'SELECT @c = @a + @b;';
DECLARE @add_a_b_parm nvarchar(500) = N'@a int, @b int, @c int OUTPUT';

DECLARE @result int;
EXEC sp_executesql @add_a_b_func, @add_a_b_parm, 2, 3, @c = @result OUTPUT;
PRINT CONVERT(varchar, @result); -- prints '5'
Tmdean
la source
4

Dans les scripts, vous avez plus d'options et une meilleure idée de la décomposition rationnelle. Regardez dans le mode SQLCMD (Menu Requête -> Mode SQLCMD), en particulier les commandes: setvar et: r.

Dans une procédure stockée, vos options sont très limitées. Vous ne pouvez pas créer de définir une fonction directement avec le corps d'une procédure. Le mieux que vous puissiez faire est quelque chose comme ceci, avec SQL dynamique:

create proc DoStuff
as begin

  declare @sql nvarchar(max)

  /*
  define function here, within a string
  note the underscore prefix, a good convention for user-defined temporary objects
  */
  set @sql = '
    create function dbo._object_name_twopart (@object_id int)
    returns nvarchar(517) as
    begin
      return 
        quotename(object_schema_name(@object_id))+N''.''+
        quotename(object_name(@object_id))
    end
  '

  /*
  create the function by executing the string, with a conditional object drop upfront
  */
  if object_id('dbo._object_name_twopart') is not null drop function _object_name_twopart
  exec (@sql)

  /*
  use the function in a query
  */
  select object_id, dbo._object_name_twopart(object_id) 
  from sys.objects
  where type = 'U'

  /*
  clean up
  */
  drop function _object_name_twopart

end
go

Cela se rapproche d'une fonction temporaire globale, si une telle chose existait. Il est toujours visible pour les autres utilisateurs. Vous pouvez ajouter le @@ SPID de votre connexion pour unifier le nom, mais cela nécessiterait alors que le reste de la procédure utilise également le SQL dynamique.

Peter Radocchia
la source
3

Voici ce que j'ai utilisé dans le passé pour répondre au besoin d'un UDF scalaire dans MS SQL:

IF OBJECT_ID('tempdb..##fn_Divide') IS NOT NULL DROP PROCEDURE ##fn_Divide
GO
CREATE PROCEDURE ##fn_Divide (@Numerator Real, @Denominator Real) AS
BEGIN
    SELECT Division =
        CASE WHEN @Denominator != 0 AND @Denominator is NOT NULL AND  @Numerator != 0 AND @Numerator is NOT NULL THEN
        @Numerator / @Denominator
        ELSE
            0
        END
    RETURN
END
GO

Exec ##fn_Divide 6,4

Cette approche qui utilise une variable globale pour la PROCÉDURE vous permet d'utiliser la fonction non seulement dans vos scripts, mais aussi dans vos besoins Dynamic SQL.

Gregory Hart
la source