So überprüfen Sie, ob eine gespeicherte Prozedur vorhanden ist, bevor Sie sie erstellen


282

Ich habe ein SQL-Skript, das jedes Mal ausgeführt werden muss, wenn ein Client die Funktionalität "Datenbankverwaltung" ausführt. Das Skript umfasst das Erstellen gespeicherter Prozeduren in der Client-Datenbank. Einige dieser Clients verfügen möglicherweise bereits beim Ausführen des Skripts über die gespeicherte Prozedur, andere möglicherweise nicht. Ich muss die fehlenden gespeicherten Prozeduren zur Client-Datenbank hinzufügen, aber es spielt keine Rolle, wie sehr ich versuche, die T-SQL-Syntax zu biegen

CREATE / ALTER PROCEDURE 'muss die erste Anweisung in einem Abfragebatch sein

Ich habe das Löschen vor dem Erstellen von Werken gelesen, aber ich mag es nicht so.

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

CREATE PROCEDURE MyProc
...

Wie kann ich die Existenz einer gespeicherten Prozedur überprüfen und erstellen, wenn sie nicht vorhanden ist, aber ändern, wenn sie vorhanden ist?


2
Nein, es funktioniert nicht, da dadurch eine gespeicherte Prozedur erstellt wird, die angeblich nicht Ihren Wünschen entspricht. Soweit wir sehen können, lässt es es auch nicht fallen, nachdem es fertig ist, so dass es definitiv in allen Aspekten des Begriffs gespeichert ist. Es ist nicht irrelevant, warum Sie eine nicht gespeicherte Prozedur benötigen
David Hedlund

Was meinst du mit "nicht gespeicherter" Prozedur? In Ihrem Beispiel wird lediglich eine gespeicherte Prozedur neu erstellt. Was hat das mit deiner Frage zu tun?
AakashM

Ok, los geht's. Die Sache ist, ich habe ein RIESIGES SQL-Skript, das viele Clients verwenden und das jedes Mal gründlich ausgeführt werden muss, wenn ein Client die von unserer Software bereitgestellte "Datenbankverwaltungs" -Funktionalität ausführt. Einige dieser Clients haben die Prozedur möglicherweise bereits beim Ausführen des Skripts gespeichert, andere möglicherweise nicht. Ich weiß, dass dies dumm ist. Ich brauche dieses Verfahren nicht, um nicht gespeichert zu werden. Ich kann einfach überprüfen, ob es vorhanden ist, und es erstellen, wenn es nicht vorhanden ist. Es spielt jedoch keine Rolle, wie sehr ich versuche, die T-SQL-Syntax zu biegen, es gibt immer einen Fehler.
The Shaper

Jedes Mal, wenn sie das Skript ausführen, wird versucht, die Prozedur erneut zu erstellen (leider muss alles in derselben SQL-Datei einschließlich des Aufrufs der Prozedur create erstellt werden). WENN NICHT EXISTIERT, funktioniert CREATE aufgrund von Syntaxbeschränkungen nicht. Was kann ich tun?
The Shaper

Antworten:


199

Sie können Prozedurcode überall dort ausführen, wo Sie eine Abfrage ausführen können.

Kopieren Sie einfach alles nach AS:

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

Dieser Code macht genau die gleichen Dinge, die ein gespeicherter Prozess tun würde, wird jedoch nicht auf der Datenbankseite gespeichert.

Das ist ähnlich wie bei der anonymen Prozedur PL/SQL.

Aktualisieren:

Ihr Fragentitel ist etwas verwirrend.

Wenn Sie eine Prozedur nur erstellen müssen, wenn sie nicht vorhanden ist, ist Ihr Code in Ordnung.

Hier ist , was SSMSim Skript erstellen ausgibt:

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

Aktualisieren:

Beispiel für die Vorgehensweise beim Einfügen des Schemas:

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

Im obigen Beispiel ist dbo das Schema.

Aktualisieren:

In SQL Server 2016+ können Sie dies einfach tun

CREATE OR ALTER PROCEDURE dbo.MyProc


Ja, das stimmt, aber Sie verlieren alle prozeduralen Funktionen, da keine Prozeduren, udfs, Ansichten und dergleichen gespeichert werden, um innerhalb von Abfragen aufzurufen. (Sorry, bearbeitet, es machte Sinn in meinem Kopf X-))
Adriaan Stander

