Cómo verificar si existe un procedimiento almacenado antes de crearlo

282

Tengo un script SQL que debe ejecutarse cada vez que un cliente ejecuta la funcionalidad de "gestión de bases de datos". El script incluye la creación de procedimientos almacenados en la base de datos del cliente. Es posible que algunos de estos clientes ya tengan el procedimiento almacenado al ejecutar el script, y algunos no. Necesito agregar los procedimientos almacenados que faltan a la base de datos del cliente, pero no importa cuánto intente doblar la sintaxis T-SQL, obtengo

CREATE / ALTER PROCEDURE 'debe ser la primera instrucción en un lote de consulta

Lo he leído antes de crear trabajos, pero no me gusta hacerlo de esa manera.

IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'MyProc')
DROP PROCEDURE MyProc
GO

CREATE PROCEDURE MyProc
...

¿Cómo puedo agregar una verificación de la existencia de un procedimiento almacenado y crearlo si no existe, pero modificarlo si existe?

El moldeador
fuente
2
no, no funciona, porque eso crea un procedimiento almacenado que supuestamente no es lo que desea. por lo que podemos ver, tampoco lo deja caer una vez hecho, por lo que definitivamente se almacena en todos los aspectos del término. es no irrelevante por qué es necesario un procedimiento no almacena
David Hedlund
¿Qué quiere decir con procedimiento 'no almacenado'? Todo lo que hace su muestra es recrear un procedimiento almacenado; ¿Qué tiene esto que ver con tu pregunta?
AakashM
Ok, ahí vamos. La cuestión es que tengo un script SQL ENORME que utilizan muchos clientes y que se debe ejecutar a fondo cada vez que un cliente ejecuta la funcionalidad de "administración de bases de datos" que proporciona nuestro software. Por lo tanto, es posible que algunos de estos clientes ya tengan el procedimiento almacenado al ejecutar el script, y otros no. Sé que esto es estúpido, en realidad no necesito este procedimiento para permanecer sin almacenar, solo puedo verificar si existe y crearlo si no es así. Sin embargo, no importa cuánto intente doblar la sintaxis T-SQL, siempre hay un error.
The Shaper
Cada vez que ejecutan el script, intentará crear el procedimiento nuevamente (desafortunadamente, todo tiene que estar programado en el mismo archivo .sql, incluida la llamada al procedimiento de creación). SI NO EXISTE LUEGO CREAR no funciona debido a limitaciones de sintaxis. ¿Que puedo hacer?
The Shaper
3
Posible duplicado de stackoverflow.com/questions/937908/…
Michael Freidgeim

Respuestas:

199

Puede ejecutar código de procedimiento en cualquier lugar donde pueda ejecutar una consulta.

Simplemente copie todo después de AS:

BEGIN
    DECLARE @myvar INT
    SELECT  *
    FROM    mytable
    WHERE   @myvar ...
END

Este código hace exactamente lo mismo que haría un proceso almacenado, pero no se almacena en el lado de la base de datos.

Eso es muy parecido a lo que se llama procedimiento anónimo en PL/SQL.

Actualizar:

El título de su pregunta es un poco confuso.

Si solo necesita crear un procedimiento si no existe, entonces su código está bien.

Esto es lo que se SSMSmuestra en el script de creación:

IF EXISTS ( SELECT  *
            FROM    sys.objects
            WHERE   object_id = OBJECT_ID(N'myproc')
                    AND type IN ( N'P', N'PC' ) ) 
DROP 
CREATE 

Actualizar:

Ejemplo de cómo hacerlo cuando se incluye el esquema:

IF EXISTS ( SELECT * 
            FROM   sysobjects 
            WHERE  id = object_id(N'[dbo].[MyProc]') 
                   and OBJECTPROPERTY(id, N'IsProcedure') = 1 )
BEGIN
    DROP PROCEDURE [dbo].[MyProc]
END

En el ejemplo anterior, dbo es el esquema.

Actualizar:

En SQL Server 2016+, solo puedes hacer

CREATE OR ALTER PROCEDURE dbo.MyProc

Quassnoi
fuente
Sí, esto es cierto, pero perderá toda la funcionalidad de los procedimientos, ya que no se almacenará ningún procedimiento, udfs, vistas, etc. (Lo siento, lo edité, tenía sentido en mi cabeza X-))
Adriaan Stander
1
Sí, pero puede llamar a procedimientos desde otros procedimientos, o usar su devolución como entrada a una tabla.
Adriaan Stander
@astander: también puede llamar a código anónimo desde los procedimientos almacenados. Para usar su salida en un INSERT, necesitará usar OPENROWSETo OPENQUERYque también funcione con el código anónimo. Por supuesto, hay inconvenientes en el código anónimo: por ejemplo, solo se ejecuta bajo los privilegios de la persona que llama. Mi punto es que es posible, no es la forma preferida de hacer las cosas :)
Quassnoi
"Si solo necesita crear un procedimiento si no existe, entonces su código está bien". Y eso es exactamente lo que quería saber. Traté de usar SSMS Create en el script real pero no sirvió de nada. Pero gracias Quassnoi, y lamento la pregunta poco clara.
The Shaper
2
Una instrucción CREATE PROC debe ser la única instrucción en un lote cuando no se utiliza SQL dinámico, por lo que no puede ajustar una transacción alrededor de DROP / CREATE cuando se implementa de esta manera. Tiene que haber un GO (separador de lotes) después de la llamada DROP PROC.
Shiv
449

