2017-10-04 1 views
1

Ich versuche eine gespeicherte Prozedur löschen, wenn es existiert, und erstellen Sie es dann, alles innerhalb einer Transaktion.SQL Server: Kann nicht abfallen und erstellen SP in Transaktion

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 

Als ich das Drehbuch oben laufen lasse, erhalte ich folgende Fehler:

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

Und ich habe verschnörkelte Linien auf der SET und @date.

Die IF-Anweisung und die create-Anweisung funktionieren alle von selbst.

+0

Sie fehlen eine GO-Anweisung vor der Erstellungsprozedur, und Sie können try/catch-Block nicht verwenden. –

+0

'CREATE PROCEDURE' und' DROP PROCEDURE' können nicht transaktional durchgeführt werden. Erstellen Sie stattdessen eine leere Prozedur, wenn sie noch nicht existiert ("CREATE PROCEDURE GET_DATA AS BEGIN RETURN END"), dann bedingungslos "ALTER". –

+0

@ DeanSavović Ich habe versucht, mit GO, aber das schafft nur andere Fehler. – dmdany07

Antwort

2

können Sie EXEC verwenden Sie die Prozedur innerhalb einer Transaktion zu erstellen, aber das ist sehr ungünstig, da der ganze Körper entkommen werden muss. Ein besserer Ansatz ist die gespeicherte Prozedur immer vorhanden ist, um sicherzustellen, und dann ein ALTER ausführen, die keine separate Transaktion benötigt:

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

Ein weiterer Vorteil dieses Ansatzes (oder ein Nachteil, je nach Bereitstellungsprozess) Dies bedeutet, dass vorhandene Berechtigungen für die gespeicherte Prozedur intakt bleiben, im Gegensatz zum Löschen und Erstellen.

+0

Ja, das scheint das zu sein, wonach ich gesucht habe, auch wenn es keine Transaktion verwendet. Danke – dmdany07

+0

@ dmdany07 - Sie können es in einer Transaktion gut wickeln. Die Schwierigkeit besteht darin, dass Sie a) "TRY"/"CATCH" nicht verwenden können (weil es * die * sind, die nicht Stapel umspannen können, nicht Transaktionen, wie einige Antworten vorgeschlagen haben) und b) wenn Sie einmal in einem separaten Stapel sind um herauszufinden, ob Sie 'COMMIT' oder' ROLLBACK' sollten, ist das schwierig zu automatisierende Bit. –

0

Eine Prozedurdefinition muss in einem eigenen Batch sein. In Management Studio würde man zwei Linien mit

setzt
go 

vor create procedure und nach den end. Leider können Transaktionen nicht mehrere Stapel umfassen.

Sie könnten die Prozedur in einem exec Aufruf erstellen? Wie:

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

"Transaktionen können nicht mehrere Stapel umfassen." - Ich weiß nicht, warum du das denkst, aber es ist offensichtlich unwahr. Chargen und Transaktionen sind orthogonal. –

0

Verwenden Sie diesen Code, das ist mein Arbeitsbeispiel.

Ändern Sie es nach Ihren Tabellen und Anforderungen.

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

Ich weiß nicht, was Sie dort zu tun versuchen, aber ich kann ein paar offensichtlichen Dinge falsch mit diesem Code sehen, die es so etwas wie diese ... Sie

Geist

Verfahren erstellen aussehen sollte muss Die einzige Anweisung innerhalb eines Stapels, das heißt, Sie können drop nicht umbrechen und proc in einer Transaktion erstellen.

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

Gibt es eine Möglichkeit, die Drop Procedure-Anweisung rückgängig zu machen, wenn die Erstellung fehlschlägt? – dmdany07

2

Nur um zu zeigen, dass diese kann in einer Transaktion, hier geschehen ist ein Demo-Skript:

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 

Es erzeugt einen Fehler, wenn eine neue A Prozedur zu erstellen versucht und das Rollback kehrt zurück auf den ursprünglichen Version von A. Dies kann nur wirklich funktionieren (wie hier), wo das Erstellen der neuen Version von A zu einem harten Fehler führt, so dass wir dies später erkennen und zu rollback anstelle von commit entscheiden können.

Das gesagt, ich würde immer noch Jeroen's answer selbst verwenden.

+0

Sie heben einen weiteren ausgezeichneten Punkt, obwohl - verzögerte Namensauflösung bedeutet, dass "CREATE PROCEDURE" wird selten fehlschlagen, auch wenn das Verfahren absolut fehlerhaft ist. Ein schwerer Syntaxfehler oder ein fehlgeschlagener DDL-Trigger sind die einzigen Dinge, vor denen die Transaktion schützen würde. –

+0

Vielen Dank für das Beispiel, gut zu wissen, dass es möglich ist. Sieht immer noch ein bisschen umständlich aus. Ich ging mit Jereon's Beispiel. – dmdany07

Verwandte Themen