如何 select 汇总折扣百分比和固定金额?

How to select aggregate discount percentages and flat amount?

我有一个场景,我需要汇总 n 个折扣以获得总折扣。每次折扣必须扣除之前的折扣。

例如: 我有一个 200 卢比的订单。 (金额总和)我有多张代金券。第一个让我享受 15% 的折扣,200-(200*(15/100)) = 170。 然后我们有第二张价值 10 卢比的代金券,170-(10) = 160。

顺序很重要,因此还有一个字段记录应用折扣的顺序。

下面是table:

  1. 订单
id  order_id  productId  amount
 1    1      5          160
 2    1      9          40

所以没有折扣的总金额是:200 卢比

  1. 折扣
id   order_id    seq   type          amt  
 1       1        1    Per (%)     15
 2       1        3    Flat        10

因此,折扣金额为:((200*(15/100))) 30 + 10 = 40 .

所以我尝试用 CTE 编写 SQL 查询,但它没有给出预期的输出:

    WITH recursive cte_calctotalamount AS
(
         SELECT   order_id,
                  sum(amount) AS totalamount
         FROM     ORDER
         WHERE    order_id=1
         GROUP BY order_id ), 
cte_totaldiscountamount AS
(
           SELECT     i.order_id,
                      i.seq,
                      i.amt,
                      ta.totalamount AS totalamount,
                      CASE
                          WHEN i.type='Flat' THEN i.amt
                          WHEN i.type='Per' THEN (ta.totalamount * (i.amt/100))
                      END totaldiscountedamount,
                      (totalamount- (
                      CASE
                          WHEN i.type='Flat' THEN i.amt
                          WHEN i.type='Per' THEN (ta.totalamount * (i.amt/100))
                      END) ) amountafterdiscount
           FROM       discount i
           INNER JOIN cte_calctotalamount ta
           ON         ta.order_id=i.order_id

           UNION

           SELECT     d.order_id,
                      d.seq,
                      d.amt,
                      ad.totalamount,
                      CASE
                          WHEN d.type='Flat' THEN d.amt
                          WHEN d.type='Per' THEN (ad.amountafterdiscount - (d.amt/100))
                      END totaldiscountedamount,
                      (amountafterdiscount - (
                      CASE
                          WHEN d.type='Flat' THEN d.amt
                          WHEN d.type='Per' THEN (ad.amountafterdiscount - (d.amt/100))
                      END) ) amountafterdiscount
           FROM       discount d
           INNER JOIN cte_totaldiscountamount ad
           ON         d.order_id=ad.order_id
           AND        d.seq=ad.seq+1 )
SELECT *
FROM   cte_totaldiscountamount;

请帮助实现以下输出,

order_id   totalAmount totalDiscountedAmount amountAfterDiscount 
   1          200            40                  160

您需要在查询中修改 4 项内容

  1. 在递归查询中,需要初始化第一个结果集。它是下一次迭代的计算基础。在这种情况下,您需要在递归部分的第一个查询中添加 where i.seq = 1(我们从初始折扣开始)。
  2. 其次,您没有递归地添加折扣金额。为此,您需要从之前的迭代中检索行折扣金额。所以不是:
    case
           when d.type='Flat' THEN d.amt
           WHEN d.type='Per' THEN (ad.AmountAfterDiscount - (d.amt/100))
         END totalDiscountedAmount

你应该写:

totalDiscountedAmount + case 
       when d.type='Flat' THEN d.amt
       WHEN d.type='Per' THEN (ad.AmountAfterDiscount - (d.amt/100))
     END totalDiscountedAmount
  1. 您需要在折扣中添加一个新的行增量器。当Union之后的第二次查询的returned结果集为null时,递归查询结束。由于条件 d.seq=ad.seq+1 将为假,查询将 return 为空。这是因为在 discounts table 中,您的下一个序列是 3 而不是 2。在建议的解决方案中,您可以看到它在折扣的 CTE 中被 returned使用 ROW_NUMBER()

  2. 最后,您只需要保留最后一行(因为如果 N 是特定订单的折扣数量,递归查询将 return 自然地 N 行。您可以只需将最后一个输出与示例中所示的子查询连接起来即可。

您的最终查询如下所示:

        WITH RECURSIVE CTE_CalcTotalAmount
AS
(
   select order_id,sum(amount) As totalAmount from "order" 
    where order_id=1
    group by order_id
),
CTE_DiscountsPerOrder as (
    select order_id, seq, amt, type, row_number() over (partition by order_id order by seq asc ) as new_seq from discount  ) ,
CTE_TotalDiscountAmount AS
(
  select i.order_id,i.new_seq,i.amt,ta.totalAmount as TotalAmount,
    case 
       when i.type='Flat' THEN i.amt
       WHEN i.type='Per' THEN (ta.totalAmount * (i.amt/100))
     END totalDiscountedAmount,
     (totalAmount-
       (case 
           when i.type='Flat' THEN i.amt
           WHEN i.type='Per' THEN (ta.totalAmount * (i.amt/100))
       END)
     ) AmountAfterDiscount
    from CTE_DiscountsPerOrder i
    inner JOIN CTE_CalcTotalAmount ta ON ta.order_id=i.order_id
    where i.new_seq=1
  UNION
    select d.order_id,d.new_seq,d.amt,ad.totalAmount,
     totalDiscountedAmount+ case 
       when d.type='Flat' THEN d.amt
       WHEN d.type='Per' THEN (ad.AmountAfterDiscount - (d.amt/100))
     END totalDiscountedAmount,
     (AmountAfterDiscount -
        (case 
       when d.type='Flat' THEN d.amt
       WHEN d.type='Per' THEN (ad.AmountAfterDiscount - (d.amt/100))
     END)
     ) amountAfterDiscount
    From CTE_DiscountsPerOrder d
    inner JOIN CTE_TotalDiscountAmount ad on d.order_id=ad.order_id
    AND d.new_seq=ad.new_seq+1
)
select * from CTE_TotalDiscountAmount a
join (select order_id, count(*) as totalDiscounts from CTE_DiscountsPerOrder group by 1) b on b.order_id = a.order_id  and b.totalDiscounts = a.new_seq;