Como verificar se existe um procedimento armazenado antes de criá-lo

282

Eu tenho um script SQL que precisa ser executado sempre que um cliente executa a funcionalidade "gerenciamento de banco de dados". O script inclui a criação de procedimentos armazenados no banco de dados do cliente. Alguns desses clientes já podem ter o procedimento armazenado na execução do script, e outros podem não ter. Eu preciso que os procedimentos armazenados ausentes sejam adicionados ao banco de dados do cliente, mas não importa o quanto eu tente dobrar a sintaxe do T-SQL, recebo

CREATE / ALTER PROCEDURE 'deve ser a primeira instrução em um lote de consulta

Eu li isso antes de criar trabalhos, mas não gosto de fazer dessa maneira.

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

CREATE PROCEDURE MyProc
...

Como posso adicionar a verificação da existência de um procedimento armazenado e criá-lo se ele não existir, mas alterá-lo se ele existir?

The Shaper
fonte
2
não, não funciona, porque isso cria um procedimento armazenado que supostamente não é o que você deseja. pelo que podemos ver, ele também não é descartado após o término, portanto é definitivamente armazenado em todos os aspectos do termo. é não irrelevante porque você precisa de um procedimento não-armazenados
David Hedlund
O que você quer dizer com procedimento 'não armazenado'? Tudo o que sua amostra faz é recriar um procedimento armazenado; o que isso tem a ver com sua pergunta?
precisa saber é o seguinte
Ok, lá vamos nós. O problema é que eu tenho um script SQL ENORME que muitos clientes usam e precisa ser executado minuciosamente toda vez que um cliente executa a funcionalidade de "gerenciamento de banco de dados" que nosso software fornece. Portanto, alguns desses clientes podem já ter o procedimento armazenado durante a execução do script, e outros podem não. Eu sei que isso é estúpido, na verdade não preciso que esse procedimento permaneça sem armazenamento, basta verificar se ele existe e criá-lo, se não existir. No entanto, não importa o quanto eu tente dobrar a sintaxe do T-SQL, sempre haverá um erro.
The Shaper
Toda vez que eles executam o script, ele tenta criar o procedimento novamente (infelizmente, tudo precisa ser script no mesmo arquivo .sql, incluindo a chamada de procedimento de criação). SE NÃO EXISTE ENTÃO, CREATE não funciona devido a limitações de sintaxe. O que eu posso fazer?
The Shaper
3
Possível duplicado de stackoverflow.com/questions/937908/...
Michael Freidgeim

Respostas:

199

Você pode executar código de procedimento em qualquer lugar em que possa executar uma consulta.

Basta copiar tudo depois AS:

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

Esse código faz exatamente as mesmas coisas que um procedimento armazenado faria, mas não é armazenado no lado do banco de dados.

Isso é muito parecido com o que é chamado de procedimento anônimo PL/SQL.

Atualizar:

O título da sua pergunta é um pouco confuso.

Se você só precisar criar um procedimento, se ele não existir, seu código estará correto.

Aqui está o que SSMSsai no script de criação:

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

Atualizar:

Exemplo de como fazer isso ao incluir o 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

No exemplo acima, dbo é o esquema.

Atualizar:

No SQL Server 2016 ou superior, você pode fazer

CREATE OR ALTER PROCEDURE dbo.MyProc

Quassnoi
fonte
Sim, isso é verdade, mas você perderá toda a funcionalidade processual, pois nenhum procedimento, udfs, visualizações e outros itens serão armazenados para chamar de dentro de consultas. (Desculpe, editado-lo, fazia sentido na minha cabeça X-))
Adriaan Stander
1
Sim, mas você pode chamar procedimentos de outros procedimentos ou usar o retorno deles como entrada para uma tabela.
Adriaan Stander
@astander: você também pode chamar código anônimo a partir dos procedimentos armazenados. Para usar a saída deles em um arquivo INSERT, você precisará usar OPENROWSETou OPENQUERYque funcione com o código anônimo também. É claro que existem desvantagens no código anônimo: por exemplo, ele é executado apenas sob os privilégios do chamador. Meu ponto é que é possível caminho, não preferido de fazer as coisas :)
Quassnoi
"Se você só precisa criar um procedimento, se ele não existir, seu código está correto." E é exatamente isso que eu queria saber. Tentei usar o SSMS Create no script real, mas não adiantou nada. Mas obrigado Quassnoi, e desculpe-me pela pergunta incerta.
The Shaper
2
Uma instrução CREATE PROC deve ser a única instrução em um lote quando não estiver usando SQL dinâmico, para que você não possa quebrar uma transação em torno do DROP / CREATE quando implementada dessa maneira. Tem que haver um GO (separador de lote) após a chamada DROP PROC.
Shiv
449

Sei que isso já foi marcado como respondido, mas costumávamos fazer assim:

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
  ....

Apenas para evitar a interrupção do procedimento.

