Come verificare se esiste una procedura memorizzata prima di crearla


284

Ho uno script SQL che deve essere eseguito ogni volta che un client esegue la funzionalità di "gestione del database". Lo script include la creazione di stored procedure sul database client. Alcuni di questi client potrebbero già avere la procedura memorizzata durante l'esecuzione dello script, altri no. Devo avere le procedure memorizzate mancanti aggiunte al database client, ma non importa quanto cerco di piegare la sintassi T-SQL, ottengo

CREATE / ALTER PROCEDURE 'deve essere la prima istruzione in un batch di query

Ho letto che abbandonare prima di creare opere, ma non mi piace farlo in quel modo.

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

CREATE PROCEDURE MyProc
...

Come posso aggiungere verificare l'esistenza di una procedura memorizzata e crearla se non esiste ma modificarla se esiste?


2
no non funziona, perché crea una stored procedure che presumibilmente non è quella che desideri. da quello che possiamo vedere, non lo lascia neanche dopo averlo fatto, quindi è sicuramente memorizzato in tutti gli aspetti del termine. è non è irrilevante perché avete bisogno di una procedura non memorizzati
David Hedlund

Cosa intendi con procedura "non memorizzata"? Tutto ciò che fa il tuo esempio è ricreare una procedura memorizzata; cosa c'entra questo con la tua domanda?
AakashM,

Ok, eccoci. Il fatto è che ho uno script SQL ENORME che molti client usano e deve essere eseguito accuratamente ogni volta che un client esegue la funzionalità di "gestione del database" fornita dal nostro software. Quindi alcuni di questi client potrebbero già avere la procedura memorizzata durante l'esecuzione dello script, e altri no. So che è stupido, in realtà non ho bisogno di questa procedura per rimanere non memorizzato, posso solo controllare se esiste e crearlo se non lo fa. Tuttavia, non importa quanto cerco di piegare la sintassi T-SQL, c'è sempre un errore.
The Shaper,

Ogni volta che eseguono lo script, proverà a creare nuovamente la procedura (sfortunatamente, tutto deve essere scritto nello stesso file .sql inclusa la chiamata alla procedura di creazione). SE NON ESISTE ALLORA CREATE non funziona a causa delle limitazioni della sintassi. Cosa posso fare?
The Shaper,

Risposte:


200

È possibile eseguire il codice procedurale ovunque sia possibile eseguire una query.

Copia tutto dopo AS:

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

Questo codice fa esattamente le stesse cose che farebbe un proc memorizzato, ma non è archiviato sul lato database.

È molto simile a ciò che viene chiamato procedura anonima in PL/SQL.

Aggiornare:

Il titolo della tua domanda è un po 'confuso.

Se hai solo bisogno di creare una procedura se non esiste, allora il tuo codice va bene.

Ecco quali sono gli SSMSoutput nello script di creazione:

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

Aggiornare:

Esempio di come farlo quando si include lo schema:

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

Nell'esempio sopra, dbo è lo schema.

Aggiornare:

In SQL Server 2016+, puoi semplicemente farlo

CREATE OR ALTER PROCEDURE dbo.MyProc


Sì, questo è vero, ma perderai tutte le funzionalità procedurali in quanto nessuna procedura, udfs, vista e simili verranno archiviati per essere richiamati dalle query. (Mi dispiace, l'ho modificato, aveva senso nella mia testa X-))
Adriaan Stander

1
Sì, ma è possibile chiamare le procedure all'interno di altre procedure o utilizzare il loro ritorno come input per una tabella.
Adriaan Stander,

@astander: puoi chiamare codice anonimo anche dalle procedure memorizzate. Per utilizzare il loro output in un INSERT, è necessario utilizzare OPENROWSETo OPENQUERYche funziona anche con il codice anonimo. Naturalmente ci sono degli svantaggi nel codice anonimo: ad esempio, funziona solo con i privilegi del chiamante. Il mio punto è che è possibile, non il modo preferito di fare le cose :)
Quassnoi,

"Se hai solo bisogno di creare una procedura se non esiste, allora il tuo codice va bene." Ed è esattamente quello che volevo sapere. Ho provato a usare SSMS Create su nello script reale ma non ha funzionato. Ma grazie Quassnoi, e mi dispiace per la domanda poco chiara.
The Shaper,

2
Un'istruzione CREATE PROC deve essere l'unica istruzione in un batch quando non si utilizza SQL dinamico, quindi non è possibile avvolgere una transazione attorno a DROP / CREATE se implementata in questo modo. Deve essere presente un GO (separatore batch) dopo la chiamata DROP PROC.
Shiv

450

Mi rendo conto che questo è già stato contrassegnato come risposta, ma lo facevamo in questo modo:

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 per evitare di abbandonare la procedura.


