Crea una nuova funzione per codice se non esiste


15

Voglio creare una nuova funzione tramite script nel mio database. Il codice dello script è di seguito:

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

Ma quando eseguo lo script sopra, SQL Server restituisce un errore:

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

Risposte:


16

Aggiornamento gennaio 2017 - SQL Server 2016+ / Database SQL di Azure

SQL Server 2016 e la versione corrente del database SQL di Azure ora hanno la sintassi seguente per funzioni, procedure, tabelle, database, ecc. ( DROP IF EXISTS):

DROP FUNCTION IF EXISTS dbo.fn_myfunc;

E SQL Server 2016 Service Pack 1 aggiunge funzionalità ancora migliori per i moduli (funzioni, procedure, trigger, viste), il che significa che non si perdono autorizzazioni o dipendenze ( CREATE OR ALTER):

CREATE OR ALTER FUNCTION dbo.fn_myfunc ...

Entrambi questi miglioramenti della sintassi possono portare a script molto più semplici utilizzati per il controllo del codice sorgente, le distribuzioni, ecc.

Ma se stai usando ...


Versioni precedenti

Devi fare ciò che fa SQL Server quando esegui lo script da 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

Oppure puoi dire:

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

Oppure puoi semplicemente dire:

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

(Qui verrà visualizzato un messaggio di errore se la funzione non esiste già, ma lo script continuerà dal GO successivo, quindi, indipendentemente dal fatto che il drop abbia funzionato o meno, la funzione verrà comunque (ri) creata.)

Se si elimina la funzione e la si ricrea, si perderanno anche le autorizzazioni e le informazioni sulla dipendenza potenziale.


1
quindi non esiste una parola chiave $$ CREA O SOSTITUISCI $$? pietà.
tintinnio il

@zinking no, ma forse in una versione futura. Si prega di votare / commentare: connect.microsoft.com/SQLServer/feedback/details/127219/…
Aaron Bertrand

1
@AaronBertrand Buona idea per votare, ma il collegamento è interrotto
bluastro

@bluish yeah mi dispiace, hanno rimosso l'elemento tra tre anni fa quando ho pubblicato il link e ora ... Prova connect.microsoft.com/SQLServer/feedback/details/344991/… o connect.microsoft.com/SQLServer/feedback / dettagli / 351217 /…
Aaron Bertrand


1

L'errore è piuttosto autoesplicativo. Ci sono un paio di modi per risolverlo.

  1. Separare lo script in diversi batch in Management Studio utilizzando la GOpseudo-parola chiave e DROP/ CREATEl'oggetto. (Si noti che la parola chiave stessa può essere modificata nelle opzioni di Management Studio, ma questa è l'impostazione di fatto, quindi suggerisco di lasciarla sola).

    Quando si esegue uno script (o la parte selezionata di uno script), Management Studio separa ogni blocco di script tra GOs e invia in sequenza le parti a SQL Server come batch separati.

  2. Utilizzare SQL dinamico per inviare un batch separato dall'interno di un altro batch.

    Questo è il metodo preferito, perché quindi lo script non dipende dalla funzionalità esterna per l'esecuzione corretta. Ad esempio, se l'applicazione ha un programma di aggiornamento del database, in genere caricherà un file di script e quindi lo eseguirà sul server di destinazione. O dovrai aggiungere la logica per separare i batch come fa Management Studio (nota: irto di pericoli) oppure scrivere lo script in modo che l' intero script possa essere eseguito correttamente come singolo batch.

    Come menzionato in un'altra risposta, puoi fare un test / CREATEusando questo metodo (o qualche altra combinazione di DROP/ CREATE, ecc.). Quello che preferisco fare è creare un oggetto stub se l'oggetto non esiste e quindi utilizzarlo ALTER <object>per eseguire effettivamente la creazione o l'alterazione. Questo approccio non elimina le dipendenze, come autorizzazioni o proprietà estese, e non è necessario copiare / incollare la logica soggetta a errori per eseguire CREATE/ ALTERin una singola istruzione.

    Ecco il modello che uso per creare o modificare una funzione scalare. Lascio che sia un esercizio per il lettore adattarlo ad altri tipi di oggetti (proc memorizzati, trigger, ecc.).

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>]
/* ... */
'

Come può funzionare l'opzione 1 qui? L'aggiunta di un GOeffettivamente garantisce la rottura dello script, dal momento IFche non porterà da nessuna parte.
Aaron Bertrand

@Aaron: stavo pensando allo scenario DROP/ CREATE- modificato. Grazie.
Jon Seigel,

1

Hai la possibilità di verificare se l'oggetto esiste in databasee creare in caso contrario:

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

...
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.