【问题标题】:Apply payments to charges recursively SQL Server以递归方式将付款应用于费用 SQL Server
【发布时间】:2017-03-30 15:02:16
【问题描述】:

我在一个表中同时包含费用和付款。付款被“应用”到费用中,以使账户余额为零。没有具体的应用顺序,只要所有费用都平衡为零(或大部分)。

在我尝试构建的 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

谢谢。

【问题讨论】:

  • 您应该在第一个插入语句中收到“提供的列数小于表中的列数...”的错误
  • 对收费的优化是一个奇怪的要求。在正常的会计情况下,第 2 行和第 3 行以及第 5 行中的 5 美元将应用于第一次收费。第 5 行的其余部分 + 其余部分将适用于第二次收费。否则,您最终可能会遇到无法均匀支付款项的情况。例如,如果您总共有 20 笔付款,每笔 15 美元,您将如何应用它们?
  • 您所寻找的似乎类似于打包算法,这是一类单独的问题,具有一些复杂的理论问题。不过,它被简化为它只是一个一维打包问题。你可能想看看Gert-Jan's SQL Server Bin Packing solutions
  • @Anand 第一列是标识,所以它会自动插入。
  • @Anand - 我很惊讶,但我测试了代码,它在 SQL 2012 中工作。我一定错过了这个变化。

标签: sql sql-server cursor balance


【解决方案1】:

您要解决的是subset sum problem。这是一个 NP 完全问题,即使是最好的算法也在指数时间 O(2N/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

【讨论】:

    猜你喜欢
    • 2020-12-06
    • 2018-06-23
    • 2014-06-11
    • 1970-01-01
    • 1970-01-01
    • 2013-05-03
    • 2012-05-01
    • 2017-11-05
    • 2017-06-12
    相关资源
    最近更新 更多