Erstellen Sie eine neue Funktion durch Code, falls dieser nicht existiert

15

Ich möchte per Skript eine neue Funktion in meiner Datenbank anlegen. Der Skriptcode ist unten:

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

Wenn ich das obige Skript ausführe, gibt SQL Server einen Fehler zurück:

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

Antworten:

16

Update Januar 2017 - SQL Server 2016+ / Azure SQL-Datenbank

SQL Server 2016 und die aktuelle Version der Azure SQL-Datenbank haben jetzt die folgende Syntax für Funktionen, Prozeduren, Tabellen, Datenbanken usw. ( DROP IF EXISTS):

DROP FUNCTION IF EXISTS dbo.fn_myfunc;

SQL Server 2016 Service Pack 1 bietet noch bessere Funktionen für Module (Funktionen, Prozeduren, Trigger, Ansichten), sodass keine Berechtigungen oder Abhängigkeiten verloren gehen ( CREATE OR ALTER):

CREATE OR ALTER FUNCTION dbo.fn_myfunc ...

Diese beiden Syntaxverbesserungen können zu wesentlich einfacheren Skripten führen, die für die Quellcodeverwaltung, Bereitstellungen usw. verwendet werden.

Aber wenn Sie verwenden ...


ältere Versionen

Sie müssen die Schritte von SQL Server ausführen, wenn Sie diese in Management Studio ausführen:

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

Oder du kannst sagen:

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

Oder Sie können einfach sagen:

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

(Hier wird eine Fehlermeldung angezeigt, wenn die Funktion noch nicht vorhanden ist. Das Skript wird jedoch beim nächsten Start fortgesetzt. Unabhängig davon, ob das Löschen funktioniert hat oder nicht, wird die Funktion dennoch (neu) erstellt.)

Beachten Sie, dass beim Löschen und erneuten Erstellen der Funktion Berechtigungen und möglicherweise auch Abhängigkeitsinformationen verloren gehen.

Aaron Bertrand
quelle
1
Also gibt es kein Schlüsselwort $$ ERSTELLEN ODER ERSETZEN $$? das Mitleid.
Zinking
@zinking nein, aber vielleicht in einer zukünftigen Version. Bitte abstimmen / kommentieren: connect.microsoft.com/SQLServer/feedback/details/127219/…
Aaron Bertrand
1
@ AaronBertrand Gute Idee, dafür zu stimmen, aber die Verbindung ist unterbrochen
bläulich
@bluish yeah sorry, sie haben das Element vor drei Jahren entfernt, als ich den Link gepostet habe und jetzt ... Versuchen Sie, connect.microsoft.com/SQLServer/feedback/details/344991/… oder connect.microsoft.com/SQLServer/feedback / details / 351217 /…
Aaron Bertrand
Dies könnte jetzt verfügbar sein: blogs.msdn.microsoft.com/sqlserverstorageengine/2016/11/17/…
Bruno
1

Der Fehler ist ziemlich selbsterklärend. Es gibt verschiedene Möglichkeiten, das Problem zu beheben.

  1. Teilen Sie das Skript in Management Studio mithilfe des GOPseudo-Schlüsselworts und DROP/ oder CREATEdes Objekts in verschiedene Stapel auf . (Beachten Sie, dass das Schlüsselwort selbst in den Management Studio-Optionen geändert werden kann, dies ist jedoch de facto die Einstellung. Ich empfehle daher, das Schlüsselwort in Ruhe zu lassen.)

    Wenn Sie ein Skript (oder den ausgewählten Teil eines Skripts) ausführen, trennt Management Studio jeden Skriptblock zwischen GOs und sendet die Teile nacheinander als separate Stapel an SQL Server.

  2. Verwenden Sie dynamisches SQL, um einen separaten Stapel aus einem anderen Stapel heraus zu senden.

    Dies ist die bevorzugte Methode, da dann die korrekte Ausführung Ihres Skripts nicht von externen Funktionen abhängt. Wenn Ihre Anwendung beispielsweise über ein Programm zur Datenbankaktualisierung verfügt, wird in der Regel eine Skriptdatei geladen und anschließend auf dem Zielserver ausgeführt. Entweder müssen Sie Logik hinzufügen, um die Stapel wie in Management Studio zu trennen (Hinweis: Mit Gefahren behaftet), oder Sie müssen das Skript so schreiben, dass das gesamte Skript erfolgreich als einzelner Stapel ausgeführt werden kann.

    Wie in einer anderen Antwort erwähnt, können Sie CREATEmit dieser Methode (oder einer anderen Kombination von DROP/ CREATEusw.) einen Test durchführen . Am liebsten erstelle ich ein Stub-Objekt, wenn das Objekt nicht vorhanden ist, und erstelle ALTER <object>oder ändere es dann tatsächlich. Bei diesem Ansatz werden keine Abhängigkeiten gelöscht, z. B. Berechtigungen oder erweiterte Eigenschaften, und es ist nicht erforderlich, fehleranfällige Logik zu kopieren / einzufügen, um das CREATE/ ALTERin einer einzelnen Anweisung auszuführen.

    Hier ist die Vorlage, die ich zum Erstellen oder Ändern einer Skalarfunktion verwende. Ich überlasse es dem Leser als Übung, dies an andere Arten von Objekten (gespeicherte Prozesse, Trigger usw.) anzupassen.

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
quelle
Wie kann Option 1 hier funktionieren? Das Hinzufügen von a GOstellt sicher, dass das Skript nicht mehr funktioniert, da IFes nirgendwo hinführt.
Aaron Bertrand
@Aaron: Ich habe über das DROP/ CREATEDrehbuch nachgedacht - bearbeitet. Vielen Dank.
Jon Seigel
1

Sie haben die Möglichkeit zu prüfen, ob das Objekt in der existiert databaseund wenn nicht, anzulegen:

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
quelle