Comment vérifier si une procédure stockée existe avant de la créer

284

J'ai un script SQL qui doit être exécuté chaque fois qu'un client exécute la fonctionnalité de "gestion de base de données". Le script comprend la création de procédures stockées sur la base de données client. Certains de ces clients peuvent déjà avoir la procédure stockée lors de l'exécution du script, et d'autres pas. J'ai besoin que les procédures stockées manquantes soient ajoutées à la base de données client, mais peu importe combien j'essaie de plier la syntaxe T-SQL, j'obtiens

CREATE / ALTER PROCEDURE 'doit être la première instruction d'un lot de requêtes

J'ai lu ça avant de créer des œuvres, mais je n'aime pas le faire de cette façon.

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

CREATE PROCEDURE MyProc
...

Comment puis-je ajouter la vérification de l'existence d'une procédure stockée et la créer si elle n'existe pas mais la modifier si elle existe?

The Shaper
la source
2
non, cela ne fonctionne pas, car cela crée une procédure stockée qui n'est prétendument pas ce que vous voulez. d'après ce que nous pouvons voir, il ne le laisse pas tomber après sa fin, donc il est définitivement stocké dans tous les aspects du terme. la raison pour laquelle vous avez besoin d'une procédure non stockée n'est pas sans importance
David Hedlund
Qu'entendez-vous par procédure «non stockée»? Tout ce que fait votre exemple est de recréer une procédure stockée; qu'est-ce que cela a à voir avec votre question?
AakashM
Ok, c'est parti. Le fait est que j'ai un énorme script SQL que de nombreux clients utilisent et doit être exécuté à chaque fois qu'un client exécute la fonctionnalité de "gestion de base de données" fournie par notre logiciel. Ainsi, certains de ces clients peuvent déjà avoir la procédure stockée lors de l'exécution du script, et d'autres pas. Je sais que c'est stupide, je n'ai pas vraiment besoin que cette procédure reste non stockée, je peux simplement vérifier si elle existe et la créer si ce n'est pas le cas. Cependant, peu importe combien j'essaie de plier la syntaxe T-SQL, il y a toujours une erreur.
The Shaper
Chaque fois qu'ils exécutent le script, il essaiera de recréer la procédure (malheureusement, tout doit être scripté dans le même fichier .sql, y compris l'appel de procédure de création). SI IL N'EXISTE PAS, CRÉER ne fonctionne pas en raison de limitations de syntaxe. Que puis-je faire?
The Shaper
3
Copie

Réponses:

200

Vous pouvez exécuter du code procédural partout où vous pouvez exécuter une requête.

Il suffit de tout copier après AS:

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

Ce code fait exactement les mêmes choses qu'un proc stocké ferait, mais n'est pas stocké du côté base de données.

Cela ressemble beaucoup à ce qu'on appelle la procédure anonyme dans PL/SQL.

Mettre à jour:

Le titre de votre question est un peu déroutant.

Si vous avez seulement besoin de créer une procédure si elle n'existe pas, alors votre code est très bien.

Voici ce qui SSMSsort dans le script de création:

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

Mettre à jour:

Exemple de procédure à suivre lors de l'inclusion du schéma:

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

Dans l'exemple ci-dessus, dbo est le schéma.

Mettre à jour:

Dans SQL Server 2016+, vous pouvez simplement faire

CREATE OR ALTER PROCEDURE dbo.MyProc

Quassnoi
la source
Oui, cela est vrai, mais vous perdrez toutes les fonctionnalités procédurales car aucune procédure, udfs, vues et autres ne seront stockés pour appeler à partir de requêtes. (Désolé, édité, ça avait du sens dans ma tête X-))
Adriaan Stander
1
Oui, mais vous pouvez appeler des procédures à partir d'autres procédures ou utiliser leur retour comme entrée dans une table.
Adriaan Stander
@astander: vous pouvez également appeler du code anonyme à partir des procédures stockées. Pour utiliser leur sortie dans un INSERT, vous devrez également utiliser OPENROWSETou OPENQUERYqui fonctionne avec le code anonyme. Bien sûr, le code anonyme présente des inconvénients: par exemple, il ne s'exécute que sous les privilèges de l'appelant. Mon point est qu'il est possible, pas une façon préférée de faire les choses :)
Quassnoi
"Si vous avez seulement besoin de créer une procédure si elle n'existe pas, alors votre code est très bien." Et c'est exactement ce que je voulais savoir. J'ai essayé d'utiliser SSMS Create sur le script réel, mais cela n'a fait aucun bien. Mais merci Quassnoi, et je suis désolé pour la question peu claire.
The Shaper du
2
Une instruction CREATE PROC doit être la seule instruction d'un lot lorsque vous n'utilisez pas SQL dynamique, vous ne pouvez donc pas encapsuler une transaction autour de DROP / CREATE lorsqu'elle est implémentée de cette manière. Il doit y avoir un GO (séparateur de lots) après l'appel DROP PROC.
Shiv
450

Je me rends compte que cela a déjà été marqué comme répondu, mais nous le faisions comme ceci:

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

Juste pour éviter de laisser tomber la procédure.

