【问题标题】:MS SQL How to avoid loops when allocating amountsMS SQL 如何在分配金额时避免循环
【发布时间】:2019-01-29 13:51:24
【问题描述】:

我正在构建一个需要自动将金额分配到Charge_Detail 表的系统。

我有以下表格:

-- a list of charges that are outstanding
DECLARE Charge_Master TABLE
(   ID             INT NOT NULL,           
    CompanyID      VARCHAR(6) NOT NULL, 
    EntryDate      SMALLDATETIME, 
    Ref            VARCHAR(30) NOT NULL,
    Amount         DECIMAL(12,2) NOT NULL
)   

-- directly linked to master table to represent charges paid in detail
DECLARE Charge_Detail TABLE
(   ID                INT NOT NULL,           
    Charge_MasterID   INT NOT NULL,            --Foreign Key 
    EntryDate         SMALLDATETIME,
    Ref               VARCHAR(30) NOT NULL,
    Amount            DECIMAL(12,2) NOT NULL

)     

INSERT Charge_Master
VALUES  ('ABC123', '01/01/2018', 'INV-111', 25),                 
        ('ABC123', '21/03/2018', 'INV-222', 30),                  
        ('ABC123', '11/05/2018', 'INV-333', 15)                   

目标是让查询采用以下参数:

  • CompanyId 例如'ABC123'
  • Amount 例如45
  • Ref 例如'REF-142'

它应该根据参数计算出需要INSERTED 的记录到Charge_Detail 表和关联的Charge_MasterID 中。

Example:

If the total amount to allocate is 45 against CompanyId: ABC123
then this is the Expected output inserted into the Charge_Detail table.

/* Charge_Master Table
 *
 *  ID     CompanyID    EntryDate      Ref      Amount
 *  1        ABC123     01/01/2018   INV-111      25
 *  2        ABC123     21/03/2018   INV-222      30
 *  3        ABC123     11/05/2018   INV-333      15
 */

/* Charge_Detail Table
 *
 *  ID  Charge_MasterID     EntryDate      Ref      Amount
 *  1         1             12/08/2018   REF-142      25
 *  2         2             12/08/2018   REF-142      20  -- cannot fully allocate therefore 10 still remaining to be allocated for next time
 */

最好的方法是什么? CTE 会有帮助吗?我对它们不太熟悉,但我尝试了带有子查询和 case 语句的 SELECT 查询,但我无法在不使用循环的情况下减少剩余数量。

任何建议将不胜感激!

【问题讨论】:

  • 最后一个例子中的 20 来自哪里? charge_master 中的匹配行数量为 30。
  • 为什么金额是25+20而不是15+15+15????有没有只合并 2 行的规则?如果还有不止一种选择呢?
  • 我使用的是示例参数,所以要分配的总量是 45。因此在细节中,第一行要分配的是 25,第二行只能分配主表中 30 中的 20。
  • 在我看来,Charge_detail 的数据是手动提供的,所以不清楚你想要什么。你能举一个主数据的例子,手动给出详细数据和预期输出吗?
  • 我添加了更多信息以使其更清晰。最后一部分显示了Master 的示例,根据给定的参数,它应该尽可能多地分配Detail

标签: sql sql-server tsql sql-server-2012


【解决方案1】:

应该是这样的:

DECLARE @CompanyID VARCHAR(6) = 'ABC123', 
    @Ref VARCHAR(30) = 'REF-142',
    @Amount DECIMAL(12,2) = 45

;WITH cte as (
    SELECT *, SUM (Amount) OVER (ORDER BY Id) AS RunningAmount
    FROM Charge_Master
    ), Limits as (
    SELECT TopRow = (SELECT MIN(ID) FROM CTE WHERE RunningAmount > @Amount),
        LastRow = (SELECT MAX(ID) FROM CTE WHERE RunningAmount < @Amount))
SELECT c.ID, c.EntryDate, @Ref, c.Amount
FROM Limits as l INNER JOIN cte as c ON c.ID < l.TopRow
UNION ALL
SELECT c2.ID, c2.EntryDate, @Ref, @Amount-c1.RunningAmount
FROM Limits as l 
INNER JOIN cte as c1 ON c1.ID = l.LastRow
INNER JOIN cte as c2 ON c2.ID = l.TopRow
ORDER BY 1

【讨论】:

  • 作为该事务的第二步,您必须从 Charge_Master 中删除行,如果不为零,则从最后一个中减去。你必须弄清楚如何去做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-01
  • 2011-05-01
  • 2012-02-15
  • 2011-03-14
  • 1970-01-01
  • 1970-01-01
  • 2013-09-22
相关资源
最近更新 更多