Home > Software design >  Discounting calculation bundled/unbundled products SQL Oracle
Discounting calculation bundled/unbundled products SQL Oracle

Time:08-05

I have below table:

enter image description here

My dataset tracks customer orders. Customers can buy a bundle, and are then charged one bundle discounted_price, or can buy individual products and are charged a discounted_price per product.

Customer 1 bought bundle A, for a discounted_price of 200. The different products within the bundle are also listed with its original list_price. I want to calculate the discount of the whole bundle, i.e. (180 20 70)/200 = 0.74. Customer 2 did not buy a bundle. In this case I want to calculate the discount per individual product, i.e. 15/20 = 0.75

So the result should look like:

enter image description here

In SQL Oracle, I tried below query, but it is not giving me the accurate discount_% when a product is a bundle

    select
customer,
product_number,
bundle,
list_price,
discounted_price,

case when sum(list_price) <> 0 then round(sum(discounted_price) / sum(list_price,2))*100 
else round(discounted_price / (list_price,2)*100  end as discount_%

from customer_orders

group by customer

CodePudding user response:

Prices in the question tables are not all the same (???) I took the last ones. Here is the code and result:

WITH
    orders AS
        (
            Select 1 "CUST_ID", 'A' "PROD_ID", 'Y' "BUNDLE",    0 "LIST_PRICE", 200 "DISCOUNTED_PRICE" From Dual Union All
            Select 1 "CUST_ID", 'B' "PROD_ID", 'N' "BUNDLE",  180 "LIST_PRICE",   0 "DISCOUNTED_PRICE" From Dual Union All
            Select 1 "CUST_ID", 'C' "PROD_ID", 'N' "BUNDLE",   20 "LIST_PRICE",   0 "DISCOUNTED_PRICE" From Dual Union All
            Select 1 "CUST_ID", 'D' "PROD_ID", 'N' "BUNDLE",   70 "LIST_PRICE",   0 "DISCOUNTED_PRICE" From Dual Union All
            Select 2 "CUST_ID", 'C' "PROD_ID", 'N' "BUNDLE",   20 "LIST_PRICE",  15 "DISCOUNTED_PRICE" From Dual Union All
            Select 2 "CUST_ID", 'B' "PROD_ID", 'N' "BUNDLE",  180 "LIST_PRICE", 110 "DISCOUNTED_PRICE" From Dual Union All
            Select 2 "CUST_ID", 'E' "PROD_ID", 'N' "BUNDLE",  500 "LIST_PRICE", 400 "DISCOUNTED_PRICE" From Dual 
        ),
    bundles AS
        ( Select CUST_ID, DISCOUNTED_PRICE From orders Where BUNDLE ='Y'  )
Select
    o.CUST_ID "CUST_ID",
    o.PROD_ID "PROD_ID",
    o.BUNDLE "BUNDLE",
    o.LIST_PRICE "LIST_PRICE",
    o.DISCOUNTED_PRICE "DISCOUNTED_PRICE",
    --
    CASE 
      WHEN b.CUST_ID Is Not Null 
      THEN  Round(b.DISCOUNTED_PRICE /    
              Sum(CASE WHEN o.BUNDLE = 'N' And o.LIST_PRICE > 0 And o.DISCOUNTED_PRICE = 0 THEN o.LIST_PRICE ELSE 0 END)
              OVER(PARTITION BY o.CUST_ID ORDER BY o.CUST_ID), 2) 
    ELSE    Round(CASE WHEN o.BUNDLE = 'N' And o.LIST_PRICE > 0 And o.DISCOUNTED_PRICE > 0 THEN o.DISCOUNTED_PRICE ELSE 0 END /
            CASE WHEN o.BUNDLE = 'N' And o.LIST_PRICE > 0 And o.DISCOUNTED_PRICE > 0 THEN o.LIST_PRICE ELSE 0 END, 2) 
    END "DISCOUNT_RATIO"
From
    orders o
Left Join
    bundles b ON (b.CUST_ID = o.CUST_ID)
Order By
    o.CUST_ID,
    o.PROD_ID
--
--  R e s u l t
--
--     CUST_ID PROD_ID BUNDLE LIST_PRICE DISCOUNTED_PRICE DISCOUNT_RATIO
--  ---------- ------- ------ ---------- ---------------- --------------
--           1 A       Y               0              200            .74 
--           1 B       N             180                0            .74 
--           1 C       N              20                0            .74 
--           1 D       N              70                0            .74 
--           2 B       N             180              110            .61 
--           2 C       N              20               15            .75 
--           2 E       N             500              400             .8

Addition
Suppose that Customer 2 has both bundled and individual orders then it can be handled like below:

WITH
    orders AS
        (
            Select 1 "CUST_ID", 'A' "PROD_ID", 'Y' "BUNDLE",    0 "LIST_PRICE", 200 "DISCOUNTED_PRICE" From Dual Union All
            Select 1 "CUST_ID", 'B' "PROD_ID", 'N' "BUNDLE",  180 "LIST_PRICE",   0 "DISCOUNTED_PRICE" From Dual Union All
            Select 1 "CUST_ID", 'C' "PROD_ID", 'N' "BUNDLE",   20 "LIST_PRICE",   0 "DISCOUNTED_PRICE" From Dual Union All
            Select 1 "CUST_ID", 'D' "PROD_ID", 'N' "BUNDLE",   70 "LIST_PRICE",   0 "DISCOUNTED_PRICE" From Dual Union All
            Select 2 "CUST_ID", 'C' "PROD_ID", 'N' "BUNDLE",   20 "LIST_PRICE",  15 "DISCOUNTED_PRICE" From Dual Union All
            Select 2 "CUST_ID", 'B' "PROD_ID", 'N' "BUNDLE",  180 "LIST_PRICE", 110 "DISCOUNTED_PRICE" From Dual Union All
            Select 2 "CUST_ID", 'E' "PROD_ID", 'N' "BUNDLE",  500 "LIST_PRICE", 400 "DISCOUNTED_PRICE" From Dual Union All
            Select 2 "CUST_ID", 'A' "PROD_ID", 'Y' "BUNDLE",    0 "LIST_PRICE", 154 "DISCOUNTED_PRICE" From Dual Union All
            Select 2 "CUST_ID", 'B' "PROD_ID", 'N' "BUNDLE",  180 "LIST_PRICE",   0 "DISCOUNTED_PRICE" From Dual Union All
            Select 2 "CUST_ID", 'C' "PROD_ID", 'N' "BUNDLE",   20 "LIST_PRICE",   0 "DISCOUNTED_PRICE" From Dual 
        ),
    bundles AS
        ( Select CUST_ID, PROD_ID, DISCOUNTED_PRICE From orders Where BUNDLE ='Y'  )
Select
    o.CUST_ID "CUST_ID",
    o.PROD_ID "PROD_ID",
    o.BUNDLE "BUNDLE",
    o.LIST_PRICE "LIST_PRICE",
    o.DISCOUNTED_PRICE "DISCOUNTED_PRICE",
    CASE WHEN b.CUST_ID Is Not Null and b.PROD_ID Is Not Null THEN 'Bundled' ELSE 'Individual' END "ORDER_TYPE",
    --
    CASE 
      WHEN b.CUST_ID Is Not Null and b.PROD_ID Is Not Null
      THEN  Round(b.DISCOUNTED_PRICE /    
              Sum(CASE WHEN o.BUNDLE = 'N' And o.LIST_PRICE > 0 And o.DISCOUNTED_PRICE = 0 THEN o.LIST_PRICE ELSE 0 END)
              OVER(PARTITION BY o.CUST_ID ORDER BY o.CUST_ID), 2) 
    ELSE    Round(CASE WHEN o.BUNDLE = 'N' And o.LIST_PRICE > 0 And o.DISCOUNTED_PRICE > 0 THEN o.DISCOUNTED_PRICE ELSE 0 END /
            CASE WHEN o.BUNDLE = 'N' And o.LIST_PRICE > 0 And o.DISCOUNTED_PRICE > 0 THEN o.LIST_PRICE ELSE 0 END, 2) 
    END "DISCOUNT_RATIO"
From
    orders o
Left Join
    bundles b ON (b.CUST_ID = o.CUST_ID And (b.PROD_ID = o.PROD_ID OR o.DISCOUNTED_PRICE = 0))
Order By
    o.CUST_ID,
    CASE WHEN b.CUST_ID Is Not Null and b.PROD_ID Is Not Null THEN 'Bundled' ELSE 'Not Bundled' END,
    o.PROD_ID
--
--  R e s u l t
--
--     CUST_ID PROD_ID BUNDLE LIST_PRICE DISCOUNTED_PRICE ORDER_TYPE DISCOUNT_RATIO
--  ---------- ------- ------ ---------- ---------------- ---------- --------------
--           1 A       Y               0              200 Bundled               .74 
--           1 B       N             180                0 Bundled               .74 
--           1 C       N              20                0 Bundled               .74 
--           1 D       N              70                0 Bundled               .74 
--           2 A       Y               0              154 Bundled               .77 
--           2 B       N             180                0 Bundled               .77 
--           2 C       N              20                0 Bundled               .77 
--           2 B       N             180              110 Individual            .61 
--           2 C       N              20               15 Individual            .75 
--           2 E       N             500              400 Individual             .8

Notice that by adjusting the bundles CTE, Left Join's ON clause and CASE expressions' conditions you can anticipate and handle even more complicated structures. This works when Bundle Product ID is lets say X and PROD_IDs within bundle are 'Y' and 'Z' or whatever. Regards...

CodePudding user response:

Try the following:

select customer, product_number, bundle, list_price, discounted_price, 
       case 
         when (list_price = 0 or discounted_price = 0) then
           round(max(discounted_price) over (partition by customer) / sum(list_price) over (partition by customer), 2) * 100
         when (list_price > 0 and discounted_price > 0) then
          round(discounted_price / list_price, 2) * 100
       end as Discount_Percentage
from customer_orders;

To avoid division by zero in case that some customers have both a list_price and discounted_price equal to 0, you may use coalesce and nullif functions as the following:

select customer, product_number, bundle, list_price, discounted_price, 
       case 
         when (list_price = 0 and discounted_price > 0) or (discounted_price = 0 and list_price > 0) then
           round(
             coalesce(
                max(discounted_price) over (partition by customer) / nullif(sum(list_price) over (partition by customer), 0)
                   , 0)
              , 2) *100
         when (list_price > 0 and discounted_price > 0) then
          round(discounted_price / list_price, 2) * 100
         when (list_price = 0 and discounted_price = 0) then 0
       end as Discount_Percentage
from customer_orders;

See a demo from db<>fiddle.

If a customer can buy a bundle and another product out of the bundle, i.e. product A and product E, then try the following:

select customer, product_number, bundle, list_price, discounted_price, 
       case 
         when (list_price = 0 or discounted_price = 0) then
           round(max(case when bundle='Y' then discounted_price else 0 end) over (partition by customer) / 
           sum(case when list_price > 0 and discounted_price = 0 then list_price else 0 end) over (partition by customer), 2) * 100
         when (list_price > 0 and discounted_price > 0) then
          round(discounted_price / list_price, 2) * 100
       end as Discount_Percentage
from customer_orders
order by customer, product_number;
  • Related