53

I want to check if a list of stored procedures exist. I want this all to be done in 1 script, one by one. So far I have this format:

USE [myDatabase]
GO

IF NOT EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'sp_1')
BEGIN
CREATE PROCEDURE sp_1
AS
.................
END
GO

IF NOT EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'sp_2')
BEGIN
CREATE PROCEDURE sp_2
AS
.................
END
GO

and so on. However, I'm getting the following error:

Incorrect syntax near the keyword 'Procedure'.

Why isn't what I'm doing working correctly?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Sean Smyth
  • 1,509
  • 3
  • 25
  • 43
  • Possible duplicate of [How to check if a stored procedure exists before creating it](http://stackoverflow.com/questions/2072086/how-to-check-if-a-stored-procedure-exists-before-creating-it) – Michael Freidgeim Jun 09 '16 at 01:11

9 Answers9

54

CREATE PROCEDURE must be the first statement in the batch. I usually do something like this:

IF EXISTS (
        SELECT type_desc, type
        FROM sys.procedures WITH(NOLOCK)
        WHERE NAME = 'myProc'
            AND type = 'P'
      )
     DROP PROCEDURE dbo.myProc
GO

CREATE PROC dbo.myProc

AS
....

    GO
    GRANT EXECUTE ON dbo.myProc TO MyUser 

(don't forget grant statements since they'll be lost if you recreate your proc)

One other thing to consider when you are deploying stored procedures is that a drop can succeed and a create fail. I always write my SQL scripts with a rollback in the event of a problem. Just make sure you don't accidentally delete the commit/rollback code at the end, otherwise your DBA might crane-kick you in the trachea :)

BEGIN TRAN 
IF EXISTS (
       SELECT type_desc, type
       FROM sys.procedures WITH(NOLOCK)
       WHERE NAME = 'myProc'
           AND type = 'P'
     )
DROP PROCEDURE myProc GO
CREATE PROCEDURE myProc
   
AS
   --proc logic here

GO
-- BEGIN DO NOT REMOVE THIS CODE (it commits or rolls back the stored procedure drop) 
    IF EXISTS(
               SELECT 1
               FROM sys.procedures WITH(NOLOCK)
               WHERE NAME = 'myProc'
                   AND type = 'P'
             )
        COMMIT TRAN
        ELSE
        ROLLBACK TRAN
-- END DO NOT REMOVE THIS CODE
Ray's Web Presence
  • 464
  • 1
  • 5
  • 15
Code Magician
  • 23,217
  • 7
  • 60
  • 77
  • That ended up being the issue. Originally, I was told not to drop the tables, but I was unable to have Create Procedure after the if statement because it wasn't the first command in the batch. Just one of Microsoft's weird rules I was not aware of. Thank you! – Sean Smyth Apr 08 '14 at 23:52
  • so much work to rollback, etc. why not use `ALTER`, like how I show in my answer. When using an `'ALTER`, if the new code fails you still have the old version and you never lose permissions like you do when you `DROP`. Too bad SQL Server doesn't have Oracle's `create or replace procedure`. – KM. Apr 10 '14 at 15:54
  • @KM. One could say the same thing "...so much work..." about dynamic sql stub creation :). It all comes down to personal preference, they are both perfectly valid approaches just different strokes/different folks. Regardless, I still prefer/recommend explicit existence checks over the try/catch approach. – Code Magician Apr 10 '14 at 16:08
  • I do wish MS would implement a CREATE OR ALTER... I have no idea why we're all still doing workaround like this when other DBMSs have solved this years ago. – Code Magician Apr 10 '14 at 16:10
  • your rollback seems much more fragile, especially at the end of the procedure where a simple cut/paste accident could easily loose them. – KM. Apr 10 '14 at 16:20
  • @KM. Good point, and worth mentioning. I've always had a single script for every DB object in source control, and a template for new ones so it's never happened to me, but if someone were using this approach more haphazardly, they could make a mistake. I debated whether to mention that when I wrote the answer yesterday. I decided to include it (as a extended answer with a warning) both for completeness and to demonstrate transactional DDL which is worth learning about anyway. – Code Magician Apr 10 '14 at 16:32
  • @CodeMagician I think there's a typo in the 2nd code where the 2nd `IF EXIST` should be on it's own line. I've tried this and the code end up rolling back everytime, `DatasetDeleteCleanup` never appears from `sys.procedures` I can't find any mention of that in MS documentation. Could you share the source? I'm keen to read up on it. Thanks – faulty Mar 06 '18 at 08:19
  • I would personally never write code that involves dropping. Should not be an acceptable way to do it. – Anders Lindén Feb 23 '21 at 15:37
49

One idiom that I've been using lately that I like quite a lot is:

if exists (select 1 from sys.objects where object_id = object_id('dbo.yourProc'))
   set noexec on
go
create procedure dbo.yourProc as
begin
   select 1 as [not yet implemented]
end
go
set noexec off
alter procedure dbo.yourProc as
begin
   /*body of procedure here*/
