简体   繁体   English

如何找出查询的哪一部分导致错误?

[英]How to figure out which part of query is causing error?

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借方账户余额不能小于0。somemail@7dmail.com/123/xxx/123456

And Two regular errors:和两个常规错误:

Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. EXECUTE 之后的事务计数表明 BEGIN 和 COMMIT 语句的数量不匹配。 Previous count = 2, current count = 0. The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.先前计数 = 2,当前计数 = 0。ROLLBACK TRANSACTION 请求没有对应的 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.自定义错误来自下面写的另一个查询(AddJournalEntry)。 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我得到的自定义错误是在名为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.当您应该只执行一次时,您正在执行多个ROLLBACK命令。 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.回滚会将事务计数从高于 0 的任何数量直接降低到 0,因此如果执行 3 BEGIN TRANSACTION ,您的@@TRANCOUNT为 3,回滚会将其设置为 0。问题是您正在执行回滚内部被调用的 SP(嵌套的)并在 SP 返回后再次调用。

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:这是来自您的 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: 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

I'd recommend using TRY/CATCH blocks and doing the ROLLBACK on the CATCH .我建议使用 TRY/CATCH 块并在CATCH上执行ROLLBACK 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 .有关 SQL 服务器错误处理的详细说明,请查看此帖子

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM