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.
Mise à jour 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 antérieures
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 (recréée).)
Notez que si vous supprimez la fonction et la recréez, vous perdrez également les autorisations et potentiellement les informations de dépendance.
Vous avez la possibilité de vérifier si l'objet existe dans le database
et 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
...
L'erreur est assez explicite. Il y a deux façons de le réparer.
Séparez le script en différents lots dans Management Studio à l'aide du pseudo-mot clé GO
et DROP
/CREATE
l'objet. (Notez que le mot clé lui-même peut être modifié dans les options de Management Studio, mais c'est le paramètre de facto, donc je suggère de le laisser tranquille).
Lorsque vous exécutez un script (ou la partie sélectionnée d'un script), Management Studio sépare chaque bloc de script entre GO
s et envoie séquentiellement les parties à SQL Server en tant que lots distincts.
tilisez le SQL dynamique pour envoyer un lot séparé à 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 chargera un fichier de script puis l'exécutera sur le serveur cible. Soit vous devrez ajouter une logique pour séparer les lots comme le fait Management Studio (note: lourde de risques), soit écrire le script de manière à ce que entier le script peut être exécuté avec succès en un seul lot.
Comme mentionné dans une autre réponse, vous pouvez faire un test/CREATE
en utilisant 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 utiliser ALTER <object>
pour effectuer 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
/ALTER
dans une seule instruction.
Voici le modèle que j'utilise pour créer ou modifier une fonction scalaire. Je vais le laisser comme un exercice au lecteur pour l'adapter à d'autres types d'objets (procs stockés, déclencheurs, 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>]
/* ... */
'