2017-10-04 15 views
1

ストアドプロシージャが存在する場合は削除してから、トランザクション内に再作成しようとしています。SQL Server:トランザクションでSPをDROPおよびCREATEできません。

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に波線を持っている:私は上記のスクリプトを実行すると

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 

は、私は次のエラーを取得します。

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を使用することができますが、体全体をエスケープする必要があるため、それは、非常に不便です。

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

このアプローチのもう一つの利点(または欠点を、デプロイメントプロセスに依存する):より良いアプローチは、ストアドプロシージャが常に存在することを確認した後、ALTERを実行し、別のトランザクションを必要としないことですこれは、ストアドプロシージャの既存のパーミッションを削除して作成するのとは異なり、そのまま残しておくことです。

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プロシージャを作成しようとすると、エラーを生成し、ロールバックが元に戻ります:

+0

はい、これは、トランザクションを使用しない場合でも、私が探していたようです。ありがとう – dmdany07

+0

@ dmdany07 - あなたはちょうど良いトランザクションでそれをラップすることができます。難しいのは、a) 'TRY' /' CATCH'を使うことができないということです。なぜなら、いくつかの回答が示唆しているようにトランザクションではなく、バッチにまたがることができないものであるからです。 'COMMIT'か' ROLLBACK'を自動化するのが難しいのかどうかを調べることです。 –

0

プロシージャ定義は、それ自身のバッチ内にある必要があります。 Management Studioで、あなたはcreate procedure前とend

go 

で2行を置くところ。残念ながら、トランザクションは複数のバッチにまたがることはできません。

execコールで手順を作成できますか? Like:

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

私はあなたがそこにやろうとしているのか分からないが、私はこのコードで間違っていくつかの明白な物事を見ることができ、それは次のようになります...

マインドあなたは手順を作成する必要がありますバッチ内で唯一のステートメントでなければなりません。つまり、1つのトランザクションでドロップをラップして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

何らかの理由でcreate procedureが失敗した場合、drop procedure文をロールバックする方法はありますか? – dmdany07

2

はちょうどこのトランザクションで行うことができることを実証するために、ここでのデモスクリプトですバージョンAです。これは実際には(ここのように)新しいバージョンのAを作成するとハードエラーが発生し、それを検出して、commitではなくrollbackにすることができます。

私はまだ自分自身Jeroen's answerを使用していると言われています。

+0

あなたは別の優れた点を挙げていますが、名前解決が遅れたということは、プロシージャーに絶対的に障害があっても、CREATE PROCEDUREはほとんど失敗しないことを意味します。ハード構文エラーまたは失敗したDDLトリガーは、トランザクションが守る唯一のものです。 –

+0

この例をありがとうございます。まだ少し厄介なようです。私はジェレオンの例と一緒に行きました。 – dmdany07

関連する問題