Crear una nueva función por código si no existe

15

Quiero crear una nueva función por script en mi base de datos. El código del script está abajo:

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

Pero cuando ejecuto el script anterior, SQL Server devuelve un error:

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

Respuestas:

16

Actualización de enero de 2017 - SQL Server 2016+ / Azure SQL Database

SQL Server 2016 y la versión actual de Azure SQL Database ahora tienen la siguiente sintaxis para funciones, procedimientos, tablas, bases de datos, etc. ( DROP IF EXISTS):

DROP FUNCTION IF EXISTS dbo.fn_myfunc;

Y SQL Server 2016 Service Pack 1 agrega una funcionalidad aún mejor para los módulos (funciones, procedimientos, disparadores, vistas), lo que significa que no se pierden permisos ni dependencias ( CREATE OR ALTER):

CREATE OR ALTER FUNCTION dbo.fn_myfunc ...

Ambas mejoras de sintaxis pueden conducir a secuencias de comandos mucho más simples que se utilizan para el control de origen, implementaciones, etc.

Pero, si estás usando ...


versiones anteriores

Debe hacer lo que hace SQL Server cuando escribe esto desde 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

O puedes decir:

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

O simplemente puedes decir:

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

(Aquí recibirá un mensaje de error si la función aún no existe, pero la secuencia de comandos continuará desde el próximo GO, por lo que si la caída funcionó o no, la función aún se volverá a crear).

Tenga en cuenta que si suelta la función y la vuelve a crear, también perderá permisos e información de dependencia potencial.

Aaron Bertrand
fuente
1
entonces no hay una palabra clave $$ ¿CREAR O REEMPLAZAR $$? lástima.
Zinking
@ zinking no, pero tal vez en una versión futura. Vote / comente: connect.microsoft.com/SQLServer/feedback/details/127219/…
Aaron Bertrand
1
@AaronBertrand Buena idea votar por él, pero el enlace está roto
azulado
@ azulado sí lo siento, eliminaron el elemento en algún momento entre hace tres años cuando publiqué el enlace y ahora ... Intente connect.microsoft.com/SQLServer/feedback/details/344991/… o connect.microsoft.com/SQLServer/feedback / detalles / 351217 /…
Aaron Bertrand
Esto podría estar disponible ahora: blogs.msdn.microsoft.com/sqlserverstorageengine/2016/11/17/…
Bruno
1

El error se explica por sí mismo. Hay un par de formas de arreglarlo.

  1. Separe el script en diferentes lotes en Management Studio usando la GOpseudo-palabra clave y DROP/ CREATEel objeto. (Tenga en cuenta que la palabra clave en sí misma se puede cambiar en las opciones de Management Studio, pero esta es la configuración de facto, por lo que sugiero dejarla sola).

    Cuando ejecuta una secuencia de comandos (o la parte seleccionada de una secuencia de comandos), Management Studio separa cada fragmento de secuencia de comandos entre GOsy envía secuencialmente las partes a SQL Server como lotes separados.

  2. Use SQL dinámico para enviar un lote separado desde otro lote.

    Este es el método preferido, porque su script no depende de la funcionalidad externa para ejecutarse correctamente. Por ejemplo, si su aplicación tiene un programa de actualización de la base de datos, en general cargará un archivo de script y luego lo ejecutará en el servidor de destino. Deberá agregar lógica para separar los lotes como lo hace Management Studio (nota: lleno de peligros), o escribir el script de manera que todo el script pueda ejecutarse con éxito como un solo lote.

    Como se menciona en otra respuesta, puede hacer una prueba / CREATEusar este método (o alguna otra combinación de DROP/ CREATE, etc.). Lo que prefiero hacer es crear un objeto auxiliar si el objeto no existe, y luego usarlo ALTER <object>para hacer la creación o alteración. Este enfoque no elimina las dependencias, como los permisos o las propiedades extendidas, y no es necesario copiar / pegar la lógica propensa a errores para hacer CREATE/ ALTERen una sola declaración.

    Aquí está la plantilla que uso para crear o cambiar una función escalar. Lo dejaré como un ejercicio para que el lector adapte esto a otros tipos de objetos (procesos almacenados, disparadores, 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
fuente
¿Cómo puede funcionar la opción 1 aquí? Agregar un archivo GOrealmente garantiza que el script se romperá, ya IFque no llevará a ninguna parte.
Aaron Bertrand
@ Aaron: Estaba pensando en el escenario DROP/ CREATEeditado. Gracias.
Jon Seigel
1

Tiene la opción de verificar si el objeto existe en el databasey crear si no:

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
fuente