1
Ja, aber Sie können Prozeduren aus anderen Prozeduren heraus aufrufen oder deren Rückgabe als Eingabe für eine Tabelle verwenden.
Adriaan Stander

@astander: Sie können auch anonymen Code aus den gespeicherten Prozeduren aufrufen. Um ihre Ausgabe in einem zu verwenden INSERT, müssen Sie OPENROWSEToder verwendenOPENQUERY was auch mit dem anonymen Code funktioniert. Natürlich hat der anonyme Code Nachteile: Er wird beispielsweise nur unter den Berechtigungen des Anrufers ausgeführt. Mein Punkt ist, dass es möglich ist, nicht bevorzugte Art, Dinge zu tun :)
Quassnoi

"Wenn Sie eine Prozedur nur erstellen müssen, wenn sie nicht vorhanden ist, ist Ihr Code in Ordnung." Und genau das wollte ich wissen. Ich habe versucht, SSMS Create für das eigentliche Skript zu verwenden, aber es hat nichts gebracht. Aber danke Quassnoi, und die unklare Frage tut mir leid.
The Shaper

2
Eine CREATE PROC-Anweisung muss die einzige Anweisung in einem Stapel sein, wenn kein dynamisches SQL verwendet wird, damit Sie bei dieser Implementierung keine Transaktion um DROP / CREATE wickeln können. Nach dem DROP PROC-Aufruf muss ein GO (Batch Separator) vorhanden sein.
Shiv

449

Mir ist klar, dass dies bereits als beantwortet markiert wurde, aber wir haben es früher so gemacht:

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

Nur um zu vermeiden, dass der Vorgang abgebrochen wird.


74
Nur um einige Hinweise hinzuzufügen, warum dies eine gute Idee ist: 1) Durch ein Löschen werden alle Sicherheitseinstellungen gelöscht. 2) Wenn das Änderungsskript aus irgendeinem Grund fehlschlägt, wird das SP nicht gelöscht.
Ryan Guill

10
Dies ist wirklich die richtige Antwort. Es wird vermieden, dass GRANTS für den betreffenden gespeicherten Prozess verloren gehen.
Andy_Vulhop

7
Dieser Ansatz hat den großen Vorteil, dass es keinen Zeitpunkt gibt, an dem die gespeicherte Prozedur nicht vorhanden ist. Dies kann von entscheidender Bedeutung sein, wenn das Update auf ein kritisches System angewendet wird, während es noch von anderen Personen, Systemen oder Threads verwendet wird. Das Aufspüren von Fehlern, die durch das kurzzeitige Löschen einer gespeicherten Prozedur verursacht werden, kann sehr ärgerlich sein, da sie sehr schwer zu reproduzieren sind.
James

3
Dies ist aus vielen bereits erwähnten Gründen eine großartige Lösung, und ich möchte nur hinzufügen, dass, falls die Datenbankadministratoren auf Proc-Metadaten (wie dem Erstellungsdatum) angewiesen sind, dieses Zeug intakt bleibt, anstatt den Proc zu erstellen jedes Mal brandneu. Ich versuche, dies in die "Best Practice" meines Teams für die Pflege unserer eigenen Prozesse zu verwandeln, die normalerweise in mehrere DBs kopiert / weitergegeben werden müssen.
NateJ

2
Bedenken Sie auch, dass einige Benutzer die GRANTAussagen explizit im Skript haben möchten, falls sie sich ändern . es gibt also immer noch eine Rechtfertigung, DROPanstatt zu verwenden ALTER.
Cody Stott

123

Wenn Sie nach der einfachsten Möglichkeit suchen, die Existenz eines Datenbankobjekts vor dem Entfernen zu überprüfen, haben Sie folgende Möglichkeit (Beispiel verwendet ein SPROC, genau wie das obige Beispiel, kann jedoch für Tabellen, Indizes usw. geändert werden):

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

Dies ist schnell und elegant, aber Sie müssen sicherstellen, dass Sie über alle Objekttypen hinweg eindeutige Objektnamen haben, da dies nicht berücksichtigt wird.

Ich hoffe das hilft!


62
Das ist besser: WENN (OBJECT_ID ('MyProcedure', 'P') NICHT NULL ist) DROP PROCEDURE MyProcedure GO
alerya

32

