2017-10-04 60 views
1

我試圖刪除存儲過程(如果存在),然後重新創建它,所有內部事務。SQL Server:不能在事務中刪除和創建SP

BEGIN TRANSACTION 
BEGIN TRY 

IF OBJECT_ID(N'dbo.GET_DATA', N'P') IS NOT NULL 
BEGIN 
    DROP PROCEDURE [dbo].[GET_DATA] 
END 

CREATE PROCEDURE [dbo].[GET_DATA] 
    @date datetime2 
AS 
    SET NOCOUNT ON 
BEGIN 

    SELECT 
     dbo.Products.product_cod AS 'product_cod', 
     dbo.Product_Types.name AS 'product_type_name', 
     dbo.UM.name AS 'um_name', 
     dbo.Products.category_id AS 'category_id', 
     dbo.Bins_Products.bin_id AS 'product_bin_id' 

    FROM dbo.Products 
     LEFT JOIN dbo.Product_Types on Products.product_type_id = Product_Types.product_type_id 
     LEFT JOIN dbo.UM on Products.um_id = UM.um_id 
     LEFT JOIN Bins_Products ON Bins_Products.product_id = Products.product_id 
    WHERE 
     Products.update_date >= @date 
END 
    COMMIT TRANSACTION 
END TRY 
BEGIN CATCH 
    SELECT ERROR_MESSAGE() AS 'ErrorMessage' 
    ROLLBACK TRANSACTION 
END CATCH 

當我運行上面的腳本,我得到以下錯誤:

Msg 156, Level 15, State 1, Line 9 
Incorrect syntax near the keyword 'PROCEDURE'. 
Msg 137, Level 15, State 2, Line 31 
Must declare the scalar variable "@date". 

而且我對SET和@date波浪線。

IF語句和create語句都可以自行工作。

+0

您在創建過程之前缺少GO語句,並且無法使用try/catch塊。 –

+0

'CREATE PROCEDURE'和'DROP PROCEDURE'不能以事務方式完成。相反,如果它還不存在,則創建一個空過程('CREATE PROCEDURE GET_DATA AS BEGIN RETURN END'),然後無條件地創建'ALTER'。 –

+0

@DeanSavović我嘗試使用GO,但只會產生其他錯誤。 – dmdany07

回答

2

您可以使用EXEC創建一個事務中的程序,但是這是非常不方便的,因爲整個身體需要進行轉義。更好的辦法是,以確保存儲過程始終存在,然後執行ALTER,這並不需要一個單獨的事務:

IF OBJECT_ID('Foo', 'P') IS NULL 
    EXEC ('CREATE PROCEDURE Foo AS BEGIN RETURN END;'); 
GO 
ALTER PROCEDURE Foo(@Arg INT) AS BEGIN 
    ... 
END; 

這種方法的另一個好處(或缺點,這取決於你的部署過程)這是不會丟棄和創建存儲過程的完整存儲權限。

+0

是的,這看起來就像我在找什麼,即使它不使用交易。謝謝 – dmdany07

+0

@ dmdany07 - 你可以把它封裝在一個事務中就好了。難點在於你a)不能使用'TRY''''CATCH'(因爲它*是那些不能跨越批次的事物,而不是像某些答案所建議的事務)和b)一旦你在一個單獨的批次中,嘗試以確定你是否應該'COMMIT'或'ROLLBACK'是難以自動化的位。 –

0

過程定義必須在其自己的批次中。在Management Studio中,你就會把兩行

go 

create procedure之前和之後end。不幸的是,交易不能跨越多個批次。

您可以在exec調用中創建程序?像:

exec ('create procedure dbo.MyProc as ...'); 
+0

」交易不能跨越多個批次。「 - 我不知道你爲什麼這麼想,但這是公然不真實的。批次和交易是正交的。 –

0

使用此代碼,這是我的工作示例。

按照您的表格和要求對其進行修改。

USE [DatabaseName] 
GO 

/****** Object: StoredProcedure [dbo].[_AdvancePaymentDelete] Script Date: 10/04/2017 15:12:43 ******/ 
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[_AdvancePaymentDelete]') AND type in (N'P', N'PC')) 
DROP PROCEDURE [dbo].[_AdvancePaymentDelete] 
GO 

USE [DatabaseName] 
GO 

/****** Object: StoredProcedure [dbo].[_AdvancePaymentDelete] Script Date: 10/04/2017 15:12:43 ******/ 
SET ANSI_NULLS ON 
GO 

SET QUOTED_IDENTIFIER ON 
GO 

CREATE PROCEDURE [dbo].[_AdvancePaymentDelete] 
(
     @_ADVANCEPAYMENTID_PK uniqueidentifier, 
     @_COMPANYID_PK uniqueidentifier , 
     @_COMPANYDETID_PK uniqueidentifier , 
     @_USERID_PK uniqueidentifier 
     ) 
AS 
BEGIN 
     BEGIN TRANSACTION; 
     SAVE TRANSACTION MySavePoint; 
    DECLARE @ErrorMessage nvarchar(MAX) = 'OK'; 

     BEGIN TRY 
UPDATE [dbo].[_ADVANCEPAYMENT] SET _ISDELETED = N'2' 

WHERE _COMPANYID_PK = @_COMPANYID_PK AND _COMPANYDETID_PK = @_COMPANYDETID_PK AND _USERID_PK = @_USERID_PK AND _ADVANCEPAYMENTID_PK = @_ADVANCEPAYMENTID_PK AND _ISDELETED = N'1' 

UPDATE [dbo].[_ADVANCEPAYMENTDET] SET _ISDELETED = N'2' 

WHERE _COMPANYID_PK = @_COMPANYID_PK AND _COMPANYDETID_PK = @_COMPANYDETID_PK AND _USERID_PK = @_USERID_PK AND _ADVANCEPAYMENTID_PK = @_ADVANCEPAYMENTID_PK AND _ISDELETED = N'1' 


    END TRY 
    BEGIN CATCH 
    IF @@TRANCOUNT > 0 
     BEGIN 

    ROLLBACK TRANSACTION MySavePoint; -- rollback to MySavePoint 
    DECLARE @ErrorSeverity INT = ERROR_SEVERITY(); 
    DECLARE @ErrorState INT = ERROR_STATE(); 
    SET @ErrorMessage = 'Error No : ' + CAST (ERROR_NUMBER() AS nvarchar(MAX)) + CHAR(13) + 'Line No : ' + CAST (ERROR_LINE() AS nvarchar(MAX))+ CHAR(13) + 'Procedure Name : ' + QUOTENAME(OBJECT_SCHEMA_NAME(@@PROCID)) + '.' + QUOTENAME(OBJECT_NAME(@@PROCID)) + CHAR(13) + 'Error Message : ' + ERROR_MESSAGE(); 
    RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState); 
     END 
    END CATCH 

    COMMIT TRANSACTION 
END; 

SELECT @ErrorMessage 

GO 
0

我不知道你正在嘗試做的有,但我可以看到一些明顯的事情錯這個代碼,它應該是這個樣子......

提個醒創建過程必須成爲批處理中唯一的語句,這意味着您無法在一個事務中包裝drop和create proc。

IF OBJECT_ID(N'dbo.GET_DATA', N'P') IS NOT NULL 
BEGIN 
    DROP PROCEDURE [dbo].[GET_DATA] 
END 
GO 

CREATE PROCEDURE [dbo].[GET_DATA] 
    @date datetime2 
AS 
BEGIN      --<-- Proc body start 
    SET NOCOUNT ON; 

BEGIN TRY 
    BEGIN TRANSACTION; 

      SELECT 
       dbo.Products.product_cod AS 'product_cod', 
       dbo.Product_Types.name AS 'product_type_name', 
       dbo.UM.name AS 'um_name', 
       dbo.Products.category_id AS 'category_id', 
       dbo.Bins_Products.bin_id AS 'product_bin_id' 

      FROM dbo.Products 
       LEFT JOIN dbo.Product_Types on Products.product_type_id = Product_Types.product_type_id 
       LEFT JOIN dbo.UM on Products.um_id = UM.um_id 
       LEFT JOIN Bins_Products ON Bins_Products.product_id = Products.product_id 
      WHERE 
       Products.update_date >= @date; 
    COMMIT TRANSACTION; 
END TRY 

BEGIN CATCH 
     IF (@@TRANCOUNT > 0) -- Check for open transactions before you try to rollback 
     BEGIN 
      ROLLBACK TRANSACTION; 
     END 

    SELECT ERROR_MESSAGE() AS 'ErrorMessage' 

END CATCH 

END      --<-- Proc body End 
GO 
+0

如果由於某種原因導致創建失敗,我有什麼方法可以回滾該刪除過程語句嗎? – dmdany07

2

只是爲了證明這種可以在事務中完成的,這裏有一個演示腳本:

create procedure dbo.A 
as 
    select 1 as T 
go 
exec dbo.A 
go 
begin transaction 
go 
IF OBJECT_ID(N'dbo.A', N'P') IS NOT NULL 
BEGIN 
    EXEC('drop procedure dbo.A') 
END 
go 
create procedure dbo.A 
as 
    select penguin from sys.objects --This will fail 
go 
IF OBJECT_ID(N'dbo.A', N'P') IS NOT NULL 
BEGIN 
    commit 
END 
ELSE 
BEGIN 
    rollback transaction 
END 
go 
exec dbo.A 

它產生試圖創建一個新的A程序時出現錯誤和回滾恢復到原來的版本A。這隻能真正起作用(如這裏所示),在創建新版本A時會產生一個硬錯誤,以便我們可以在之後檢測到並確定rollback而不是commit

這就是說,我仍然會自己使用Jeroen's answer

+0

您提出了另一個優點,但延遲名稱解析意味着「CREATE PROCEDURE」很少會失敗,即使該過程是絕對錯誤的。硬語法錯誤或失敗的DDL觸發器是關於事務將防範的唯一事情。 –

+0

謝謝你的例子,很高興知道它可以做到。看起來似乎有點煩人。我最終以Jereon的榜樣去。 – dmdany07