Crie uma nova função por código, se ela não existir

15

Quero criar uma nova função por script no meu banco de dados. O código do script está abaixo:

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

Mas quando executo o script acima, o SQL Server retorna um erro:

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

Respostas:

16

Atualização de janeiro de 2017 - SQL Server 2016+ / Banco de Dados SQL do Azure

O SQL Server 2016 e a versão atual do Banco de Dados SQL do Azure agora têm a seguinte sintaxe para funções, procedimentos, tabelas, bancos de dados etc. ( DROP IF EXISTS):

DROP FUNCTION IF EXISTS dbo.fn_myfunc;

E o SQL Server 2016 Service Pack 1 adiciona funcionalidades ainda melhores aos módulos (funções, procedimentos, gatilhos, exibições), o que significa que não há perda de permissões ou dependências ( CREATE OR ALTER):

CREATE OR ALTER FUNCTION dbo.fn_myfunc ...

Ambos os aprimoramentos de sintaxe podem levar a scripts muito mais simples usados ​​para controle de origem, implantações etc.

Mas, se você estiver usando ...


versões mais antigas

Você precisa fazer o que o SQL Server faz ao criar um script no 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 você pode dizer:

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

Ou você pode apenas dizer:

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

(Aqui você receberá uma mensagem de erro se a função ainda não existir, mas o script continuará a partir do próximo GO, portanto, se a queda funcionou ou não, a função ainda será (re) criada.)

Observe que, se você soltar a função e recriá-la, também perderá permissões e informações potencialmente sobre dependência.

Aaron Bertrand
fonte
1
portanto, não há palavra-chave $$ CRIAR OU SUBSTITUIR $$? pena.
zinking
@ zinking não, mas talvez em uma versão futura. Vote
Aaron Bertrand
1
@AaronBertrand Boa ideia de voto para ele, mas ligação é interrompida
azulada
@bluish sim desculpe, eles removeram o item em algum momento entre três anos atrás, quando eu publiquei o link e agora ... Tente connect.microsoft.com/SQLServer/feedback/details/344991/… ou connect.microsoft.com/SQLServer/feedback / detalhes / 351217 /…
Aaron Bertrand
Isso pode estar disponível agora: blogs.msdn.microsoft.com/sqlserverstorageengine/2016/11/17/…
Bruno
1

O erro é bastante auto-explicativo. Existem algumas maneiras de corrigi-lo.

  1. Separe o script em lotes diferentes no Management Studio usando a GOpseudo-palavra-chave e DROP/ CREATEo objeto. (Observe que a própria palavra-chave pode ser alterada nas opções do Management Studio, mas essa é a configuração de fato, por isso sugiro deixá-la em paz).

    Quando você executa um script (ou a parte selecionada de um script), o Management Studio separa cada parte do script entre GOs e envia seqüencialmente as partes para o SQL Server como lotes separados.

  2. Use SQL dinâmico para enviar um lote separado de outro lote.

    Esse é o método preferencial, pois seu script não depende da funcionalidade externa para ser executado corretamente. Por exemplo, se seu aplicativo tiver um programa de atualização de banco de dados, geralmente ele carregará um arquivo de script e o executará no servidor de destino. Você precisará adicionar lógica para separar os lotes como o Management Studio (nota: repleta de riscos) ou gravar o script de forma que o script inteiro possa ser executado com sucesso como um único lote.

    Como mencionado em outra resposta, você pode fazer um teste / CREATEusando esse método (ou alguma outra combinação de DROP/ CREATE, etc.). O que eu prefiro fazer é criar um objeto stub se o objeto não existir e, em seguida, usar ALTER <object>para realmente criar ou alterar. Essa abordagem não elimina dependências, como permissões ou propriedades estendidas, e não é necessário copiar / colar uma lógica propensa a erros para executar o CREATE/ ALTERem uma única instrução.

    Aqui está o modelo que eu uso para criar ou alterar uma função escalar. Vou deixar como um exercício para o leitor adaptar isso a outros tipos de objetos (procs armazenados, gatilhos, 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
fonte
Como a opção 1 pode funcionar aqui? A adição de um GOgarante que o script será interrompido, pois IFnão levará a lugar algum.
Aaron Bertrand
@ Aaron: Eu estava pensando no cenário DROP/ CREATE- editado. Obrigado.
precisa
1

Você tem a opção de verificar se o objeto existe no databasee criar se não:

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
fonte