Ich weiß, dass Sie "eine Prozedur ändern möchten, wenn sie existiert, und sie nur löschen möchten, wenn sie nicht existiert", aber ich glaube, es ist einfacher, die Prozedur einfach immer zu löschen und sie dann neu zu erstellen. So löschen Sie die Prozedur nur, wenn sie bereits vorhanden ist:

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

Der zweite Parameter weist OBJECT_IDan, nur nach Objekten zu suchen object_type = 'P', bei denen es sich um gespeicherte Prozeduren handelt:

AF = Aggregatfunktion (CLR)

C = CHECK-Einschränkung

D = STANDARD (Einschränkung oder eigenständig)

F = FOREIGN KEY-Einschränkung

FN = SQL-Skalarfunktion

FS = Assembly (CLR) -Skalarfunktion

FT = CLR-Tabellenfunktion (Assembly)

IF = SQL-Inline-Tabellenwertfunktion

IT = Interne Tabelle

P = Gespeicherte SQL-Prozedur

PC = Assembly (CLR) gespeicherte Prozedur

PG = Planführung

PK = PRIMARY KEY-Einschränkung

R = Regel (altmodisch, eigenständig)

RF = Replikationsfilter-Prozedur

S = Systembasistabelle

SN = Synonym

SO = Sequenzobjekt

TF = SQL-Tabellenwertfunktion

TR = Trigger

Die vollständige Liste der Optionen erhalten Sie über:

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

1
TF fehlt. Trotzdem +1 für die Bereitstellung dieser Liste
Crono

Auch TR für Trigger
CarlosOro


23

Ich weiß , es ist eine sehr alte Post, aber da dies scheint in den Top - Suchergebnissen somit das neueste Update für die Zugabe mit SQL Server 2016 SP1 -

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

Dadurch wird eine gespeicherte Prozedur erstellt, falls diese noch nicht vorhanden ist, sie wird jedoch geändert, wenn sie vorhanden ist.

Referenz


1
Das ist so, so nützlich.
AgentFire

Ich möchte betonen, dass dies nur in SQL Studio funktioniert - in einer SQL-Datei schlägt dies für mich fehl.
James L.

10

DROP IF EXISTS ist eine neue Funktion von 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
Dies ist keine SqlServer-Syntax ..., ein Ratschlag, die Antwort zu entfernen, bevor die Jungs mit dem Downvoting beginnen, und Verwirrung für Neulinge zu vermeiden.
Pawel Czapski

@PawelCz es ist gültig für SQL Server 2016 und höher, ich habe die Antwort umformuliert. Danke für die Rückmeldung!
JayJay

Dies beantwortet nicht den ursprünglichen Beitrag. Es gibt einen subtilen Unterschied zwischen dem automatischen Löschen und Neuerstellen und dem Erstellen nur, wenn es nicht vorhanden ist. Durch das Löschen eines Prozesses wird die damit verbundene Sicherheit gelöscht, die möglicherweise per Skript erstellt wurde.
Ron

7

Ich hatte den gleichen Fehler. Ich weiß, dass dieser Thread bereits ziemlich tot ist, aber ich möchte neben "anonymes Verfahren" eine weitere Option festlegen.

Ich habe es so gelöst:

  1. Überprüfen Sie, ob die gespeicherte Prozedur vorhanden ist:

    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. Das "CREATE/ALTER PROCEDURE' must be the first statement in a query batch"ist aber noch da. Ich habe es so gelöst:

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE -- view procedure function or anything you want ...
  3. Am Ende habe ich diesen Code:

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

Sie brauchen den Anfang und das Ende nicht, wenn es nur eine Codezeile wie DROP PROCEDURE ist ...
Phillip Senn

Warnung: Die Funktion "Überprüfen, ob die gespeicherte Prozedur vorhanden ist" gibt immer "vorhanden" zurück, unabhängig davon, welchen Funktionsnamen Sie eingegeben haben (für T-SQL). Es ist eine unzuverlässige Prüfung.
Ryan Battistone

Eine bessere Alternative: WENN EXISTIERT (SELECT 1 FROM sys.procedures WHERE name = 'name_of_table_as_seen_in_sysprocedures') BEGIN wähle -1 als 'status' END
Ryan Battistone

5

Hier ist eine Methode und einige Gründe dafür. Es ist nicht so schön, den gespeicherten Prozess zu bearbeiten, aber es gibt Vor- und Nachteile ...

