以递归方式向SQL Server

时间:2017-03-30 15:02:16

标签: sql sql-server cursor balance

我在一张表中都有收费和付款。付款被“应用”到收费,以便将帐户余额归零。只要所有费用均衡为零(或大部分费用),就没有具体的订单申请。

在我尝试构建SP之后,该表将有两种可能的状态。正如我所说,只要大部分收费余额为零,我都不在乎它们是如何被“应用”的。

CREATE TABLE dbo.Transactions (
TrxID       INT IDENTITY,
TrxType     BIT, -- 1 for Charges, 0 for Payments
TrxDescription  VARCHAR(MAX),
Amount  DECIMAL(13,2),
ApplyTo     INT -- TrxID of the charge to which the payment is "applied"
);

INSERT INTO dbo.Transactions VALUES(1,'Charge1',100,NULL);
INSERT INTO dbo.Transactions VALUES(0,'Payment1',-80,NULL);
INSERT INTO dbo.Transactions VALUES(0,'Payment2',-15,NULL);
INSERT INTO dbo.Transactions VALUES(1,'Charge3',200,NULL);
INSERT INTO dbo.Transactions VALUES(0,'Payment4',-20,NULL);
INSERT INTO dbo.Transactions VALUES(0,'Payment5',-80,NULL);
INSERT INTO dbo.Transactions VALUES(0,'Payment6',-105,NULL);

SELECT * FROM dbo.transactions

SELECT SUM(Amount) FROM dbo.Transactions;
SELECT SUM(Amount) FROM dbo.Transactions WHERE TrxType=1;
SELECT SUM(Amount) FROM dbo.Transactions WHERE TrxType=0;

-- CORRECT APPLICATION
UPDATE dbo.Transactions SET ApplyTo=1 WHERE TrxID IN(2,5)
UPDATE dbo.Transactions SET ApplyTo=4 WHERE TrxID IN(3,6,7)

-- The global balance is zero
SELECT SUM(Amount) FROM dbo.Transactions

-- Both charges have zero balance
SELECT t.*,t.Amount+b.Balance 'Balance'
FROM dbo.Transactions t
OUTER APPLY (SELECT SUM(t2.Amount)Balance
         FROM dbo.Transactions t2
         WHERE t.TrxID=t2.ApplyTo
)b

-- WRONG APPLICATION
UPDATE dbo.Transactions SET ApplyTo=1 WHERE TrxID IN(2,3,5)
UPDATE dbo.Transactions SET ApplyTo=4 WHERE TrxID IN(6,7)

-- The global balance is zero
SELECT SUM(Amount) FROM dbo.Transactions

-- Charges dont have correct balance, as they could be both zero if applied correctly
SELECT t.*,t.Amount+b.Balance 'Balance'
FROM dbo.Transactions t
OUTER APPLY (SELECT SUM(t2.Amount)Balance
         FROM dbo.Transactions t2
         WHERE t.TrxID=t2.ApplyTo
)b

感谢。

1 个答案:

答案 0 :(得分:0)

您要解决的问题是subset sum problem。这是一个NP完全问题,即使最好的算法也是以指数时间O(2 N / 2 )运行。

鉴于您的问题看起来像资产负债表,可能涉及数千笔付款和费用(子集和总和),但没有一个确切的解决方案可以在合理的时间内运行。

以下解决方案将尽最大努力使用MySQL存储过程来应用付款:

BEGIN


    DECLARE charge int;
    declare charge_id int;
    DECLARE payment int;
    declare payment_id int;
    declare foundrows int;

    balance_loop: LOOP

       SELECT trxid, amount into charge_id, charge 
        from transactions 
        where trxtype = 1
        and applyto is null
        limit 1;

        SET foundrows = (SELECT FOUND_ROWS());

        if foundrows = 0 then
            call debug(concat('Finished '));
            leave balance_loop;
        end if;

        call debug(concat('Balancing charge ', charge_id));  
        call debug(concat('starting with charge amount: ', charge));

        payment_loop: while charge > 0 do
            call debug(concat('start charge: ', charge));

            SELECT trxid, ABS(amount) into payment_id, payment
            FROM transactions
            WHERE trxtype =  0
            AND applyto is null
            AND ABS(amount) <= charge
            order by RAND()
            limit 1;


            call debug(concat('applying payment: ', payment));
            set charge = charge - payment;      

            call debug(concat('remaining charge: ', charge));


            if charge >= 0 then
                update transactions 
                set applyto = charge_id
                where trxid = payment_id;               
                call debug(concat('applied payment_id', payment_id, ' to charge ID ', charge_id));
            end if;     

        end while;

        update transactions set applyto = 0 where trxid = charge_id;

    end loop;

END