运行此查询后,出现一个自定义错误:
借记帐户余额不能少于0。somemail@7dmail.com/123/xxx/123456
以及两个常规错误:
EXECUTE之后的事务计数表明BEGIN和COMMIT语句的数量不匹配。上一个计数= 2,当前计数= 0。 ROLLBACK TRANSACTION请求没有相应的BEGIN TRANSACTION。
我认为,发生事务错误是因为在事务提交之前某些东西引发异常。 自定义错误来自下面编写的另一个查询(AddJournalEntry)。我看不到这两个查询之间的连接。
查询:
-- =============================================
-- 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
我得到的自定义错误是在名为的查询中定义的 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
答案 0 :(得分:1)
要执行多个ROLLBACK
命令,而只执行一次。回滚会将事务计数从大于0的任何金额直接降低到0,因此如果执行3 BEGIN TRANSACTION
,则@@TRANCOUNT
为3,回滚会将其设置为0。问题是您在被调用的SP(嵌套的SP)内部执行回滚,并在SP返回之后再次执行回滚。
您可以在此示例中看到问题:
BEGIN TRANSACTION
SELECT @@TRANCOUNT -- 1
BEGIN TRANSACTION
SELECT @@TRANCOUNT -- 2
ROLLBACK
SELECT @@TRANCOUNT -- 0
ROLLBACK -- The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
这是您的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
SP被称为:
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
我建议使用TRY / CATCH块并在ROLLBACK
上进行CATCH
。如下所示:
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
有关SQL Server错误处理的详细说明,请选中this post。