UPDATE: Sie können diesen gesamten Anruf auch in eine TRANSAKTION einschließen. Einschließen vieler gespeicherter Prozeduren in eine einzelne Transaktion, die alle festschreiben oder alle zurücksetzen können. Ein weiterer Vorteil des Einschlusses in eine Transaktion besteht darin, dass die gespeicherte Prozedur für andere SQL-Verbindungen immer vorhanden ist, solange sie nicht die Transaktionsisolationsstufe READ UNCOMMITTED verwenden!

1) Um Änderungen nur als Prozessentscheidung zu vermeiden. Unsere Prozesse sind immer, wenn Existenzen fallen, dann schaffen. Wenn Sie das gleiche Muster anwenden und davon ausgehen, dass der neue PROC der gewünschte Prozess ist, ist das Catering für Änderungen etwas schwieriger, da Sie eine IF EXISTS ALTER ELSE CREATE haben würden.

2) Sie müssen CREATE / ALTER als ersten Aufruf in einen Stapel setzen, damit Sie eine Folge von Prozeduraktualisierungen nicht in eine Transaktion außerhalb von Dynamic SQL einschließen können. Wenn Sie einen ganzen Stapel von Prozeduraktualisierungen ausführen oder alle zurücksetzen möchten, ohne eine DB-Sicherung wiederherzustellen, ist dies im Grunde eine Möglichkeit, alles in einem einzigen Stapel zu erledigen.

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

Ab SQL Server 2008 können Sie " INFORMATION_SCHEMA.ROUTINES"

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

3

Ich habe anscheinend nicht den Ruf, um abzustimmen oder zu kommentieren, aber ich wollte nur sagen, dass Geoffs Antwort mit EXEC (sp_executesql könnte besser sein) definitiv der richtige Weg ist. Durch das Löschen und erneute Erstellen der gespeicherten Prozedur wird die Aufgabe am Ende erledigt, aber es gibt einen Moment, in dem die gespeicherte Prozedur überhaupt nicht vorhanden ist, und das kann sehr schlecht sein, insbesondere wenn dies der Fall ist wiederholt ausführen. Ich hatte alle möglichen Probleme mit meiner Anwendung, weil ein Hintergrund-Thread einen IF EXISTS DROP ... CREATE ausführte, während ein anderer Thread versuchte, die gespeicherte Prozedur zu verwenden.


3

** Der einfachste Weg, einen in T-Sql gespeicherten Prozess zu löschen und neu zu erstellen, ist **

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

Hier ist das Skript, das ich benutze. Damit vermeide ich, die gespeicherten Prozesse unnötig zu löschen und neu zu erstellen.

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

Warum gehst du nicht so einfach?

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

    CREATE Procedure uspBlackListGetAll

..........


Schlechte Idee, hier eine LIKE% -Anweisung zu verwenden. Was wäre, wenn das OP einen anderen Sproc wie uspBlackListGetAll_V2 hätte, den sie nicht löschen wollten?
Dave Hogan

@ DaveHogan Ich stimme zu. Allerdings hat er keine gesetzt %, so dass sich die LIKEals=
Diego Jancic

1
@DiegoJancic Wenn Sie sich den bearbeiteten Verlauf ansehen, werden Sie sehen, dass er ursprünglich mit einem '%' versehen war
Dave Hogan

0

Neben der Antwort von @Geoff habe ich ein einfaches Tool erstellt, das eine SQL-Datei generiert, die Anweisungen für gespeicherte Prozeduren, Ansichten, Funktionen und Trigger enthält.

Siehe MyDbUtils @ CodePlex . Geben Sie hier die Bildbeschreibung ein


1
Ich denke, Management Studio bietet bereits ein solches Tool. Es heißt "Generate scripts"
Hybris95

0

Ich wundere mich! Warum schreibe ich nicht die ganze Abfrage wie

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

Ich weiß bereits, dass die ersten beiden Prozeduren bereits vorhanden sind. SQL wird ausgeführt. Die Abfrage gibt den Fehler der ersten beiden Prozeduren aus, aber es wird immer noch die letzte Prozedur erstellt. SQl kümmert sich selbst um das, was bereits vorhanden ist. Dies ist, was ich immer mit all meinen mache Kunden!


-2

CREATE-Prozedur, WENN NICHT 'Ihr Prozessname' existiert () BEGIN ... END


Dies würde nichts bewirken, wenn das Verfahren vorhanden ist. Der Anforderer möchte die Prozedur ändern, falls vorhanden, und sie erstellen, wenn nicht.
Randy Gamage
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.