Reputation: 2073
In my stored procedure, I have three insert statements.
On duplicate key value insertion first two queries generate the error
Violation of PRIMARY KEY constraint
and third query runs as usual.
Now I want that if any query generates any exception, everything should get rolled back.
If there isn't any exception generate by any query, it should get committed.
declare @QuantitySelected as char
set @QuantitySelected = 2
declare @sqlHeader as varchar(1000)
declare @sqlTotals as varchar(1000)
declare @sqlLine as varchar(1000)
select @sqlHeader = 'Insert into tblKP_EstimateHeader '
select @sqlHeader = @sqlHeader + '(CompanyID,CompanyName,ProjectName,EstimateID,EstimateHeader,QuoteDate,ValidUntil,RFQNum,Revision,Contact,Status,NumConfigurations) '
select @sqlHeader = @sqlHeader + ' select CompanyID,CompanyName,ProjectName,EstimateID,EstimateHeader,QuoteDate,ValidUntil,RFQNum,Revision,Contact,Status,NumConfigurations '
select @sqlHeader = @sqlHeader + 'from V_EW_Estimate_Header where EstimateID = 2203'
select @sqlTotals = 'Insert into tblKP_Estimate_Configuration_Totals '
select @sqlTotals = @sqlTotals + '(ConfigRecId,RecId,SellQty,ConfigNum,ConfigDesc,SortOrder,OptionsInMainPrice,MarkupPctQty,'
select @sqlTotals = @sqlTotals + ' SellPriceQty,RubberStamp,OptPriceQty,StatusRecid,LastUpdate_Date,LastUpdate_User,TotalCost,QuantityBracketSelected)'
select @sqlTotals = @sqlTotals + ' select ConfigRecId,RecId,SellQty' + @QuantitySelected + ',ConfigNum,ConfigDesc,SortOrder,OptionsInMainPrice'
select @sqlTotals = @sqlTotals + ' ,MarkupPctQty' + @QuantitySelected + ',SellPriceQty' + @QuantitySelected + ',RubberStamp,OptPriceQty' + @QuantitySelected + ',StatusRecid,LastUpdate_Date,LastUpdate_User,TotalCost' + @QuantitySelected + ',' + @QuantitySelected
select @sqlTotals = @sqlTotals + ' from v_EW_Estimate_Configuration_Totals where ConfigRecId = -3'
select @sqlLine = 'Insert into tblKP_Estimate_Configuration_Lines'
select @sqlLine = @sqlLine + '(MstrRfqRecId,RfqRecId,RfqLineRecId,CompanyId,VendorQuoteNum,LineGrp,LineNum,StatusRecId,'
select @sqlLine = @sqlLine + ' LineDesc,LineSize,LineMatl,LineDeco,LineFinish,CopyFromRecId,PerPieceCost,IsOptional,'
select @sqlLine = @sqlLine + ' CopyToNewRev,RecId,UnitPrice,LineQty,LinePrice,CustOrVend,SellQty1,RfqNum,ConfigLineIsOptional,ConfigLinePerPieceCost,ConfigLineRecid,SellPrice,SaleQty)'
select @sqlLine = @sqlLine + ' select distinct MstrRfqRecId,RfqRecId,RfqLineRecId,CompanyId,VendorQuoteNum,LineGrp,LineNum,'
select @sqlLine = @sqlLine + ' StatusRecId,LineDesc,LineSize,LineMatl,LineDeco,LineFinish,CopyFromRecId,PerPieceCost,IsOptional,'
select @sqlLine = @sqlLine + ' CopyToNewRev,RecId,UnitPrice' + @QuantitySelected + ',LineQty' + @QuantitySelected + ', isnull(LinePrice' + @QuantitySelected + ', 0.0000),CustOrVend,SellQty' + @QuantitySelected + ',RfqNum,ConfigLineIsOptional,ConfigLinePerPieceCost,ConfigLineRecid,SellPrice' + @QuantitySelected + ',SaleQty' + @QuantitySelected
select @sqlLine = @sqlLine + ' from v_EW_EstimateLine where rfqlinerecid in (select RfqLineRecID from kp_tblVendorRfqConfigLine where ConfigRecID = -3) '
exec( @sqlHeader)
exec(@sqlTotals)
exec(@sqlLine)
Upvotes: 79
Views: 320060
Reputation: 96016
An alternative method doesn't need a TRY...CATCH
. Instead you can enable XACT_ABORT
, which will result in the entire transaction being rolled back if an error occurs inside the transaction. Then you only need COMMIT
the transaction at the end.
Modifying the succinct code from Code Magician's answer:
SET XACT_ABORT ON;
BEGIN TRANSACTION
EXEC sys.sp_executesql @sqlHeader;
EXEC sys.sp_executesql @sqlTotals;
EXEC sys.sp_executesql @sqlLine;
COMMIT;
As a side note, as well, the SQL that builds the dynamic queries should be updated to not be open to injection and parametrise the calls to sys.sp_executesql
.
Upvotes: 0
Reputation: 24032
The good news is a transaction in SQL Server can span multiple batches (each exec
is treated as a separate batch.)
You can wrap your EXEC
statements in a BEGIN TRANSACTION
and COMMIT
but you'll need to go a step further and rollback if any errors occur.
Ideally you'd want something like this:
BEGIN TRY
BEGIN TRANSACTION
exec( @sqlHeader)
exec(@sqlTotals)
exec(@sqlLine)
COMMIT
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK
END CATCH
The BEGIN TRANSACTION
and COMMIT
I believe you are already familiar with. The BEGIN TRY
and BEGIN CATCH
blocks are basically there to catch and handle any errors that occur. If any of your EXEC
statements raise an error, the code execution will jump to the CATCH
block.
Your existing SQL building code should be outside the transaction (above) as you always want to keep your transactions as short as possible.
Upvotes: 208