After Running this query, I am getting one custom Error :
Debit account balance can not be less than 0. somemail#7dmail.com/123/xxx/123456
And Two regular errors :
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 2, current count = 0.
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
I think, that transaction errors happen because something is throwing exception before transaction is committed.
Custom Error is from another query(AddJournalEntry) which is written below. I can not see connection between these two queries.
Query:
-- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE [dbo].[RevokeOrder]
#OrderId int = null
--,#EntryId int OUTPUT
AS
declare #OrderTypeId nvarchar(30)
declare #MasterEntryId int
declare #NewEntryId int
declare #CustomerGuid uniqueidentifier
declare #Debit int
declare #Credit int
declare #Explanation nvarchar(100)
declare #Amount decimal(18, 8)
declare #AmountFilled decimal(18, 8)
declare #Total decimal(18, 8)
declare #TotalLeft decimal(18, 8)
declare #AmountLeft decimal(18, 8)
declare #AssetId nvarchar(30)
declare #QuoteAssetId nvarchar(30)
declare #QuotePrice decimal(18, 8)
declare #AssetReserveAccountId int
declare #AssetAccountId int
declare #EntryAmount decimal(18, 8)
BEGIN;
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET XACT_ABORT ON;
SET NOCOUNT ON;
print ''
print '++++++++++++++++++++++++++++++++++++++++++++++++'
print 'Start RevokeOrder procedure'
print '++++++++++++++++++++++++++++++++++++++++++++++++'
print ''
begin tran
select
#OrderTypeId = OrderTypeId,
#CustomerGuid = CustomerGuid,
#Amount = Amount,
#AmountFilled = AmountFilled,
#AssetId = AssetId,
#QuoteAssetId = QuoteAssetId,
#QuotePrice = QuotePrice,
#Total = Total,
#TotalLeft = TotalLeft
from dbo.[vOrder] WITH (HOLDLOCK, ROWLOCK)
where OrderId = #OrderId
if ##ERROR <> 0 or ##ROWCOUNT = 0
begin
rollback
raiserror ('Order not found', 16, 1)
return 3
end
set #AmountLeft = #Amount - ISNULL(#AmountFilled, 0)
if (#OrderTypeId = 'buy')
begin
--declare #Released decimal(18, 8)
--select #Released = coalesce(SUM(Total), 0)
--from dbo.[vOrder]
--where OrderId in (select FillerId from dbo.Filler where OrderId = #OrderId)
--or OrderId in (select OrderId from dbo.Filler where FillerId = #OrderId)
declare #Released decimal(18, 8)
select #Released = Amount
from dbo.Trade t join dbo.JournalEntry j on t.EntryId = j.EntryId
where SourceOrderId = #OrderId
set #Released = ISNULL(#Released, 0)
print 'Order Type = ' + #OrderTypeId
print '#InitialAmount = ' + isnull(cast (#Amount as nvarchar), 'NULL') + ' ' + #AssetId
print '#AmountFilled = ' + isnull(cast (#AmountFilled as nvarchar), 'NULL') + ' ' + #AssetId
print '#AmountLeft = ' + isnull(cast (#AmountLeft as nvarchar), 'NULL')
print ''
print '#InitialBlocked = ' + isnull(cast (#Total as nvarchar), 'NULL') + ' ' + #QuoteAssetId
print '#Released = ' + cast (#Released as nvarchar) + ' ' + #QuoteAssetId
print '#CurrentBlocked = ' + isnull(cast (#TotalLeft as nvarchar), 'NULL') + ' ' + #QuoteAssetId
print ''
set #Explanation = N'Revoke order process. Reverse blocked quote amount' --+ 'reverse' --+ cast(#EntryId as nvarchar)
DECLARE #RC int
declare #Date datetimeoffset
set #Date = sysdatetimeoffset()
set #AssetReserveAccountId = (select AccountId from dbo.Account where CustomerGuid = #CustomerGuid and MasterAccountNo = 99931 and AssetId = #QuoteAssetId)
set #AssetAccountId = (select AccountId from dbo.Account where CustomerGuid = #CustomerGuid and MasterAccountNo = 9993 and AssetId = #QuoteAssetId)
set #EntryAmount = #Amount * #QuotePrice - #Released
print 'Order total = ' + cast (#Amount * #QuotePrice as nvarchar)
print 'Money spent to buy asset = ' + cast (#Released as nvarchar)
print 'Money to refund to buyer = ' + cast (#EntryAmount as nvarchar)
print '#Amount = ' + cast (#Amount as nvarchar)
print '#QuotePrice = ' + cast (#QuotePrice as nvarchar)
--rollback
--return 111
EXECUTE #RC = [dbo].[AddJournalEntry]
#Date
,#AssetReserveAccountId
,#AssetAccountId
,#EntryAmount
,#QuoteAssetId
,#Explanation
,'revoke'
,#OrderId
,#MasterEntryId
,#NewEntryId OUTPUT
if ##ERROR <> 0 or #RC <> 0
begin
rollback
raiserror ('Revoke order process. Can not add reverse blocked quote amount journal entry', 16, 1)
return 4
end
end
if (#OrderTypeId = 'sell')
begin
print 'sell order'
set #Explanation = N'Revoke order process. Reverse blocked amount journal entry' --+ 'რევერსი' --+ cast(#EntryId as nvarchar)
set #Date = sysdatetimeoffset()
set #AssetReserveAccountId = (select AccountId from dbo.Account where CustomerGuid = #CustomerGuid and MasterAccountNo = 99931 and AssetId = #AssetId)
set #AssetAccountId = (select AccountId from dbo.Account where CustomerGuid = #CustomerGuid and MasterAccountNo = 9993 and AssetId = #AssetId)
set #EntryAmount = #AmountLeft
print '#EntryAmount = ' + isnull(cast (#EntryAmount as nvarchar), 'NULL')
EXECUTE #RC = [dbo].[AddJournalEntry]
#Date
,#AssetReserveAccountId
,#AssetAccountId
,#EntryAmount
,#AssetId
,#Explanation
,'revoke'
,#OrderId
,#MasterEntryId
,#NewEntryId OUTPUT
if ##ERROR <> 0 or #RC <> 0
begin
rollback
raiserror ('Revoke order process. Can not add reverse blocked amount journal entry', 16, 1)
return 5
end
end
-- STEP 4
update dbo.[Order]
set OrderStatusId = 30
where OrderId = #OrderId
if ##ERROR <> 0 or ##ROWCOUNT = 0
begin
rollback
raiserror ('Can not set order status to REVOKED', 16, 1)
return 2
end
commit tran
return 0
END
go
Custom error I am getting is defined in query called
AddJournalEntry
-- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE [dbo].[AddJournalEntry]
#Date datetimeoffset,
#Debit int,
#Credit int,
#Amount decimal(18, 8),
#AssetId nvarchar(50),
#Explanation nvarchar(100),
#EntryType nvarchar(50),
#OrderId int = null,
#MasterEntryId int = null,
#EntryId int OUTPUT
AS
declare #DebitBalance decimal(18, 8)
declare #DebitAccountAssetId nvarchar(10)
declare #CreditAccountAssetId nvarchar(10)
declare #CreditBalance decimal(18, 8)
declare #ToIncrease nvarchar(100)
declare #DebitAccountTitle nvarchar(500)
declare #CreditAccountTitle nvarchar(500)
declare #Error nvarchar(500)
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET XACT_ABORT ON;
SET NOCOUNT ON;
print ''
print '++++++++++++++++++++++++++++++++++++++++++++++++'
print 'Start AddJournalEntry procedure'
print '++++++++++++++++++++++++++++++++++++++++++++++++'
print ''
begin tran
-- STEP 1
print '#Debit = ' + isnull(cast(#Debit as nvarchar), 'NULL')
print '#Credit = ' + isnull(cast(#Credit as nvarchar), 'NULL')
print '#AssetId = ' + cast(#AssetId as nvarchar(50))
print '#Amount = ' + cast(#Amount as nvarchar(50))
update dbo.Account
set Debit = Debit + #Amount, LastTransactionDate = SYSDATETIMEOFFSET()
where AccountId = #Debit
if ##ERROR <> 0 or ##ROWCOUNT = 0
begin
rollback
raiserror ('Can not update debit account balance', 16, 1)
return 2
end
update dbo.Account
set Credit = Credit + #Amount, LastTransactionDate = SYSDATETIMEOFFSET()
where AccountId = #Credit
if ##ERROR <> 0 or ##ROWCOUNT = 0
begin
rollback
raiserror ('Can not find or update credit account balance', 16, 1)
return 3
end
select
#DebitBalance = Balance,
#ToIncrease = ToIncrease,
#DebitAccountTitle = AccountFullTitle
from dbo.vAccount
where AccountId = #Debit and AssetId = #AssetId
if ##ERROR <> 0 or ##ROWCOUNT = 0
begin
rollback
raiserror ('Can not find debit account', 16, 1)
return 4
end
print 'New Debit Balance = ' + cast(#DebitBalance as nvarchar(50))
if (#DebitBalance < 0 and #ToIncrease = 'debit') or (#DebitBalance > 0 and #ToIncrease = 'credit')
begin
rollback
set #Error = 'Debit account balance can not be less than 0. ' + #DebitAccountTitle
raiserror (#Error, 16, 1)
return 5
end
-- STEP 4
select
#CreditBalance = Balance,
#ToIncrease = ToIncrease,
#CreditAccountTitle = AccountFullTitle
from dbo.vAccount
where AccountId = #Credit and AssetId = #AssetId
if ##ERROR <> 0 or ##ROWCOUNT = 0
begin
rollback
raiserror ('Can not find credit account', 16, 1)
return 55
end
if (#CreditBalance > 0 and #ToIncrease = 'credit') or (#CreditBalance < 0 and #ToIncrease = 'debit')
begin
rollback
set #Error = 'Credit account balance can not be less than 0. ' + #CreditAccountTitle
raiserror ( #Error, 16, 1)
return 56
end
-- STEP 4
insert dbo.JournalEntry
select SYSDATETIMEOFFSET(), #Debit, #Credit, #Amount, #AssetId, #Explanation, #DebitBalance, #CreditBalance, #OrderId, #MasterEntryId, #EntryType, NEWID()
if ##ERROR <> 0
begin
rollback
raiserror ('Can not insert entry record', 16, 1)
return 1
end
commit tran
set #EntryId = SCOPE_IDENTITY()
return 0
END
go
You are executing multiple ROLLBACK commands when you should only execute it once. A rollback will lower the transaction count from any amount higher than 0 directly to 0, so if you execute 3 BEGIN TRANSACTION, your ##TRANCOUNT is 3 and a rollback will set it to 0. The problem is that you are executing a rollback inside the called SP (the nested one) and again after the SP returns.
You can see the problem with this example:
BEGIN TRANSACTION
SELECT ##TRANCOUNT -- 1
BEGIN TRANSACTION
SELECT ##TRANCOUNT -- 2
ROLLBACK
SELECT ##TRANCOUNT -- 0
ROLLBACK -- The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
And this is the failing execution route from your SP:
CREATE PROCEDURE [dbo].[RevokeOrder]
#OrderId int = null
AS
begin tran -- Create a transaction here (TRANCOUNT = 1)
if (...)
begin
EXECUTE #RC = [dbo].[AddJournalEntry] -- Executes a rollback inside
if ##ERROR <> 0 or #RC <> 0
begin
rollback -- When the execution reaches this rollback, TRANCOUNT is 0 and the rollback fails
raiserror ('Revoke order process. Can not add reverse blocked quote amount journal entry', 16, 1)
return 4
end
end
END
And the SP being called:
CREATE PROCEDURE [dbo].[AddJournalEntry]
AS
BEGIN
begin tran -- TRANCOUNT = 2
if (#DebitBalance < 0 and #ToIncrease = 'debit') or (#DebitBalance > 0 and #ToIncrease = 'credit')
begin
rollback -- Undoes all changes from the start of the first BEGIN TRAN and sets TRANCOUNT to 0
set #Error = 'Debit account balance can not be less than 0. ' + #DebitAccountTitle
raiserror (#Error, 16, 1)
return 5
end
END
I'd recommend using TRY/CATCH blocks and doing the ROLLBACK on the CATCH. This would be like the following:
CREATE PROCEDURE [dbo].[RevokeOrder]
#OrderId int = null
AS
BEGIN TRY
begin tran
if (...)
begin
EXECUTE #RC = [dbo].[AddJournalEntry]
if ##ERROR <> 0 or #RC <> 0
begin
raiserror ('Revoke order process. Can not add reverse blocked quote amount journal entry', 16, 1)
return 4
end
end
COMMIT
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0 -- Might want to check XACT State also
ROLLBACK
-- Additional logging/fixing stuff
END CATCH
END
For detailed explanation on SQL Server error handling, check this post.
Related
Im getting this error message I've tried to look at my code I cant seem to see where its coming from, please help.
ERROR:-
Msg 102, Level 15, State 1, Procedure _TESTbspPostAPTrans, Line 249
Incorrect syntax near '0'.
SET ANSI_NULLS ON
GO
CREATE procedure [dbo].[_TESTbspPostAPTrans]
#AutoIdx bigint = NULL OUTPUT,
#TxDate datetime,
#Id varchar(5),
#AccountLink int,
#TrCodeID int,
#Debit float,
#Credit float,
#CurrencyID int,
#ExchangeRate float,
#ForeignDebit float,
#ForeignCredit float,
#Description varchar(100),
#TaxTypeID int,
#Reference varchar(50),
#Order_No varchar(50),
#ExtOrderNum varchar(50),
#AuditNumber varchar(50),
#Tax_Amount float,
#ForeignTaxAmount float,
#Project int,
#Outstanding float,
#ForeignOutstanding float,
#InvNumKey bigint,
#UserName varchar(20),
#Reference2 varchar(50),
#SettlementTermsID int,
#TxBranchID int,
#GLTaxAccountID int = 0,
#cLineUserFields varchar (max) = ''
as
set nocount on
set identity_insert PostAP on
if (IsNull(#AccountLink, 0) = 0) begin
RAISERROR('_bspPostAPTrans: No Accounts Payable Account specified!', 16, 1)
return 0
end
else
if (IsNull(#Id, '') = '') begin
RAISERROR('_bspPostAPTrans: No Transaction ID specified!', 16, 1)
return 0
end
else
if (IsNull(#TrCodeID, 0) = 0) begin
RAISERROR('_bspPostAPTrans: No Transaction Code specified!', 16, 1)
return 0
end
else
if (IsNull(#AuditNumber, '') = '') begin
RAISERROR('_bspPostAPTrans: No Audit Trail Number specified!', 16, 1)
return 0
end
--begin tran --CC: Will now be controlled in Code
declare #SPError int
declare #BranchID int
select #BranchID = Vendor_iBranchID from Vendor where DCLink = #AccountLink
if isnull(#AutoIdx, 0) <= 0 begin
if isnull (#AccountLink, 0) = 4917 begin
set nocount on
set identity_insert PostAR on
--begin tran --CC: Will now be controlled in Code
select #BranchID = Client_iBranchID from Client where DCLink = #AccountLink
select #AutoIdx = ident_current('PostAR') + 1
insert into PostAR
(
AutoIdx,
TxDate,
[Id],
AccountLink,
TrCodeID,
Debit,
Credit,
iCurrencyID,
fExchangeRate,
fForeignDebit,
fForeignCredit,
[Description],
TaxTypeID,
Reference,
Order_No,
ExtOrderNum,
cAuditNumber,
Tax_Amount,
fForeignTax,
Project,
Outstanding,
fForeignOutstanding,
InvNumKey,
UserName,
cReference2,
iPostSettlementTermsID,
iTxBranchID,
iGLTaxAccountID
)
values
(
#AutoIdx,
#TxDate,
#Id,
#AccountLink,
#TrCodeID,
#Debit,
#Credit,
#CurrencyID,
#ExchangeRate,
#ForeignDebit,
#ForeignCredit,
#Description,
#TaxTypeID,
#Reference,
#Order_No,
#ExtOrderNum,
#AuditNumber,
#Tax_Amount,
#ForeignTaxAmount,
#Project,
#Outstanding,
#ForeignOutstanding,
#InvNumKey,
#UserName,
#Reference2,
#SettlementTermsID,
#TxBranchID,
#GLTaxAccountID
)
set #SPError = ##ERROR
set identity_insert PostAR off
-- Post User Defined Fields
if (len (#cLineUserFields) > 0) begin
declare #UDFSQLText1 varchar(max)
set #UDFSQLText1 = 'update PostAR set ' + #cLineUserFields + ' where AutoIdx = ' + CAST(#AutoIdx as varchar)
execute (#UDFSQLText1)
set #SPError = ##ERROR
if #SPError <> 0 goto AbortTran
-- Update Client Balances
end
UPDATE dbo.Client
SET DCBalance = Round(IsNull (DCBalance, 0) + (#Debit - #Credit), 4),
fForeignBalance = Round(IsNull(fForeignBalance, 0) + (#ForeignDebit - #ForeignCredit), 4)
WHERE DCLink = #AccountLink
set #SPError = ##ERROR
if #SPError <> 0 goto AbortTran1
goto CommitTran1
--rollback tran --CC: Will now be controlled in Code
RAISERROR (#SPError, 16, 1)
return #SPError
if #SPError <> 0 goto AbortTran
END
select #AutoIdx = ident_current('PostAP') + 1
insert into PostAP
(
AutoIdx,
TxDate,
[Id],
AccountLink,
TrCodeID,
Debit,
Credit,
iCurrencyID,
fExchangeRate,
fForeignDebit,
fForeignCredit,
[Description],
TaxTypeID,
Reference,
Order_No,
ExtOrderNum,
cAuditNumber,
Tax_Amount,
fForeignTax,
Project,
Outstanding,
fForeignOutstanding,
InvNumKey,
DTStamp,
UserName,
cReference2,
iPostSettlementTermsID,
PostAP_iBranchID,
iTxBranchID,
iGLTaxAccountID
)
values
(
#AutoIdx,
#TxDate,
#Id,
#AccountLink,
#TrCodeID,
#Debit,
#Credit,
#CurrencyID,
#ExchangeRate,
#ForeignDebit,
#ForeignCredit,
#Description,
#TaxTypeID,
#Reference,
#Order_No,
#ExtOrderNum,
#AuditNumber,
#Tax_Amount,
#ForeignTaxAmount,
#Project,
#Outstanding,
#ForeignOutstanding,
#InvNumKey,
GetDate(),
#UserName,
#Reference2,
#SettlementTermsID,
#BranchID,
#TxBranchID,
#GLTaxAccountID
)
set #SPError = ##ERROR
set identity_insert PostAP off
if #SPError <> 0 goto AbortTran1
-- Post User Defined Fields
if (len (#cLineUserFields) > 0) begin
declare #UDFSQLText varchar(max)
set #UDFSQLText = 'update PostAP set ' + #cLineUserFields + ' where AutoIdx = ' + CAST(#AutoIdx as varchar)
execute (#UDFSQLText)
set #SPError = ##ERROR
if #SPError <> 0 goto AbortTran1
end
-- Update Vendor Balances
UPDATE VENDOR
SET DCBalance = Round(IsNull (DCBalance, 0) + (#Credit - #Debit), 4),
fForeignBalance = Round(IsNull(fForeignBalance, 0) + (#ForeignCredit - #ForeignDebit), 4)
WHERE DCLink = #AccountLink
set #SPError = ##ERROR
if #SPError <> 0 goto AbortTran1
goto CommitTran1
AbortTran1:
--rollback tran --CC: Will now be controlled in Code
RAISERROR (#SPError, 16, 1)
return #SPError
CommitTran1:
--commit tran --CC: Will now be controlled in Code
return 0 --scope_identity() -- return can only return an int, not a bigint,
-- AutoIdx has been added as a paramater for the BA sync and is an OUTPUT paramter
GO
Any help will be greatly appreciated
I think in line 62 and 63 you have 2 opened BEGIN with just first END in line 159.on END is missing in line 160
I'm having this error when I try to execute this code:
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.
I know the problem is on the sp [dbo].[QueueInsert], the sp has an error and it goes directly to the catch. It's funny because the first one (EXEC [dbo].[BacthInsert]) is inserting a summary record and after running the sp, I see the record, so is doing a commit and not doing a rollback. What's wrong with this code? thanks!
CREATE PROCEDURE [cnfg].[SendEmail]
(
,#Employees [dbo].[Employees] readonly
,#ApplicationName NVARCHAR(256)
,#ErrorMsg NVARCHAR(300) = NULL OUTPUT
)
AS
BEGIN
DECLARE #ReturnVal INT
DECLARE #ApplicationId UNIQUEIDENTIFIER
DECLARE #NewIdBatch INT
DECLARE #ID INT
DECLARE #EmployeeId INT
DECLARE #Index INT = 1
DECLARE #Total INT
SET NOCOUNT ON;
SET XACT_ABORT ON;
SET #ReturnVal = 0;
SET #ErrorMsg = '';
SET #ApplicationId = [GetId](#ApplicationName);
IF (#ApplicationId IS NULL)
BEGIN
SET #ReturnVal = 1;
SET #ErrorMsg = 'The Application Name does not exist in the database';
Goto ProcedureExit
END
----------------------------------------------------------
BEGIN TRY -- Start Main TRY
----------------------------------------------------------
BEGIN TRANSACTION;
EXEC [dbo].[BacthInsert]
#ParameterId = 1
,#ID = #NewSendEmailBatchId OUTPUT
,#ErrorMsg = #ErrorMsg OUTPUT
IF ( #ErrorMsg <> '' )
BEGIN
SET #ReturnVal = 1;
SET #ErrorMsg = 'There was an error trying to insert data into [dbo].[BacthInsert] table';
RAISERROR(#ErrorMsg, 16, 1)
END
SELECT ROW_NUMBER() OVER ( ORDER BY EmployeeId ) Row,
EmployeeId
INTO #EmpIds
FROM #Employees
SELECT #Total = COUNT(*) FROM #EmpIds
WHILE ( #Index <= #Total )
BEGIN
SELECT #EmployeeId=EmployeeId FROM #EmpIds WHERE Row = #Index
EXEC [dbo].[QueueInsert]
#SendEmailBatchId = #NewIdBatch
,#ID = #ID OUTPUT
,#ErrorMsg = #ErrorMsg OUTPUT
IF ( #ErrorMsg <> '' )
BEGIN
SET #ReturnVal = 1;
SET #ErrorMsg = 'There was an error trying to insert data into [dbo].[QueueInsert] table';
RAISERROR(#ErrorMsg, 16, 1)
END
SET #Index+=1;
END
COMMIT TRANSACTION;
----------------------------------------------------------
END TRY -- End Main TRY
----------------------------------------------------------
----------------------------------------------------------
BEGIN CATCH -- Start Main CATCH
----------------------------------------------------------
SELECT ERROR_MESSAGE()
IF (XACT_STATE()) = -1
BEGIN
ROLLBACK TRANSACTION;
END;
----------------------------------------------------------
END CATCH -- End Main CATCH
----------------------------------------------------------
ProcedureExit:
RETURN #ReturnVal;
END
I have a stored procedure that is run by the SQL Agent every x minutes. The stored procedure has a while loop that reads each row and does something with them.
I want to handle errors as they occur in the while loop. I need to use Throw in the CATCH block because then SQL Server Agent will not notify if an error occurred.
But the problem is if I use the throw block it breaks out of the while loop but does not process the other records.
How can I use TRY CATCH in a while loop and if an error occurs it should continue with while loop?
This is my code:
WHILE #i<#Count OR #Count IS NULL
BEGIN
SELECT #Id = NULL --Clear variable
SELECT TOP 1
#Id = Id,
#TableNo = [TableNo],
#Action = [Action],
#RecId = [RecId],
#NDB = dbo.GetDB(CId)
FROM
dbo.alot WITH (NOLOCK)
WHERE
CId = #Cid
AND Error = 0
AND (#Table IS NULL OR TableNo = #Table)
AND Tableno <> 50109
ORDER BY
Id
IF #Id IS NOT NULL
BEGIN
SELECT
#SQL = N'EXECUTE #RC = ['+dbo.GetDB(#CId)+'].[dbo].[alot_web] #TableNo, #Action, #RecId, #NaviDB'
BEGIN TRY
IF #RecId = '0-761345-27353-4'
BEGIN
SELECT 1 / 0; -- Generate an error here.
END
EXEC master.dbo.sp_executesql #SQL, N'#TableNo nvarchar(12), #Action tinyint, #RecId nvarchar(36), #NDB varchar(12), #RC int OUTPUT'
, #TableNo, #Action, #RecId, #NaviDB, #Rc OUTPUT
END TRY
BEGIN CATCH
DECLARE #Description VARCHAR(1024);
SELECT #Description = 'WebQueue ID: ' + ISNULL(CAST(#Id AS VARCHAR), '') + ' CompanyID: ' + ISNULL(#Cid, '') + ' Action: ' + ISNULL(CAST(#Action AS VARCHAR), '') + ' RecID: ' + ISNULL(CAST(#RecId AS VARCHAR), '') + ' #RC: ' + ISNULL(CAST(#RC AS VARCHAR), '');
EXEC dbo.LogError #Description;
THROW;
END CATCH
IF #RC = 0 AND ##ERROR = 0
BEGIN
IF EXISTS(SELECT * FROM Queue
WHERE CId = #CId AND [Action] = #Action
AND TableNo = #Tableno AND RecId = #RecID AND Id <> #Id)
BEGIN
DELETE FROM Queue
WHERE CId = #CId AND [Action] = #Action AND TableNo = #Tableno
AND RecId = #RecID
SELECT #Ok += ##ROWCOUNT
END
ELSE BEGIN
DELETE FROM Queue WHERE Id = #Id
SELECT #Ok += ##ROWCOUNT
END
END
ELSE BEGIN
IF EXISTS(SELECT * FROM Queue
WHERE CId = #CId AND [Action] = #Action
AND TableNo = #Tableno AND RecId = #RecID AND Id <> #Id)
BEGIN
UPDATE Queue
SET Error = 1
WHERE CId = #CId AND [Action] = #Action AND TableNo = #Tableno AND RecId = #RecID
SELECT #Failed += ##ROWCOUNT
END
ELSE BEGIN
UPDATE Queue
SET Error = 1
WHERE Id = #Id
SELECT #Failed += ##ROWCOUNT
END
END
END
ELSE
BREAK
SELECT #i += 1
/*IF #i>0 BEGIN--logging >>
INSERT INTO [AdminDB].[dbo].[Replication_Log] ([Date],[CId],[Loops],[DurationSS],[Ok],[Failed])
SELECT Getdate(),#CId,#i,DATEDIFF(ss,#Startdt,getdate()),#Ok,#Failed
END */
END
Replace your THROW with CONTINUE; this way the next record will be processed without Canceling the code.
(EDITED!)
since you log your errors with
EXEC dbo.LogError #Description;
i think you don't need to rethrow the error. Since you stated you don't want the program to end.
example of CONTINUE:
DECLARE #intFlag INT
SET #intFlag = 1
WHILE (#intFlag <=5)
BEGIN
PRINT #intFlag
SET #intFlag = #intFlag + 1
CONTINUE;
IF #intFlag = 4 -- This will never executed
BREAK;
END
GO
source : http://blog.sqlauthority.com/2007/10/24/sql-server-simple-example-of-while-loop-with-continue-and-break-keywords/
EDIT:
for your job agent:
create a new variable at the top of your procedure :
int #ErrorAmount = 0
your catch would need to look like this:
BEGIN CATCH
DECLARE #Description VARCHAR(1024);
SELECT #Description = 'WebQueue ID: ' + ISNULL(CAST(#Id AS VARCHAR), '') + ' CompanyID: ' + ISNULL(#Cid, '') + ' Action: ' + ISNULL(CAST(#Action AS VARCHAR), '') + ' RecID: ' + ISNULL(CAST(#RecId AS VARCHAR), '') + ' #RC: ' + ISNULL(CAST(#RC AS VARCHAR), '');
EXEC dbo.LogError #Description;
SET #ErrorAmount = #ErrorAmount+1; --add this line
CONTINUE; --REPLACE THROW with CONTINUE
END CATCH
at the end of your procedure Add
if #ErrorAmount > 0
BEGIN
THROW 6000,'Your message' , 1;
END
this way your Agent will show you the error and your whole process still did the job.
Your THROW raises an exception which causes the SP to stop processing. Essentially you are catching any errors that occur but rethrowing them which will indeed break out of the while loop.
Remove it and the sp should then just continue as normal.
My suggestion is use one more loop outside the loop you have.
So the TSQL will be something like this, will this helps?
DECLARE #Continue bit,
#i INT,
#Count INT
SELECT #i = 1 ,
#Count =10
SET #Continue =1
While #Continue=1
BEGIN
BEGIN TRY
--YOUR existing while loop
WHILE #i<#Count OR #Count IS NULL
BEGIN
-- The rest of your code
IF #i=3
BEGIN
set #i= #i/0
END
PRINT #i
SET #i=#i+1
--Set to false when complete
IF #i =#Count
SET #Continue =0
END
END TRY
BEGIN CATCH
print ERROR_MESSAGE()
set #i =#i+1
END CATCH
END
Expected result from above
1
2
Divide by zero error encountered.
4
5
6
7
8
9
Not all errors can be caught by TRY/CATCH. In this case, sp_start_job actually calls external procedures, and these are outside the bounds of SQL Server's error handling. Or at least that's the story that they're sticking to:
http://connect.microsoft.com/SQLServer/feedback/details/362112/sp-start-job-error-handling
Please refer this for more details
You could:
Remove the THROW. Your script will continue as normal and errors will be logged
At the end of the script check #Failed and if appropriate use RAISEERROR which will be noted by the SQL Agent
I have a lot of stored procedures. But I am only getting Request Timeout sometimes only for this SP ?
ALTER PROCEDURE [dbo].[Insertorupdatedevicecatalog]
(#OS NVARCHAR(50)
,#UniqueID VARCHAR(500)
,#Longitude FLOAT
,#Latitude FLOAT
,#Culture VARCHAR(10)
,#Other NVARCHAR(200)
,#IPAddress VARCHAR(50)
,#NativeDeviceID VARCHAR(50))
AS
BEGIN
SET NOCOUNT ON;
DECLARE #TranCount INT;
SET #TranCount = ##TRANCOUNT;
DECLARE #OldUniqueID VARCHAR(500) = ''-1'';
SELECT #OldUniqueID = [UniqueID] FROM DeviceCatalog WHERE (#NativeDeviceID != '''' AND [NativeDeviceID] = #NativeDeviceID);
BEGIN TRY
IF #TranCount = 0
BEGIN TRANSACTION
ELSE
SAVE TRANSACTION Insertorupdatedevicecatalog;
DECLARE #Geo GEOGRAPHY = geography::STGeomFromText(''POINT('' + CONVERT(VARCHAR(100), #Longitude) + '' '' + CONVERT(VARCHAR(100), #Latitude) + '')'', 4326);
IF EXISTS(SELECT 1 FROM DeviceCatalog WHERE [UniqueID] = #UniqueID)
BEGIN
DECLARE #OldGeo GEOGRAPHY
,#OldCity NVARCHAR(100)
,#OldCountry NVARCHAR(100)
,#OldAddress NVARCHAR(100);
SELECT #OldGeo = [LastUpdatedLocationFromJob]
,#OldCity = [City]
,#OldCountry = [Country]
,#OldAddress = [Address]
FROM DeviceCatalog
WHERE [UniqueID] = #UniqueID;
UPDATE DeviceCatalog
SET [OS] = #OS
,[Location] = #Geo
,[Culture] = #Culture
,[Other] = #Other
,[IPAddress] = #IPAddress
WHERE [UniqueID] = #UniqueID;
IF (#OldGeo IS NULL OR #OldAddress IS NULL OR #OldCity IS NULL OR #OldCountry IS NULL OR ISNULL(#Geo.STDistance(#OldGeo) / 1000,0) > 50)
BEGIN
UPDATE DeviceCatalog
SET [Lastmodifieddate] = Getdate()
WHERE [UniqueID] = #UniqueID;
END
END
ELSE
BEGIN
INSERT INTO DeviceCatalog
([OS]
,[UniqueID]
,[Location]
,[Culture]
,[Other]
,[IPAddress]
,[NativeDeviceID])
VALUES (#OS
,#UniqueID
,#Geo
,#Culture
,#Other
,#IPAddress
,#NativeDeviceID);
IF(#OldUniqueID != ''-1'' AND #OldUniqueID != #UniqueID)
BEGIN
EXEC DeleteOldAndroidDeviceID #OldUniqueID, #UniqueID;
END
END
LBEXIT:
IF #TranCount = 0
COMMIT;
END TRY
BEGIN CATCH
DECLARE #Error INT, #Message VARCHAR(4000), #XState INT;
SELECT #Error = ERROR_NUMBER() ,#Message = ERROR_MESSAGE() ,#XState = XACT_STATE();
IF #XState = -1
ROLLBACK;
IF #XState = 1 AND #TranCount = 0
rollback
IF #XState = 1 AND #TranCount > 0
ROLLBACK TRANSACTION Insertorupdatedevicecatalog;
RAISERROR (''Insertorupdatedevicecatalog: %d: %s'', 16, 1, #error, #message) ;
END CATCH
END
The timeout occurs due to two updates to same table inside same transaction. You could avoid it with a case statement. Also whole IF ELSE can be replaced with a merge.
MERGE INTO DeviceCatalog DC
USING (SELECT #UniqueID AS UniqueID) T ON (DC.UniqueID = T.UniqueID)
WHEN MATCHED THEN
UPDATE SET [OS] = #OS
,[Location] = #Geo
,[Culture] = #Culture
,[Other] = #Other
,[IPAddress] = #IPAddress
,[Lastmodifieddate] = (CASE
WHEN (LastUpdatedLocationFromJob IS NULL OR [Address] IS NULL OR [City] IS NULL OR [Country] IS NULL OR ISNULL(#Geo.STDistance(LastUpdatedLocationFromJob) / 1000,0) > 50)
THEN Getdate()
ELSE [Lastmodifieddate]
END)
WHEN NOT MATCHED THEN
INSERT INTO DeviceCatalog
([OS]
,[UniqueID]
,[Location]
,[Culture]
,[Other]
,[IPAddress]
,[NativeDeviceID])
VALUES (#OS
,#UniqueID
,#Geo
,#Culture
,#Other
,#IPAddress
,#NativeDeviceID)
WHEN NOT MATCHED BY SOURCE AND #OldUniqueID != ''-1'' AND #OldUniqueID != #UniqueID THEN
DELETE;
Try it and check whether this is what you expected.
Already discussed here
You can achieve it using sp_getapplock in TSQL.
But you need a wrapper storedproc or batch for this. Check the following example it will help you to desing your wrapper sp/batch statement.
Sample Code Snippet
Create table MyTable
(
RowId int identity(1,1),
HitStartedAt datetime,
HitTimestamp datetime,
UserName varchar(100)
)
Go
Create proc LegacyProc (#user varchar(100), #CalledTime datetime)
as
Begin
Insert Into MyTable
Values(#CalledTime, getdate(), #user);
--To wait for 10 sec : not required for your procedures, producing the latency to check the concurrent users action
WAITFOR DELAY '000:00:10'
End
Go
Create Proc MyProc
(
#user varchar(100)
)
as
Begin
Declare #PorcName as NVarchar(1000), #CalledTime datetime
Begin Tran
--To get the Current SP Name, it should be unique for each SP / each batch
SET #PorcName = object_name(##ProcID)
SET #CalledTime = Getdate()
--Lock the Current Proc
Exec sp_getapplock #Resource = #PorcName, #LockMode = 'Exclusive'
--Execute Your Legacy Procedures
Exec LegacyProc #user, #CalledTime
--Release the lock
Exec sp_releaseapplock #Resource = #PorcName
Commit Tran
End
You are doing two seperate updates on the DeviceCatalog table where [UniqueID] = #UniqueID in the same transaction.
I bet your locking/request timeout issue is happening when:
IF (#OldGeo IS NULL OR #OldAddress IS NULL OR #OldCity IS NULL OR #OldCountry IS NULL OR ISNULL(#Geo.STDistance(#OldGeo) / 1000,0) > 50) is true.
Try something like this in place of the two updates.
Obviously test in dev first.
In the else clause, you want to have it insert something if the when is false. Here I am just inserting the current before update field contents.
UPDATE DeviceCatalog
SET [OS] = #OS
,[Location] = #Geo
,[Culture] = #Culture
,[Other] = #Other
,[IPAddress] = #IPAddress
,[Lastmodifieddate] =
case when (
#OldGeo is NULL
OR
#OldAddress is NULL
OR
#OldCity is NULL
OR
#OldCountry is NULL
OR
ISNULL(#Geo.STDistance(#OldGeo) / 1000,0) > 50
) then Getdate()
else [Lastmodifieddate]
end
WHERE [UniqueID] = #UniqueID
Hello Every One The Following Error Comes To Me Suddenly When I Changed Simple And Few Thing On My Stored Procedure Code And It Was Working Great And If I Cleared The Updated Code It Works Fine Again So I Do Not Know What Is The Reason
And Here Is The Error "The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction"
Thanks In Advance
ALTER Procedure [dbo].[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
#English Bit = 0 ,
#ID BigInt = NULL Output ,
#Company_ID SmallInt = NULL ,
#Cust_ID NVarChar (20) = NULL ,
#Cust_Cat NVarChar (10) = NULL ,
#Debit_Limit Decimal (18,3) = NULL ,
#From_Date NVarChar (19) = NULL ,
#To_Date NVarChar (19) = NULL ,
#User_ID NVarChar(50) = NULL
As
BEGIN
Create Table #Errors (ErrorNumber int Not Null, ErrorValue nvarchar(300) Collate Arabic_CI_AS Null);
Begin Tran trn_INV_CustDebLim_setupInsert;
Begin Try
Insert Into INV_Cust_Debit_Limit_setup
( Company_ID , Cust_ID , Cust_Cat , Debit_Limit , From_Date , To_Date )
Values
( #Company_ID , #Cust_ID , #Cust_Cat , #Debit_Limit , #From_Date , #To_Date )
set #ID = ##IDENTITY
-- From Here Is New Part --
declare #str nvarchar(50)
IF(#Cust_ID IS NOT NULL)
set #str = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx ' + #Cust_ID
IF(#Cust_Cat IS NOT NULL)
set #str += 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx ' + #Cust_Cat
set #str += ' xxxxx' +#Debit_Limit + ' xxxx xx' +#From_Date
IF(#English = 1)
BEGIN
IF(#Cust_ID IS NOT NULL)
set #str = 'Credit Limit Added On Customer ' + #Cust_ID
IF(#Cust_Cat IS NOT NULL)
set #str += 'Credit Limit Added On Customer Category ' + #Cust_Cat
set #str = ' With Value ' +#Debit_Limit + ' From Date ' +#From_Date
END
exec usp_GLApplicationAudit #Company_ID , NULL , 10 , #User_ID , #str ,#ID ,10, NULL , NULL ,NULL ,NULL,NULL,'SAl'
-- To Here Is New Part --
End Try
Begin Catch
Insert #Errors Values(1002,ERROR_MESSAGE());
Goto Finished;
End Catch
-- Return Error Records
Finished:
-- retrieve Errors and commit/Rollbak Trans.
If (Select count(E.ErrorNumber)
From #Errors E Left Join GVT_Errors G
On E.ErrorNumber = G.Err_Number
Where G.Err_Type=0 ) > 0
Begin
-- The Following are Errors
Select E.ErrorNumber As [Err_No], G.MessageA + ': ' + E.ErrorValue As [Err_Desc], Err_Source, dbo.fn_GetItemDescription('Err_Type',Cast(Err_Type As nvarchar), null, null, null, null, 0) As Err_Type_Desc, Err_Type, SeverityLevel As [Severity], CategoryID
From #Errors E Left Join GVT_Errors G
On E.ErrorNumber = G.Err_Number;
Rollback Tran trn_INV_CustDebLim_setupInsert;
End
Else
Begin
-- The Following Not Errors They are Warnings or Information
Select E.ErrorNumber As [Err_No], G.MessageA + ': ' + E.ErrorValue As [Err_Desc], Err_Source, dbo.fn_GetItemDescription('Err_Type',Cast(Err_Type As nvarchar), null, null, null, null, 0) As Err_Type_Desc, Err_Type, SeverityLevel As [Severity], CategoryID
From #Errors E Left Join GVT_Errors G
On E.ErrorNumber = G.Err_Number;
Commit Tran trn_INV_CustDebLim_setupInsert;
End
DROP Table #Errors;
END
In the CATCH code you must check the state of XACT_STATE() and act accordingly. For a procedure template that handles transactions and try/catch blocks correctly see Exception Handling and Nested Transactions:
create procedure [usp_my_procedure_name]
as
begin
set nocount on;
declare #trancount int;
set #trancount = ##trancount;
begin try
if #trancount = 0
begin transaction
else
save transaction usp_my_procedure_name;
-- Do the actual work here
lbexit:
if #trancount = 0
commit;
end try
begin catch
declare #error int, #message varchar(4000), #xstate int;
select #error = ERROR_NUMBER(), #message = ERROR_MESSAGE(), #xstate = XACT_STATE();
if #xstate = -1
rollback;
if #xstate = 1 and #trancount = 0
rollback
if #xstate = 1 and #trancount > 0
rollback transaction usp_my_procedure_name;
raiserror ('usp_my_procedure_name: %d: %s', 16, 1, #error, #message) ;
end catch
end