Geoff
fonte
74
Apenas para adicionar algumas notas sobre por que essa é uma boa idéia: 1) uma queda limpará todas as configurações de segurança, 2) fazendo assim, se o script alter falhar por algum motivo, o sp não será eliminado.
Ryan Guill
10
Esta é realmente a resposta correta. Evita a perda de qualquer SUBSÍDIO no processo armazenado em questão.
Andy_Vulhop
7
Há um grande benefício para essa abordagem, pois não há um momento em que o procedimento armazenado não exista. Isso pode ser crucial se a atualização estiver sendo aplicada a um sistema crítico enquanto ainda estiver em uso por outras pessoas, sistemas ou threads. O rastreamento dos erros causados ​​pela interrupção momentânea de um procedimento armazenado pode ser bastante irritante, porque eles são muito difíceis de reproduzir.
James
3
Essa é uma ótima solução por muitos motivos já mencionados, e eu gostaria de acrescentar que, caso os DBAs confiem em metadados de proc (como data de criação), isso deixa intacto o material, em vez de torná-lo proc. novinho em folha sempre. Estou tentando transformar isso nas "melhores práticas" da minha equipe para manter nossos próprios procs, que normalmente precisam ser copiados / propagados para vários bancos de dados.
NateJ
2
Considere também que algumas pessoas desejamGRANT explicitamente as instruções no script , caso elas mudem ; então ainda há justificativa para usar em DROPvez de ALTER.
Cody Stott
123

Se você está procurando a maneira mais simples de verificar a existência de um objeto de banco de dados antes de removê-lo, aqui está uma maneira (o exemplo usa um SPROC, exatamente como o exemplo acima, mas pode ser modificado para tabelas, índices, etc ...):

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

Isso é rápido e elegante, mas você precisa ter nomes de objetos exclusivos em todos os tipos de objetos, pois isso não leva isso em consideração.

Eu espero que isso ajude!

MrChips
fonte
62
Que melhor: SE (OBJECT_ID ('MyProcedure', 'P') NÃO É NULL) PROCEDIMENTO DE QUEDA MyProcedure GO
alerya
32

Sei que você deseja "alterar um procedimento, se ele existir, e excluí-lo somente se ele não existir", mas acredito que é mais simples sempre largar o procedimento e recriá-lo. Veja como descartar o procedimento apenas se ele já existir:

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

O segundo parâmetro diz OBJECT_IDque olhar somente para objetos com object_type = 'P', procedimentos que são armazenados:

AF = Função agregada (CLR)

C = restrição de CHECK

D = PADRÃO (restrição ou autônomo)

F = restrição de CHAVE ESTRANGEIRA

FN = função escalar SQL

FS = Função escalar Assembly (CLR)

Função com valor de tabela FT = Assembly (CLR)

IF = função com valor de tabela embutida SQL

IT = tabela interna

P = SQL Stored Procedure

Procedimento armazenado PC = Assembly (CLR)

PG = Guia do plano

Restrição PK = PRIMARY KEY

R = Regra (estilo antigo, autônomo)

RF = Procedimento de filtro de replicação

S = tabela base do sistema

SN = Sinônimo

SO = objeto de sequência

TF = função com valor de tabela SQL

TR = Disparador

Você pode obter a lista completa de opções em:

SELECT name 
FROM master..spt_values
WHERE type = 'O9T'
Michael Currie
fonte
1
Falta TF. Ainda assim, +1 por fornecer esta lista
Crono 7/17
Também TR para Trigger
CarlosOro
23

Sei que é uma postagem muito antiga, mas como ela aparece nos principais resultados da pesquisa, adicionamos a atualização mais recente para aqueles que usam o SQL Server 2016 SP1 -

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

Isso cria um Procedimento Armazenado, se ainda não existir, mas o altera, se existir.

Referência

gkb
fonte
1
Isto é tão, tão útil.
AgentFire 10/03/19
Quero enfatizar que isso só funciona no SQL Studio - em um arquivo sql, ele falha para mim.
James L.
10

DROP IF EXISTS é um novo recurso do 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
fonte
1
essa não é a sintaxe do SqlServer ..., conselhos para remover a resposta antes que os caras comecem a votar e evitar confusões para iniciantes.
Pawel Czapski
@PawelCz é válido para o SQL Server 2016 e acima, reformulei a resposta. Obrigado pelo feedback!
JayJay
Isso não responde à postagem original. Há uma diferença sutil entre soltar e recriar automaticamente e criar apenas se não existir. A eliminação de um processo eliminará a segurança associada a ele, que pode ter sido script.
Ron
7

Eu tive o mesmo erro. Eu sei que esse segmento já está praticamente morto, mas quero definir outra opção além de "procedimento anônimo".