Me doy cuenta de que esto ya se ha marcado como respondido, pero solíamos hacerlo así:

IF NOT EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND OBJECT_ID = OBJECT_ID('dbo.MyProc'))
   exec('CREATE PROCEDURE [dbo].[MyProc] AS BEGIN SET NOCOUNT ON; END')
GO

ALTER PROCEDURE [dbo].[MyProc] 
AS
  ....

Solo para evitar perder el procedimiento.

Geoff
fuente
74
Solo para agregar algunas notas sobre por qué es una buena idea: 1) una caída borrará cualquier configuración de seguridad, 2) al hacerlo de esta manera, si el script alter falla por alguna razón, la sp no se habrá eliminado.
Ryan Guill
10
Esta es realmente la respuesta correcta. Evita perder las SUBVENCIONES en el proceso almacenado en cuestión.
Andy_Vulhop
77
Hay un gran beneficio para este enfoque, ya que no existe un punto en el tiempo cuando el procedimiento almacenado no existe. Esto puede ser crucial si la actualización se aplica a un sistema crítico mientras otras personas, sistemas o subprocesos todavía la usan. Rastrear los errores causados ​​por la caída momentánea de un procedimiento almacenado puede ser bastante molesto porque son muy difíciles de reproducir.
James
3
Esta es una gran solución por muchas razones ya mencionadas, y me gustaría agregar que, en caso de que los DBA se basen en metadatos de proceso (como fecha de creación), esto deja esas cosas intactas, en lugar de hacer el proceso nuevo cada vez. Estoy tratando de que esto se convierta en la "mejor práctica" de mi equipo para mantener nuestros propios procedimientos, que generalmente tienen que copiarse / propagarse a varios DB.
NateJ
2
También considere que algunas personas desean que las GRANTdeclaraciones sean explícitas en el script en caso de que cambien ; así que todavía hay justificación para usar en DROPlugar de ALTER.
Cody Stott
123

Si está buscando la forma más simple de verificar la existencia de un objeto de base de datos antes de eliminarlo, aquí hay una manera (el ejemplo usa un SPROC, al igual que su ejemplo anterior, pero podría modificarse para tablas, índices, etc.):

IF (OBJECT_ID('MyProcedure') IS NOT NULL)
  DROP PROCEDURE MyProcedure
GO

Esto es rápido y elegante, pero debe asegurarse de tener nombres de objeto únicos en todos los tipos de objetos, ya que no lo tiene en cuenta.

¡Espero que esto ayude!

MrChips
fuente
62
Eso mejor: SI (OBJECT_ID ('MyProcedure', 'P') NO ES NULO) PROCEDIMIENTO DE GOTA MyProcedure GO
alerya
32

Sé que desea "alterar un procedimiento si existe y solo eliminarlo si no existe", pero creo que es más simple simplemente abandonar el procedimiento y volver a crearlo. Aquí le mostramos cómo descartar el procedimiento solo si ya existe:

IF OBJECT_ID('MyProcedure', 'P') IS NOT NULL
    DROP PROCEDURE MyProcedure
GO

El segundo parámetro le dice OBJECT_IDque solo busque objetos con object_type = 'P', que son procedimientos almacenados:

AF = Función agregada (CLR)

C = restricción CHECK

D = PREDETERMINADO (restricción o independiente)

F = restricción de CLAVE EXTRANJERA

FN = función escalar SQL

FS = función escalar de ensamblaje (CLR)

FT = Función con valor de tabla de ensamblaje (CLR)

IF = función en línea con valores de tabla SQL

IT = tabla interna

P = Procedimiento almacenado de SQL

PC = Procedimiento almacenado de ensamblaje (CLR)

PG = Guía del plan

PK = restricción de CLAVE PRIMARIA

R = Regla (estilo antiguo, independiente)

RF = Replicación-procedimiento-filtro

S = tabla base del sistema

SN = Sinónimo

SO = objeto de secuencia

TF = función de valor de tabla SQL

TR = disparador

Puede obtener la lista completa de opciones a través de:

SELECT name 
FROM master..spt_values
WHERE type = 'O9T'
Michael Currie
fuente
1
Falta TF. Aún así, +1 por proporcionar esta lista
Crono
También TR para Trigger
CarlosOro
23