74
Solo per aggiungere alcune note sul perché questa è una buona idea: 1) una goccia cancellerà tutte le impostazioni di sicurezza, 2) facendolo in questo modo, se lo script di modifica fallisce per qualche motivo, lo sp non sarà stato eliminato.
Ryan Guill,

10
Questa è veramente la risposta corretta. Evita di perdere eventuali SOVVENZIONI sul proc memorizzato in questione.
Andy_Vulhop,

7
Questo approccio presenta un enorme vantaggio in quanto non esiste un momento nel quale la procedura memorizzata non esiste. Questo può essere cruciale se l'aggiornamento viene applicato a un sistema critico mentre è ancora in uso da altre persone, sistemi o thread. Rintracciare gli errori causati dall'abbandono momentaneo di una procedura memorizzata può essere abbastanza fastidioso perché sono molto difficili da riprodurre.
James,

3
Questa è un'ottima soluzione per molte ragioni già menzionate, e vorrei solo aggiungere che, nel caso in cui i DBA si basino su metadati proc (come data-creazione), ciò lascia intatta quella roba, invece di rendere il proc nuovo di zecca ogni volta. Sto cercando di trasformare questo in "best practice" del mio team per mantenere i nostri proc, che in genere devono essere copiati / propagati su più DB.
NateJ,

2
Considera anche che alcune persone voglionoGRANT esplicitamente le dichiarazioni nello script nel caso in cui cambino ; quindi c'è ancora giustificazione da usare al DROPposto di ALTER.
Cody Stott,

123

Se stai cercando il modo più semplice per verificare l'esistenza di un oggetto database prima di rimuoverlo, ecco un modo (l'esempio usa uno SPROC, proprio come il tuo esempio sopra ma potrebbe essere modificato per tabelle, indici, ecc ...):

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

Questo è veloce ed elegante, ma è necessario assicurarsi di avere nomi di oggetti univoci in tutti i tipi di oggetti poiché non ne tiene conto.

Spero che aiuti!


62
Meglio: IF (OBJECT_ID ('MyProcedure', 'P') NON È NULL) PROCEDURA DI GOCCIA MyProcedure GO
alerya

33

So che vuoi "modificare una procedura se esiste ed eliminarla solo se non esiste" ma credo che sia più semplice abbandonare sempre la procedura e quindi ricrearla. Ecco come eliminare la procedura solo se esiste già:

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

Il secondo parametro dice OBJECT_IDdi cercare solo oggetti con object_type = 'P', che sono stored procedure:

AF = Funzione aggregata (CLR)

C = vincolo CHECK

D = DEFAULT (vincolo o autonomo)

F = vincolo FOREIGN KEY

FN = funzione scalare SQL

FS = Funzione scalare Assembly (CLR)

FT = Funzione con valori di tabella Assembly (CLR)

IF = funzione inline SQL con valori di tabella

IT = tabella interna

P = SQL Stored Procedure

PC = procedura memorizzata Assembly (CLR)

PG = Guida al piano

PK = vincolo PRIMARY KEY

R = Regola (vecchio stile, autonomo)

RF = Procedura filtro-replica

S = tabella di base del sistema

SN = Sinonimo

SO = oggetto sequenza

TF = funzione di valori di tabella SQL

TR = Trigger

Puoi ottenere l'elenco completo delle opzioni tramite:

SELECT name 
FROM master..spt_values
WHERE type = 'O9T'

1
TF mancante. Tuttavia, +1 per aver fornito questo elenco
Crono

Anche TR per Trigger
CarlosOro,


23

So che è un post molto vecchio, ma poiché questo appare nei risultati di ricerca principali, quindi aggiungendo l'ultimo aggiornamento per coloro che utilizzano SQL Server 2016 SP1 -

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

Questo crea una Stored Procedure se non esiste già, ma la modifica se esiste.

Riferimento


1
Questo è così, così utile.
AgentFire

Voglio sottolineare che questo funziona solo in SQL Studio - in un file sql fallisce per me.
James L.,

10

DROP IF EXISTS è una nuova funzionalità di 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]

1
questa non è sintassi di SqlServer ..., consiglio di rimuovere la risposta prima che i ragazzi inizino il downvoting e di evitare confusione per i neofiti.
Pawel Czapski,

@PawelCz è valido per SQL Server 2016 e versioni successive, ho riformulato la risposta. Grazie per il feedback!
JayJay,

Questo non risponde al post originale. Esiste una sottile differenza tra la caduta automatica e la ricreazione e la creazione solo se non esiste. La caduta di un proc elimina la sicurezza ad esso associata, che potrebbe essere stata copiata da script.
Ron

7

Ho avuto lo stesso errore. So che questo thread è praticamente già morto, ma voglio impostare un'altra opzione oltre alla "procedura anonima".