end

Essentially, you're creating a stub if the procedure doesn't exist and then altering either the stub (if it was just created) or the pre-existing procedure. The nice thing about this is that you don't drop a pre-existing procedure which drops all the permissions as well. You can also cause issues with any application that happens to want it in that brief instant where it doesn't exist.

[Edit 2018-02-09] - In SQL 2016 SP1, create procedure and drop procedure got some syntactic sugar that helps with this kind of thing. Specifically, you can now do this:

create or alter dbo.yourProc as
go

drop procedure if exists dbo.yourProc;

Both provide idempotency in the intended statement (i.e. you can run it multiple times and the desired state is achieved). This is how I'd do it now (assuming you're on a version of SQL Server that supports it).

Ben Thul
  • 31,080
  • 4
  • 45
  • 68
  • 4
    +1 I've never seen this approach before. Clever use of NOEXEC! – Code Magician Apr 10 '14 at 16:12
  • 2
    I didn't invent it, but I got it from here: https://www.simple-talk.com/sql/database-administration/automating-sql-server-database-deployments-scripting-details/ – Ben Thul Apr 10 '14 at 17:24
  • 3
    you don't even need the dummy statement (select 1 as [not yet implemented]), just "create procedure dbo.yourProc as" works fine. – slashCoder Dec 30 '16 at 19:17
  • Good to know, @slashCoder. I know SQL doesn't like empty/naked begin/end blocks. It never occurred to me that it'd be okay with an empty/naked `create procedure` statement! – Ben Thul Jan 04 '17 at 03:07
  • 2
    This should be the Accepted Answer. – Kalyan Feb 09 '18 at 00:56
  • Why not just `IF OBJECT_ID('SP_Name', N'P') IS NOT NULL SET NoExec ON` ? – AjV Jsy Aug 06 '18 at 14:24
  • 2
    @AjVJsy - If I had to do that today, I'd probably do that. You learn things in four years. :) But! If I had to do this today and I were on a recent version of SQL Server, I'd issue a `create or alter procedure`; all of the above is a workaround for that not existing. – Ben Thul Aug 06 '18 at 16:04
  • This just put a big ol' smile on my face! – pim Jan 31 '19 at 16:51
  • This is perfect. In my case, I have two independent (but linked) packages of applications and SQL procedures. One needs to have a callable stub, just so the compile and execute won't fail for lack of the procedure. The other contains the real code, and updates it as necessary. "Noexec" lets me create the stub if it doesn't exist, without replacing the real code if it already exists. – Kim Crosser Apr 04 '23 at 18:15
25

I know that there's an accepted answer, but the answer does not address exactly what the original question asks, which is to CREATE the procedure if it does not exist. The following always works and has the benefit of not requiring dropping procedures which can be problematic if one is using sql authentication.

USE [MyDataBase]
GO

IF OBJECT_ID('mySchema.myProc') IS NULL
EXEC('CREATE PROCEDURE mySchema.myProc AS SET NOCOUNT ON;')
GO

ALTER PROCEDURE mySchema.myProc
    @DeclaredParmsGoHere    DataType

AS 
   BEGIN
       DECLARE @AnyVariablesINeed    Their DataType
   SELECT myColumn FROM myTable WHERE myIndex = @IndexParm
Ron
  • 867
  • 11
  • 26
  • Hi, your answer is pretty close to my requirment. It owrks for me fine. Can you please tell how can i do this without the GO... Like below ::: IF OBJECT_ID('mySchema.myProc') IS NULL EXEC('CREATE PROCEDURE mySchema.myProc AS SET NOCOUNT ON;') ALTER PROCEDURE mySchema.myProc @DeclaredParmsGoHere DataType AS BEGIN – rasoo Feb 21 '18 at 16:52
  • @rasoo I do not believe you can do that in Sql Server, and I cannot think of a reason why you would want to. The go command separates the individual batch commands. As soon as you remove one, you'll get an error that the 'alter' statement must be the first statement in a batch. Plus, Go makes the code easier to read because it correctly suggests that the step to create the procedure if it doesn't exist is its own batch, as is the step to alter the procedure – Ron Feb 22 '18 at 01:43
  • You are right. But I need to do it. As I am writing sql scripts for project in c# project. Which will be executed during deployemnt. and it does not support the 'go'. For the time being I have made a workaround, creating two seperate sql script file. First one is to check and create. Next one is the alter procedure. – rasoo Jul 17 '18 at 15:38
  • Still, this one alters the procedure. What if somebody has already created a newer version of it and now we are overwriting? How to avoid altering the procedure if it already exists and creating a new one only if it does not exist, to avoid losing the existing procedure code? – JustAMartin May 26 '20 at 11:56
  • I don't understand your comment at all. The above statement uses alter, which means it's going to change whatever compiled version exists in the database. If someone has created a newer version of the proc, you should be creating a newer version of the newer version. The entire point is to alter the procedure. – Ron May 27 '20 at 12:25