Eu resolvi assim:

  1. Verifique se o procedimento armazenado existe:

    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. No entanto, o "CREATE/ALTER PROCEDURE' must be the first statement in a query batch"ainda está lá. Eu resolvi assim:

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE -- view procedure function or anything you want ...
  3. Termino com 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
fonte
Você não precisa do início e fim que seja apenas 1 linha de código como GOTA PROCEDIMENTO ...
Phillip Senn
Aviso: a função 'verificar se o procedimento armazenado existe' sempre retornará 'existe', independentemente do nome da função que você colocar (para T-SQL). É um cheque não confiável.
Ryan Battistone
Uma alternativa melhor: SE EXISTE (SELECIONE 1 DE sys.procedures WHERE name = 'name_of_table_as_seen_in_sysprocedures') COMEÇO selecione -1 como 'status' END
Ryan Battistone
5

Aqui está um método e algumas razões para usá-lo dessa maneira. Não é tão bonito editar o processo armazenado, mas existem prós e contras ...

ATUALIZAÇÃO: Você também pode agrupar toda a chamada em uma TRANSAÇÃO. Incluindo muitos procedimentos armazenados em uma única transação que podem confirmar ou reverter todos. Outra vantagem do agrupamento em uma transação é que o procedimento armazenado sempre existe para outras conexões SQL, desde que elas não usem o nível de isolamento de transação READ UNCOMMITTED!

1) Evitar alterações apenas como uma decisão de processo. Nossos processos devem sempre SE EXISTIR GOTAR ENTÃO CRIAR. Se você adotar o mesmo padrão de suposição de que o novo PROC é o proc desejado, a restauração de alterações é um pouco mais difícil, porque você teria uma opção SE EXISTS ALTER ELSE CREATE.

2) Você deve colocar CREATE / ALTER como a primeira chamada em um lote, para não poder agrupar uma sequência de atualizações de procedimentos em uma transação fora do SQL dinâmico. Basicamente, se você deseja executar uma pilha inteira de atualizações de procedimentos ou reverter todas elas sem restaurar um backup do banco de dados, essa é uma maneira de fazer tudo em um único 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
fonte
5

No servidor Sql 2008 em diante, você pode usar " INFORMATION_SCHEMA.ROUTINES"

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

Aparentemente, não tenho a reputação necessária para votar ou comentar, mas só queria dizer que a resposta de Geoff usando EXEC (sp_executesql pode ser melhor) é definitivamente o caminho a percorrer. Descartar e depois recriar o procedimento armazenado faz o trabalho no final, mas há um momento em que o procedimento armazenado não existe, e isso pode ser muito ruim, especialmente se isso for algo que será necessário. execute repetidamente. Eu estava tendo todos os tipos de problemas com meu aplicativo porque um thread em segundo plano estava executando um IF EXISTS DROP ... CREATE ao mesmo tempo em que outro thread tentava usar o procedimento armazenado.

James
fonte
3

** A maneira mais simples de descartar e recriar um processo armazenado no T-Sql é **

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
fonte
3

Aqui está o script que eu uso. Com isso, evito eliminar e recriar desnecessariamente os procs armazenados.

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
fonte
2

Verifique se existe algum procedimento armazenado

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

Verifique se existe para acionar, função também clicando no link abaixo http://www.gurujipoint.com/2017/05/check-if-exist-for-trigger-function-and.html

Jatin Phulera
fonte
1

por que você não segue o caminho mais simples como

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

    CREATE Procedure uspBlackListGetAll

..........

dnxit
fonte
Má idéia para usar uma instrução LIKE% aqui. E se o OP tivesse outro ramo, como uspBlackListGetAll_V2, que não quisesse soltar?
Dave Hogan
@DaveHogan Eu concordo. No entanto, ele não colocou um %, então o LIKEcomportamento se comporta como=
Diego Jancic 20/04
1
@DiegoJancic, se você olhar para o histórico editado, verá que ele era originalmente com um '%' #
Dave Hogan #
0

Além da resposta de @Geoff , criei uma ferramenta simples que gera um arquivo SQL com instruções para procedimentos armazenados, exibições, funções e acionadores.

Veja MyDbUtils @ CodePlex . insira a descrição da imagem aqui

Stef Heyenrath
fonte
1
Eu acho que o Management Studio já oferece essa ferramenta. É chamado de "gerar scripts"
Hybris95
0

Eu me pergunto! Por que não escrevo toda a 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

Eu já sei que os dois primeiros procedimentos já existem O sql executará a consulta dará o erro dos dois primeiros procedimentos, mas ainda assim criará o último procedimento O SQl está cuidando do que já existe, é isso que eu sempre faço para todos os meus clientes!

Shaikh Noman Nasir
fonte
-2

Procedimento CREATE SE NÃO EXISTE 'Your proc-name' () BEGIN ... END

Wartari
fonte
isso não faria nada se o procedimento existir. O solicitante deseja alterar o procedimento, se existir, e criar se não.
Randy Gamage