2014-04-08 121 views
4

我想檢查是否存在一個存儲過程列表。我希望這一切都能在1個腳本中逐一完成。到目前爲止,我有這樣的格式:創建一個存儲過程,如果它不存在

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 

等等。但是,我收到以下錯誤:

Incorrect syntax near the keyword 'Procedure'.

爲什麼不是我在做的工作正常?

+0

的可能的複製[如何檢查是否存儲過程創建它之前就存在(http://stackoverflow.com/questions/2072086/how-to-check-if-a-stored-procedure-exists -before-creating-it) –

回答

11

CREATE PROCEDURE必須是該批次中的第一條語句。我平時做這樣的事情:

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

CREATE PROC dbo.procname 

AS 
.... 

    GO 
    GRANT EXECUTE ON dbo.MyProc TO MyUser 

還有一兩件事,當你正在部署存儲過程考慮(如果你重新創建PROC不要忘了GRANT語句,因爲他們會丟失)是一滴可以成功,一個創建失敗。如果出現問題,我總是使用回滾編寫SQL腳本。只要確保你不小心在最後刪除提交/回滾代碼,否則你的DBA威力起重機踢你在氣管:)

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 = 'DatasetDeleteCleanup' 
      AND type = 'P' 
    ) 
COMMIT TRAN 
ELSE 
ROLLBACK TRAN 
-- END DO NOT REMOVE THIS CODE 
+0

最終成爲問題。最初,我被告知不要刪除這些表,但是我不能在if語句之後創建Create Procedure,因爲它不是批處理中的第一個命令。這只是我不知道的微軟怪異規則之一。謝謝! –

+0

這麼多的回滾等工作,爲什麼不使用'ALTER',就像我在答案中顯示的一樣。當使用「ALTER」時,如果新代碼失敗,您仍舊擁有舊版本,並且您絕對不會像在「DROP」時那樣失去權限。太糟糕的SQL Server沒有Oracle的「創建或替換過程」。 –

+0

@KM。人們可以說同樣的事情「......非常多的工作......」關於動態SQL存根創建:)。這一切都歸結爲個人偏好,它們都是完全有效的方法,只是不同的筆畫/不同的人。 無論如何,我仍然喜歡/建議對try/catch方法進行顯式存在檢查。 –

1
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 
+0

我刪除了BEGIN和END語句以便工作 – Kurapika

12

一個成語,我一直在使用最近我喜歡相當多的是:

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 

本質上講,你要創建一個存根如果程序不存在,然後改變無論是存根(如果它剛剛創建的)或現有的預處理過程。關於這一點的好處在於,您不會丟棄預先存在的過程,從而也會丟棄所有權限。您也可以在任何恰好需要它的應用程序中在不存在的情況下導致問題。

[編輯2018-02-09] - 在SQL 2016 SP1中,create proceduredrop procedure得到了一些有助於這種事情的語法糖。具體來說,你現在可以這樣做:

create or alter dbo.yourProc as 
go 

drop procedure if exists dbo.yourProc; 

雙方提供預定語句冪等(即您可以運行它多次,理想狀態)。這就是我現在要做的(假設你使用的是支持它的SQL Server版本)。

+2

+1我從來沒有見過這種方法。巧妙使用NOEXEC! –

+1

我沒有發明它,但我從這裏得到它:https://www.simple-talk.com/sql/database-administration/automating-sql-server-database-deployments-scripting-details/ –

+2

你不知道甚至不需要僞語句(選擇1作爲[尚未實現]),只是「創建過程dbo.yourProc爲」工作正常。 – slashCoder

2

我喜歡用ALTER所以我不失去權限,如果你有一個語法錯誤,舊版本依然存在:

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 
+0

+1,因爲在你的存根中有RAISEERROR而不是一些noop。我寧願讓一個存根失敗,而不是默默無聞。 –

0

你可以執行以下命令:

DROP PROCEDURE IF EXISTS name_of_procedure; 
CREATE PROCEDURE name_of_procedure(....) 
+2

這不適用於SQL Server 2012 – Kurapika

+0

甚至不能在Sql Server 2014中工作 –

4

我知道有一個可以接受的答案,但答案並不能完全解決原始問題的問題,即如果該過程不存在,則創建該過程。以下內容總是有效,並且具有不需要刪除程序的好處,如果您使用的是SQL身份驗證,則可能會出現問題。

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 
+0

嗨,你的答案非常接近我的要求。它對我很好。 你能告訴我怎麼能做到這一點,而不去... 像下面::: IF OBJECT_ID( 'mySchema.myProc')IS NULL EXEC( 'CREATE PROCEDURE mySchema.myProc AS SET NOCOUNT ON;') ALTER PROCEDURE mySchema.myProc @DeclaredParmsGoHere數據類型 AS BEGIN – rasoo

+0

@rasoo我不相信你能做到在SQL Server中,我想不出理由,爲什麼你會想。 go命令分離單個批處理命令。一旦你刪除一個,你會得到一個錯誤,即'alter'語句必須是批處理中的第一條語句。另外,Go使代碼更易於閱讀,因爲它正確地表明創建過程的步驟(如果它不存在的話)是它自己的批處理,就像更改過程的步驟一樣 – Ron

相關問題