16

Updated on Sep 2020

You can use CREATE OR ALTER statement (was added in SQL Server 2016 SP1):

The CREATE OR ALTER statement acts like a normal CREATE statement by creating the database object if the database object does not exist and works like a normal ALTER statement if the database object already exists.

Iskuskov Alexander
  • 4,077
  • 3
  • 23
  • 38
8
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[spGetRailItems]') AND type in (N'P', N'PC'))
BEGIN 
execute ('
CREATE PROCEDURE [dbo].[spGetRailItems]  
AS  
BEGIN  

Declare @isLiftedBagsEnable bit=1;  
select @isLiftedBagsEnable=cast(DataValu as bit) from setups where scope =''Rail Setting'' and dataName = ''isLiftedBagsEnable'';

IF @isLiftedBagsEnable=1
BEGIN
    IF EXISTS (SELECT * FROM ITEMCONFIG)
    BEGIN
        SELECT [Item],[Desc] FROM ProcData WHERE Item IN (SELECT Item FROM ItemConfig) ORDER BY [Desc]
    END
    ELSE
    BEGIN
        SELECT [Item],[Desc] FROM ProcData ORDER BY [Desc]
    END
END
ELSE
BEGIN
    SELECT [Item],[Desc] FROM ProcData ORDER BY [Desc]
END

END

')
END

exec spGetRailItems;
Robert
  • 5,278
  • 43
  • 65
  • 115
Ajay Dagade
  • 91
  • 1
  • 1
5

Just in case if you are using SQL server 2016, then there is a shorter version to check if the proc exist and then drop and recreate it

USE [DATABASENAME]
GO
DROP PROCEDURE IF EXISTS <proc name>
GO
CREATE PROCEDURE <proc name>
AS
-- your script here
END
GO
GRANT EXECUTE ON <proc name> TO <username>

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

Niladri
  • 5,832
  • 2
  • 23
  • 41
4
USE [myDatabase]
GO

IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'sp_1')
BEGIN
  DROP PROCEDURE sp_1
END
GO   --<-- Add a Batch Separator here



CREATE PROCEDURE sp_1
AS
.................
END
GO

In SQL Server 2017 and later versions you can use the "IF EXISTS" to drop a proc or even better you can use "CREATE OR ALTER PROCEDURE"

USE [myDatabase]
GO

DROP PROCEDURE IF EXISTS sp_1;
GO   --<-- Add a Batch Separator here



CREATE OR ALTER PROCEDURE sp_1
AS
BEGIN
  .................
END
GO

You can simply ignore the "DROP IF EXISTS" command and just use "CREATE OR ALTER"

M.Ali
  • 67,945
  • 13
  • 101
  • 127
3

I like to use ALTER so I don't lose permissions and if you have a syntax error the old version still exists:

BEGIN TRY
    --if procedure does not exist, create a simple version that the ALTER will replace.  if it does exist, the BEGIN CATCH will eliminate any error message or batch stoppage
    EXEC ('CREATE PROCEDURE AAAAAAAA AS DECLARE @A varchar(100); SET @A=ISNULL(OBJECT_NAME(@@PROCID), ''unknown'')+'' was not created!''; RAISERROR(@A,16,1);return 9999')
END TRY BEGIN CATCH END CATCH
GO

ALTER PROCEDURE AAAAAAAA 
(
     @ParamsHere varchar(10)
)
AS
PRINT 'HERE IN '+(OBJECT_NAME(@@PROCID))
GO
KM.
  • 101,727
  • 34
  • 178
  • 212
  • +1 for having RAISEERROR in your stub instead of some noop. I'd rather have a stub fail loudly than do nothing silently. – Code Magician Apr 10 '14 at 16:26
  • I disagree. Testing for the existence of the procedure is much more logical than failing if it exists. That the procedure already exists is simply a logic flow point, not an error condition. – Ron Jul 03 '18 at 01:09
  • @Ron, it is no fun recreating permissions for stored procedures as they are changed randomly as developers work on them. This was an answer from over 8 years ago, TSQL has other features now. – KM. Aug 23 '18 at 13:13
  • @KM Four years, not eight. And, my original answer above specifically eliminates the permissions issue by always altering an existing proc. I'm not real clear on what that has to do with my comment above though that raising errors is not an appropriate way to deal with logic flow. The flow is to check existence, create (skeleton, with permissions if that's the way you do it), then an alter to either alter the proc you just created or alter one that had already existed. – Ron Aug 24 '18 at 21:22
0

you can execute the following:

DROP PROCEDURE IF EXISTS name_of_procedure;
CREATE PROCEDURE name_of_procedure(....)
jdlcgarcia
  • 183
  • 11
  • I down voted this because the syntax for the statement is incorrect. At a minimum, you should take the slightest effort and test the statement. – Ron Aug 21 '18 at 01:57
  • Apparently the syntax is correct for modern instances of SQL Server (v2016, 2017, ...). – Jon Davis Oct 30 '19 at 16:27