Sé que es una publicación muy antigua, pero dado que esto aparece en los principales resultados de búsqueda, por lo tanto, agrego la última actualización para aquellos que usan SQL Server 2016 SP1 :

create or alter procedure procTest
as
begin
 print (1)
end;
go

Esto crea un Procedimiento almacenado si aún no existe, pero lo modifica si existe.

Referencia

gkb
fuente
1
Esto es muy, muy útil.
AgentFire
Quiero enfatizar que esto solo funciona en SQL Studio: en un archivo sql falla para mí.
James L.
10

DROP IF EXISTS es una nueva característica de SQL Server 2016

https://blogs.msdn.microsoft.com/sqlserverstorageengine/2015/11/03/drop-if-exists-new-thing-in-sql-server-2016/

DROP  PROCEDURE IF EXISTS dbo.[procname]
Jay Jay
fuente
1
esta no es la sintaxis de SqlServer ..., consejo para eliminar la respuesta antes de que los chicos comiencen a votar negativamente y para evitar confusiones para los novatos.
Pawel Czapski
@PawelCz es válido para SQL Server 2016 y versiones posteriores, reformulé la respuesta. ¡Gracias por la respuesta!
JayJay
Esto no responde a la publicación original. Hay una sutil diferencia entre soltar y recrear automáticamente y crear solo si no existe. Eliminar un proceso eliminará la seguridad asociada con él, que puede haber sido programada.
Ron
7

Yo tenía el mismo error. Sé que este hilo ya está muerto, pero quiero establecer otra opción además del "procedimiento anónimo".

Lo resolví así:

  1. Compruebe si existe el procedimiento almacenado:

    IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='my_procedure') BEGIN
        print 'exists'  -- or watever you want
    END ELSE BEGIN
        print 'doesn''texists'   -- or watever you want
    END
  2. Sin embargo, el "CREATE/ALTER PROCEDURE' must be the first statement in a query batch"todavía está allí. Lo resolví así:

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE -- view procedure function or anything you want ...
  3. Termino con este código:

    IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID('my_procedure'))
    BEGIN
        DROP PROCEDURE my_procedure
    END
    
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE PROCEDURE [dbo].my_procedure ...
Oaxas
fuente
No necesita el comienzo y el final si es solo 1 línea de código como PROCEDIMIENTO DE GOTA ...
Phillip Senn
Advertencia: la función 'comprobar si el procedimiento almacenado existe' siempre devolverá 'existe', sin importar el nombre de la función que ingrese (para T-SQL). Es un cheque poco confiable.
Ryan Battistone
Una alternativa mejor: SI EXISTE (SELECCIONE 1 DE sys.procedures WHERE name = 'name_of_table_as_seen_in_sysprocedures') COMIENCE seleccione -1 como 'estado' FIN
Ryan Battistone
5

Aquí hay un método y algunas razones para usarlo de esta manera. No es tan bonito editar el proceso almacenado, pero hay ventajas y desventajas ...

ACTUALIZACIÓN: También puede completar esta llamada completa en una TRANSACCIÓN. Incluyendo muchos procedimientos almacenados en una sola transacción que pueden comprometerse o revertirse. ¡Otra ventaja de envolver en una transacción es que el procedimiento almacenado siempre existe para otras conexiones SQL, siempre y cuando no utilicen el nivel de aislamiento de transacción LEÍDO NO COMPROMETIDO!

1) Para evitar alteraciones solo como una decisión del proceso. Nuestros procesos son siempre SI EXISTE GOTA LUEGO CREAR. Si sigue el mismo patrón de suponer que el nuevo PROC es el proceso deseado, atender las modificaciones es un poco más difícil porque tendría un SI EXISTE ALTERAR OTRA CREACIÓN.

2) Debe colocar CREATE / ALTER como la primera llamada en un lote para que no pueda ajustar una secuencia de actualizaciones de procedimientos en una transacción fuera de SQL dinámico. Básicamente, si desea ejecutar una pila completa de actualizaciones de procedimientos o revertirlas todas sin restaurar una copia de seguridad de la base de datos, esta es una manera de hacer todo en un solo lote.

IF NOT EXISTS (select ss.name as SchemaName, sp.name as StoredProc 
    from sys.procedures sp
    join sys.schemas ss on sp.schema_id = ss.schema_id
    where ss.name = 'dbo' and sp.name = 'MyStoredProc')
BEGIN
    DECLARE @sql NVARCHAR(MAX)

    -- Not so aesthetically pleasing part. The actual proc definition is stored
    -- in our variable and then executed.
    SELECT @sql = 'CREATE PROCEDURE [dbo].[MyStoredProc]
(
@MyParam int
)
AS
SELECT @MyParam'
    EXEC sp_executesql @sql
END
Shiv
fuente
5

En SQL Server 2008 en adelante, puede usar " INFORMATION_SCHEMA.ROUTINES"

IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.ROUTINES 
  WHERE ROUTINE_NAME = 'MySP'
        AND ROUTINE_TYPE = 'PROCEDURE') 
Romil Kumar Jain
fuente
3

Aparentemente no tengo la reputación requerida para votar o comentar, pero solo quería decir que la respuesta de Geoff usando EXEC (sp_executesql podría ser mejor) es definitivamente el camino a seguir. Dejar y volver a crear el procedimiento almacenado hace el trabajo al final, pero hay un momento en el tiempo en que el procedimiento almacenado no existe en absoluto, y eso puede ser muy malo, especialmente si esto es algo que será correr repetidamente Estaba teniendo todo tipo de problemas con mi aplicación porque un subproceso en segundo plano estaba haciendo una GOTA SI EXISTE ... CREAR al mismo tiempo que otro subproceso estaba tratando de usar el procedimiento almacenado.

James
fuente
3

** La forma más sencilla de soltar y recrear un proceso almacenado en T-Sql es **

Use DatabaseName
go
If Object_Id('schema.storedprocname') is not null
begin
   drop procedure schema.storedprocname
end
go

create procedure schema.storedprocname
as

begin
end
Joseph Rennish
fuente
3

Aquí está el script que uso. Con él, evito dejar caer innecesariamente y recrear los procesos almacenados.

IF NOT EXISTS (
    SELECT *
    FROM sys.objects
    WHERE object_id = OBJECT_ID(N'[dbo].[uspMyProcedure]')
    )
BEGIN
  EXEC sp_executesql N'CREATE PROCEDURE [dbo].[uspMyProcedure] AS select 1'
END
GO

ALTER PROCEDURE [dbo].[uspMyProcedure] 
    @variable1 INTEGER  
AS
BEGIN
   -- Stored procedure logic
END
myroslav
fuente
2

Verifique si existe para el procedimiento almacenado

IF EXISTS (SELECT * FROM sys.objects 
            WHERE object_id = OBJECT_ID
             (N'[Schema].[Procedure_Name]') AND type IN (N'P', N'PC'))
BEGIN
       DROP PROCEDURE [Schema].[Procedure_Name]
       Print('Proceudre dropped => [Schema].[Procedure_Name]')
END

Marque IF Exist for Trigger, Function también haciendo clic en el siguiente enlace http://www.gurujipoint.com/2017/05/check-if-exist-for-trigger-function-and.html

Jatin Phulera
fuente
1

¿Por qué no vas de la manera simple como

    IF EXISTS(SELECT * FROM sys.procedures WHERE NAME LIKE 'uspBlackListGetAll')
    BEGIN
         DROP PROCEDURE uspBlackListGetAll
    END
    GO

    CREATE Procedure uspBlackListGetAll

..........

dnxit
fuente
Mala idea usar una declaración LIKE% aquí. ¿Qué pasa si el OP tenía otra sproc como uspBlackListGetAll_V2 que no querían dejar caer?
Dave Hogan
@DaveHogan, estoy de acuerdo. Sin embargo, no puso un %, así que se LIKEcomporta como un=
Diego Jancic
1
@DiegoJancic si miras el historial editado, verás que originalmente estaba con un '%'
Dave Hogan
0

Además de la respuesta de @Geoff , he creado una herramienta simple que genera un archivo SQL que declara procedimientos almacenados, vistas, funciones y disparadores.

Ver MyDbUtils @ CodePlex . ingrese la descripción de la imagen aquí

Stef Heyenrath
fuente
1
Creo que Management Studio ya ofrece esa herramienta. Se llama "Generar guiones"
Hybris95
0

¡Me pregunto! ¿Por qué no escribo toda la consulta como

GO
create procedure [dbo].[spAddNewClass] @ClassName varchar(20),@ClassFee int
as
begin
insert into tblClass values (@ClassName,@ClassFee)
end

GO
create procedure [dbo].[spAddNewSection] @SectionName varchar(20),@ClassID       int
as
begin
insert into tblSection values(@SectionName,@ClassID)
end

Go
create procedure test
as
begin 
select * from tblstudent
end

ya sé que ya existen los primeros dos procedimientos, sql ejecutará la consulta dará el error de los dos primeros procedimientos, pero aún así creará el último procedimiento SQl se está encargando de lo que ya existe, esto es lo que siempre hago a todos mis ¡clientela!

Shaikh Noman Nasir
fuente
-2

CREAR Procedimiento SI NO EXISTE 'Su nombre de proceso' () COMIENZA ... FIN

Wartari
fuente
esto no haría nada si el procedimiento existe. El solicitante desea alterar el procedimiento si existe, crearlo si no.
Randy Gamage