将数字分为多个波段

时间:2020-09-30 04:50:31

标签: sql postgresql analytics

我一直在处理将指标分为几个范围的问题。为了给您一些背景信息,让我们以这个示例为例,其中每个客户有一定数量的订单。现在,客户可以订购n个产品。让我们根据订单数量为客户提供一定的折扣。折扣基于分层模型提供。为了简化起见,我将省略多个产品类别。这是表格的一些示例。

订单表

Customer    |   order_no
----------------------------
Customer1   |   400
Customer2   |   1200
Customer3   |   40
Customer4   |   2000
Customer5   |   700

分层定价表

Tier   | lower_th | higer_th | price |
--------------------------------------
Tier1  | 0        | 250      | 50    |
TIer2  | 251      | 500      | 45    |
Tier3  | 501      | 1000     | 40    |
TIer4  | 1001     | 10000    |  30   |

示例1 :我希望能够向Customer1收取250笔订单的50美元费用,以及其余150种产品(共400笔)的45美元费用。

示例2:我希望能够向Customer5收取250个订单的50美元费用,另外250个订单的45美元费用,其余700个产品中的其余200个产品的费用为40美元。

如何在PostgreSQL中实现这一目标?对于Customer1,我的输出需要为以下内容。拆分订单总数并将其加入定价层以获取相应金额的最佳方法是什么?

Customer  | order_no | charges |
--------------------------------
Customer1 | 250      | 50      |
Customer1 | 150      | 45      |

2 个答案:

答案 0 :(得分:1)

您可以将层视为间隔。

两个时间间隔[a1, b1][a2, b2]相交

a1 <= b2 AND b1 >= a2

订单数是另一个始终从1开始的时间间隔。

您的两个时间间隔是:等级[lower_th, higer_th]和订单[1, order_no]

查询是使用此交集表达式的简单联接:

SELECT *
    ,CASE WHEN O.order_no > T.higer_th
    THEN T.higer_th - T.lower_th + 1 -- full tier
    ELSE O.order_no - T.lower_th + 1
    END AS SplitOrderNumbers
FROM
    Orders AS O
    INNER JOIN Tiers AS T
        -- ON 1 <= T.higer_th AND O.order_no >= T.lower_th
        ON O.order_no >= T.lower_th
ORDER BY
    O.Customer
    ,T.lower_th
;

您真的不需要1 <= T.higer_th部分,因为它始终是真实的,因此表达式变得简单O.order_no >= T.lower_th

此外,通常最好将间隔存储为[closed; open)。它通常可以简化算术运算,这与大多数编程语言的数组索引从0开始而不是从1开始的原因类似。您的间隔似乎为[closed; closed]。在这种情况下,您需要将lower_th设置为1,而不是0,并在计算中使用+1

通过调整样本数据,此查询将产生以下结果:

+-----------+----------+-------+----------+----------+-------+-------------------+
| Customer  | order_no | Tier  | lower_th | higer_th | price | SplitOrderNumbers |
+-----------+----------+-------+----------+----------+-------+-------------------+
| Customer1 |      400 | Tier1 |        1 |      250 | 50.00 |               250 |
| Customer1 |      400 | Tier2 |      251 |      500 | 45.00 |               150 |
| Customer2 |     1200 | Tier1 |        1 |      250 | 50.00 |               250 |
| Customer2 |     1200 | Tier2 |      251 |      500 | 45.00 |               250 |
| Customer2 |     1200 | Tier3 |      501 |     1000 | 40.00 |               500 |
| Customer2 |     1200 | Tier4 |     1001 |    10000 | 30.00 |               200 |
| Customer3 |       40 | Tier1 |        1 |      250 | 50.00 |                40 |
| Customer4 |     2000 | Tier1 |        1 |      250 | 50.00 |               250 |
| Customer4 |     2000 | Tier2 |      251 |      500 | 45.00 |               250 |
| Customer4 |     2000 | Tier3 |      501 |     1000 | 40.00 |               500 |
| Customer4 |     2000 | Tier4 |     1001 |    10000 | 30.00 |              1000 |
| Customer5 |      700 | Tier1 |        1 |      250 | 50.00 |               250 |
| Customer5 |      700 | Tier2 |      251 |      500 | 45.00 |               250 |
| Customer5 |      700 | Tier3 |      501 |     1000 | 40.00 |               200 |
+-----------+----------+-------+----------+----------+-------+-------------------+

答案 1 :(得分:1)

对于定价数据,我将使用这样的表格来简化数据维护

create table pricing_data
(
   high_limit int, 
   price numeric
);

视图将使用窗口功能为您提供所需的时间间隔:

create view pricing as
  select coalesce(lag(high_limit) over (order by high_limit), 0) as last_limit,
         high_limit, price
    from pricing_data;

这简化了进入定价层的突破:

select o.customer, 
       least(o.order_no - p.last_limit, p.high_limit - p.last_limit) as order_no,
       p.price as charges
  from orders o
  join pricing p on p.last_limit < o.order_no
 order by o.customer, p.price desc
;

结果:

customer    order_no    charges
Customer1    250            50
Customer1    150            45
Customer2    250            50
Customer2    250            45
Customer2    500            40
Customer2    200            30
Customer3     40            50
Customer4    250            50
Customer4    250            45
Customer4    500            40
Customer4   1000            30
Customer5    250            50
Customer5    250            45
Customer5    200            40
14 rows

Fiddle here.