Geoff
la source
74
Juste pour ajouter quelques notes sur la raison pour laquelle c'est une bonne idée: 1) une suppression effacera tous les paramètres de sécurité, 2) en procédant de cette façon, si le script alter échoue pour une raison quelconque, le sp n'aura pas été supprimé.
Ryan Guill
10
C'est vraiment la bonne réponse. Cela évite de perdre des subventions sur le processus stocké en question.
Andy_Vulhop
7
Il y a un énorme avantage à cette approche en ce qu'il n'y a aucun moment où la procédure stockée n'existe pas. Cela peut être crucial si la mise à jour est appliquée à un système critique alors qu'elle est encore utilisée par d'autres personnes, systèmes ou threads. Retrouver les erreurs provoquées par la suppression momentanée d'une procédure stockée peut être assez dérangeant car très difficile à reproduire.
James
3
C'est une excellente solution pour de nombreuses raisons déjà mentionnées, et je voudrais juste ajouter que, dans le cas où les DBA s'appuient sur des métadonnées proc (telles que create-date), cela laisse ces choses intactes, au lieu de faire le proc flambant neuf à chaque fois. J'essaie de transformer cela en "meilleure pratique" de mon équipe pour maintenir nos propres procs, qui doivent généralement être copiés / propagés sur plusieurs bases de données.
NateJ
2
Considérez également que certaines personnes veulent que les GRANTdéclarations soient explicites dans le script au cas où elles changeraient ; il est donc toujours justifié d'utiliser à la DROPplace de ALTER.
Cody Stott
123

Si vous cherchez le moyen le plus simple de vérifier l'existence d'un objet de base de données avant de le supprimer, voici un moyen (l'exemple utilise un SPROC, tout comme votre exemple ci-dessus mais pourrait être modifié pour les tables, les index, etc ...):

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

C'est rapide et élégant, mais vous devez vous assurer que vous avez des noms d'objet uniques sur tous les types d'objet car il n'en tient pas compte.

J'espère que ça aide!

MrChips
la source
62
C'est mieux: SI (OBJECT_ID ('MyProcedure', 'P') N'EST PAS NUL) PROCEDURE DE DÉPART MyProcedure GO
alerya
33

Je sais que vous voulez "modifier une procédure si elle existe et la supprimer uniquement si elle n'existe pas", mais je pense qu'il est plus simple de simplement toujours supprimer la procédure, puis de la recréer. Voici comment supprimer la procédure uniquement si elle existe déjà:

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

Le deuxième paramètre indique OBJECT_IDde ne rechercher que les objets avec object_type = 'P', qui sont des procédures stockées:

AF = fonction d'agrégation (CLR)

C = contrainte CHECK

D = DEFAULT (contrainte ou autonome)

F = contrainte FOREIGN KEY

FN = fonction scalaire SQL

FS = fonction scalaire d'assemblage (CLR)

FT = fonction table (CLR) assemblée

IF = fonction table SQL en ligne

IT = Table interne

P = Procédure stockée SQL

PC = procédure stockée d'assemblage (CLR)

PG = Guide de plan

PK = contrainte PRIMARY KEY

R = règle (à l'ancienne, autonome)

RF = procédure de filtre de réplication

S = Table de base du système

SN = Synonyme

SO = objet séquence

TF = fonction table valeur SQL

TR = déclencheur

Vous pouvez obtenir la liste complète des options via:

SELECT name 
FROM master..spt_values
WHERE type = 'O9T'
Michael Currie
la source
1
TF est manquant. Encore, +1 pour avoir fourni cette liste
Crono
Aussi TR pour Trigger
CarlosOro
23

Je sais que c'est un article très ancien, mais puisque cela apparaît dans les premiers résultats de recherche, ce qui ajoute la dernière mise à jour pour ceux qui utilisent SQL Server 2016 SP1 -

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

Cela crée une procédure stockée si elle n'existe pas déjà, mais la modifie si elle existe.

Référence

gkb
la source
1
C'est tellement, tellement utile.
AgentFire
Je tiens à souligner que cela ne fonctionne que dans SQL Studio - dans un fichier sql, cela échoue pour moi.
James L.
10

DROP IF EXISTS est une nouvelle fonctionnalité 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
la source
1
ce n'est pas la syntaxe SqlServer ..., des conseils pour supprimer la réponse avant que les gars ne commencent à voter et pour éviter toute confusion pour les débutants.
Pawel Czapski
@PawelCz c'est valable pour SQL Server 2016 et plus, j'ai reformulé la réponse. Merci pour les commentaires!
JayJay
Cela ne répond pas au message d'origine. Il existe une différence subtile entre la suppression et la recréation automatiques et la création uniquement si elle n'existe pas. Supprimer un proc supprimera la sécurité qui lui est associée, qui peut avoir été scriptée.
Ron
7

J'ai eu la même erreur. Je sais que ce fil est déjà pratiquement mort, mais je veux définir une autre option en plus de la "procédure anonyme".

Je l'ai résolu comme ceci:

  1. Vérifiez si la procédure stockée 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. Cependant, le "CREATE/ALTER PROCEDURE' must be the first statement in a query batch"est toujours là. Je l'ai résolu comme ceci:

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE -- view procedure function or anything you want ...
  3. Je me retrouve avec ce 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 ...
Oaxas
la source
Vous n'avez pas besoin du début et de la fin si c'est seulement 1 ligne de code comme DROP PROCEDURE ...
Phillip Senn
Attention: la fonction 'vérifier si la procédure stockée existe' retournera toujours 'existe', quel que soit le nom de la fonction que vous insérez (pour T-SQL). Il s'agit d'un chèque peu fiable.
Ryan Battistone
Une meilleure alternative: SI EXISTE (SELECT 1 FROM sys.procedures WHERE name = 'name_of_table_as_seen_in_sysprocedures') BEGIN select -1 as 'status' END
Ryan Battistone
5

Voici une méthode et un raisonnement derrière son utilisation de cette façon. Ce n'est pas aussi joli d'éditer le proc stocké mais il y a des avantages et des inconvénients ...

MISE À JOUR: Vous pouvez également envelopper tout cet appel dans une TRANSACTION. Inclusion de nombreuses procédures stockées dans une seule transaction pouvant toutes être validées ou annulées. Un autre avantage de l'encapsulation dans une transaction est que la procédure stockée existe toujours pour les autres connexions SQL tant qu'elles n'utilisent pas le niveau d'isolement de transaction READ UNCOMMITTED!

1) Pour éviter les modifications tout comme une décision de processus. Nos processus sont à toujours SI EXISTE BAISSE PUIS CRÉE. Si vous faites le même modèle en supposant que le nouveau PROC est le proc souhaité, la restauration des modifications est un peu plus difficile car vous auriez un IF EXISTS ALTER ELSE CREATE.

2) Vous devez mettre CREATE / ALTER comme premier appel d'un lot afin de ne pas pouvoir encapsuler une séquence de mises à jour de procédure dans une transaction en dehors de SQL dynamique. Fondamentalement, si vous souhaitez exécuter toute une pile de mises à jour de procédures ou les restaurer toutes sans restaurer une sauvegarde de base de données, c'est un moyen de tout faire en un seul lot.

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
la source
5

Dans Sql server 2008 onwards, vous pouvez utiliser " INFORMATION_SCHEMA.ROUTINES"

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

Apparemment, je n'ai pas la réputation requise pour voter ou commenter, mais je voulais juste dire que la réponse de Geoff en utilisant EXEC (sp_executesql pourrait être mieux) est certainement la voie à suivre. La suppression puis la recréation de la procédure stockée finissent par faire le travail, mais il y a un moment où la procédure stockée n'existe pas du tout, et cela peut être très mauvais, surtout si c'est quelque chose qui sera exécuter à plusieurs reprises. J'avais toutes sortes de problèmes avec mon application car un thread d'arrière-plan effectuait un IF EXISTS DROP ... CREATE en même temps qu'un autre thread essayait d'utiliser la procédure stockée.

James
la source
3

** Le moyen le plus simple de supprimer et de recréer un proc stocké dans T-Sql est **

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
Rennish Joseph
la source
3

Voici le script que j'utilise. Avec cela, j'évite de supprimer et de recréer inutilement les proc stockés.

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
la source
2

Vérifiez s'il existe une procédure stockée

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

Vérifiez IF Exist for Trigger, Function également en cliquant sur le lien ci-dessous http://www.gurujipoint.com/2017/05/check-if-exist-for-trigger-function-and.html

Jatin Phulera
la source
1

pourquoi tu ne vas pas de la manière simple comme

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

    CREATE Procedure uspBlackListGetAll

..........

dnxit
la source
Mauvaise idée d'utiliser une instruction LIKE% ici. Et si l'OP avait un autre sproc comme uspBlackListGetAll_V2 qu'il ne voulait pas abandonner?
Dave Hogan
@DaveHogan Je suis d'accord. Cependant, il n'a pas mis un %, donc il LIKEse comporte comme=
Diego Jancic
1
@DiegoJancic si vous regardez l'historique édité, vous verrez qu'il était à l'origine avec un '%'
Dave Hogan
0

En plus de la réponse de @Geoff, j'ai créé un outil simple qui génère un fichier SQL contenant des instructions pour les procédures stockées, les vues, les fonctions et les déclencheurs.

Voir MyDbUtils @ CodePlex . entrez la description de l'image ici

Stef Heyenrath
la source
1
Je pense que Management Studio fournit déjà un tel outil. Ça s'appelle "Générer des scripts"
Hybris95
0

Je me demande! Pourquoi je n'écris pas la requête entière comme

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

je sais déjà que les deux premières procédures existent déjà sql exécutera la requête donnera l'erreur des deux premières procédures mais toujours il créera la dernière procédure SQl s'occupe de ce qui existe déjà c'est ce que je fais toujours pour tous mes clients!

Shaikh Noman Nasir
la source
-2

CRÉER Procédure SI IL N'EXISTE PAS 'Votre nom de proc' () COMMENCER ... FIN

Wartari
la source
cela ne ferait rien si la procédure existe. Le demandeur souhaite modifier la procédure si elle existe, la créer sinon.
Randy Gamage