L'ho risolto in questo modo:

  1. Controllare se esiste la procedura memorizzata:

    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. Tuttavia, "CREATE/ALTER PROCEDURE' must be the first statement in a query batch"è ancora lì. L'ho risolto in questo modo:

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE -- view procedure function or anything you want ...
  3. Finisco con questo codice:

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

Non è necessario l'inizio e la fine se è solo 1 riga di codice come DROP PROCEDURE ...
Phillip Senn

Avvertenza: la funzione 'controlla se esiste la procedura memorizzata' restituirà sempre 'esiste', indipendentemente dal nome della funzione che hai inserito (per T-SQL). È un controllo inaffidabile.
Ryan Battistone,

Un'alternativa migliore: SE ESISTE (SELEZIONA 1 DA sys.procedures WHERE name = 'name_of_table_as_seen_in_sysprocedures') INIZIA seleziona -1 come 'status' END
Ryan Battistone

5

Ecco un metodo e alcuni ragionamenti per usarlo in questo modo. Non è bello modificare il proc memorizzato ma ci sono pro e contro ...

AGGIORNAMENTO: puoi anche racchiudere l'intera chiamata in una TRANSAZIONE. Comprese molte procedure memorizzate in un'unica transazione che possono essere tutte impegnate o tutte rollback. Un altro vantaggio del wrapping in una transazione è che la procedura memorizzata esiste sempre per altre connessioni SQL purché non utilizzino il livello di isolamento della transazione READ UNCOMMITTED!

1) Per evitare alterazioni proprio come una decisione di processo. I nostri processi sono sempre SE GLI ESISTI CADONO E CREANO. Se si fa lo stesso modello nell'ipotizzare che il nuovo PROC sia il proc desiderato, il catering per gli alter è un po 'più difficile perché si avrebbe SE ESISTE ALTER ELSE CREATE.

2) Devi inserire CREATE / ALTER come prima chiamata in un batch in modo da non poter racchiudere una sequenza di aggiornamenti di procedura in una transazione esterna a SQL dinamico. Fondamentalmente se si desidera eseguire un intero stack di aggiornamenti delle procedure o ripristinarli tutti senza ripristinare un backup del DB, questo è un modo per fare tutto in un singolo batch.

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

5

In SQL Server dal 2008 in poi, è possibile utilizzare "INFORMATION_SCHEMA.ROUTINES "

IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.ROUTINES 
  WHERE ROUTINE_NAME = 'MySP'
        AND ROUTINE_TYPE = 'PROCEDURE') 

3

Apparentemente non ho la reputazione richiesta per votare o commentare, ma volevo solo dire che la risposta di Geoff usando EXEC (sp_executesql potrebbe essere migliore) è sicuramente la strada da percorrere. Eliminare e quindi ricreare la procedura memorizzata fa il lavoro alla fine, ma c'è un momento nel tempo in cui la procedura memorizzata non esiste affatto e ciò può essere molto negativo, specialmente se questo è qualcosa che sarà correre ripetutamente. Avevo molti problemi con la mia applicazione perché un thread in background stava eseguendo un DROP EXISTS ... CREATE mentre un altro thread stava tentando di utilizzare la procedura memorizzata.


3

** Il modo più semplice per eliminare e ricreare un proc memorizzato in 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

3

Ecco lo script che uso. Con esso, evito inutilmente di far cadere e ricreare i proc memorizzati.

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


1

perché non vai nel modo semplice

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

    CREATE Procedure uspBlackListGetAll

..........


Cattiva idea di utilizzare un'istruzione LIKE% qui. E se l'OP avesse un altro sproc come uspBlackListGetAll_V2 che non volevano far cadere?
Dave Hogan,

@DaveHogan Sono d'accordo. Tuttavia non ha messo un %, quindi LIKEsi comporta come un=
Diego Jancic

1
@DiegoJancic se guardi la cronologia modificata vedrai che era originariamente con un '%'
Dave Hogan,

0

Oltre alla risposta di @Geoff ho creato un semplice strumento che genera un file SQL con istruzioni per Stored procedure, viste, funzioni e trigger.

Vedi MyDbUtils @ CodePlex . inserisci qui la descrizione dell'immagine


1
Penso che Management Studio fornisca già tale strumento. Si chiama "Genera script"
Hybris95,

0

Mi chiedo! Perché non scrivo l'intera query come

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

so già che esistono già le prime due procedure sql eseguirà la query darà l'errore delle prime due procedure ma creerà comunque l'ultima procedura SQl si prende cura di ciò che già esiste questo è quello che faccio sempre a tutti i miei i clienti!


-2

CREATE Procedura SE NON ESISTE 'Your proc-name' () BEGIN ... END


questo non farebbe nulla se la procedura esiste. Il richiedente desidera modificare la procedura se esiste, crearla in caso contrario.
Randy Gamage,
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.