Créer une nouvelle fonction par code si elle n'existe pas

15

Je veux créer une nouvelle fonction par script dans ma base de données. Le code du script est ci-dessous:

IF Exists(Select * From sys.sysobjects A Where A.name =N'fn_myfunc' and xtype=N'FN') return;

CREATE FUNCTION fn_myfunc ()
returns varchar(10)
AS Begin
...
End

Mais lorsque j'exécute le script ci-dessus, SQL Server renvoie une erreur:

'CREATE FUNCTION' must be the first statement in a query batch.
mehdi lotfi
la source

Réponses:

16

Mise à jour de janvier 2017 - SQL Server 2016+ / Azure SQL Database

SQL Server 2016 et la version actuelle d'Azure SQL Database ont désormais la syntaxe suivante pour les fonctions, procédures, tables, bases de données, etc. ( DROP IF EXISTS):

DROP FUNCTION IF EXISTS dbo.fn_myfunc;

Et SQL Server 2016 Service Pack 1 ajoute des fonctionnalités encore meilleures pour les modules (fonctions, procédures, déclencheurs, vues), ce qui signifie aucune perte d'autorisations ou de dépendances ( CREATE OR ALTER):

CREATE OR ALTER FUNCTION dbo.fn_myfunc ...

Ces deux améliorations de la syntaxe peuvent conduire à des scripts beaucoup plus simples utilisés pour le contrôle des sources, les déploiements, etc.

Mais si vous utilisez ...


Versions plus anciennes

Vous devez faire ce que fait SQL Server lorsque vous créez un script à partir de Management Studio:

IF NOT EXISTS (SELECT 1 FROM sys.objects WHERE type = 'FN' AND name = 'fn_myfunc')
BEGIN
    DECLARE @sql NVARCHAR(MAX);
    SET @sql = N'CREATE FUNCTION ...';
    EXEC sp_executesql @sql;
END

Ou vous pouvez dire:

BEGIN TRY
    DROP FUNCTION dbo.fn_myfunc;
END TRY
BEGIN CATCH
    PRINT 'Function did not exist.';
END CATCH
GO
CREATE FUNCTION...

Ou vous pouvez simplement dire:

DROP FUNCTION dbo.fn_myfunc;
GO
CREATE FUNCTION...

(Ici, vous obtiendrez un message d'erreur si la fonction n'existe pas déjà, mais le script continuera à partir du prochain GO, donc que la suppression ait fonctionné ou non, la fonction sera toujours (re) créée.)

Notez que si vous supprimez la fonction et la recréez, vous perdrez également les autorisations et potentiellement les informations de dépendance.

Aaron Bertrand
la source
1
il n'y a donc pas de mot clé $$ CRÉER OU REMPLACER $$? pitié.
zinking
@zinking non, mais peut-être dans une future version. Veuillez voter / commenter: connect.microsoft.com/SQLServer/feedback/details/127219/…
Aaron Bertrand
1
@AaronBertrand Bonne idée de voter pour, mais le lien est rompu
bleuâtre
@bluish ouais désolé, ils ont supprimé l'élément il y a environ trois ans lorsque j'ai publié le lien et maintenant ... Essayez connect.microsoft.com/SQLServer/feedback/details/344991/… ou connect.microsoft.com/SQLServer/feedback / détails / 351217 /…
Aaron Bertrand
Cela pourrait être disponible maintenant: blogs.msdn.microsoft.com/sqlserverstorageengine/2016/11/17/…
Bruno
1

L'erreur est assez explicite. Il y a deux façons de le réparer.

  1. Séparez le script en différents lots dans Management Studio à l'aide du GOpseudo-mot clé et DROP/ de CREATEl'objet. (Notez que le mot clé lui-même peut être modifié dans les options de Management Studio, mais il s'agit du paramètre de facto, je suggère donc de le laisser seul).

    Lorsque vous exécutez un script (ou la partie sélectionnée d'un script), Management Studio sépare chaque bloc de script entre GOs et envoie séquentiellement les parties à SQL Server sous forme de lots distincts.

  2. Utilisez SQL dynamique pour envoyer un lot distinct à partir d'un autre lot.

    Il s'agit de la méthode préférée, car votre script ne dépend pas alors des fonctionnalités externes pour s'exécuter correctement. Par exemple, si votre application dispose d'un programme de mise à jour de base de données, d'une manière générale, elle charge un fichier de script puis l'exécute sur le serveur cible. Vous devrez soit ajouter une logique pour séparer les lots comme le fait Management Studio (remarque: lourde de risques), soit écrire le script de manière à ce que l' ensemble du script puisse être exécuté avec succès en un seul lot.

    Comme mentionné dans une autre réponse, vous pouvez faire un test / en CREATEutilisant cette méthode (ou une autre combinaison de DROP/ CREATE, etc.). Ce que je préfère faire, c'est créer un objet stub si l'objet n'existe pas, puis utiliserALTER <object> pour réellement faire la création ou la modification. Cette approche ne supprime pas les dépendances, telles que les autorisations ou les propriétés étendues, et il n'est pas nécessaire de copier / coller la logique sujette aux erreurs pour effectuer le CREATE/ ALTERdans une seule instruction.

    Voici le modèle que j'utilise pour créer ou modifier une fonction scalaire. Je le laisserai comme un exercice au lecteur pour l'adapter à d'autres types d'objets (procs stockés, triggers, etc.).

IF NOT EXISTS(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[<schema>].[<function name>]') AND type IN ('FN', 'FS'))
    EXEC sp_executesql N'CREATE FUNCTION [<schema name>].[<function name>] (@a int) RETURNS int AS BEGIN /* Stub */ RETURN @a END'

EXEC sp_executesql N'
ALTER FUNCTION [<schema name>].[<function name>]
/* ... */
'
Jon Seigel
la source
Comment l'option 1 peut-elle fonctionner ici? L'ajout d'un GOgarantit que le script se cassera, car le IFne mènera nulle part.
Aaron Bertrand
@Aaron: Je pensais au DROP/ CREATEscénario - édité. Merci.
Jon Seigel
1

Vous avez la possibilité de vérifier si l'objet existe dans le databaseet de créer sinon:

IF OBJECT_ID('new_function', 'FN') IS NULL
BEGIN
  EXEC('CREATE FUNCTION new_function() RETURNS INT AS BEGIN RETURN 1 END');
END;
go

ALTER FUNCTION new_function() RETURNS INT AS
BEGIN

...
Sorack
la source