将一个数字分成多个波段
Divide a number into multiple bands
我一直在处理将一个指标拆分成几个波段的问题。为了给您一些背景信息,让我们以这个例子为例,每个客户都有一定数量的订单。现在,一个客户可以订购 n 种产品。让我们根据订单数量给客户一定的折扣。折扣是基于分层模型提供的。为了简单起见,我省略了多个产品类别。以下是 table 的一些示例。
订单table
Customer | order_no
----------------------------
Customer1 | 400
Customer2 | 1200
Customer3 | 40
Customer4 | 2000
Customer5 | 700
分层定价table
Tier | lower_th | higer_th | price |
--------------------------------------
Tier1 | 0 | 250 | 50 |
TIer2 | 251 | 500 | 45 |
Tier3 | 501 | 1000 | 40 |
TIer4 | 1001 | 10000 | 30 |
示例 1:我希望能够向客户 1 收取 250 件订单 50 美元的费用,并向总共 400 件产品中的其余 150 件收取 45 美元。
示例 2:我希望能够向 Customer5 收取 250 笔订单 50 美元,另外 250 笔订单收取 45 美元,其余 200 笔订单 40 美元,共计 700 件商品。
如何在 PostgreSQL 中实现这一点?对于 Customer1,我的输出需要如下所示。拆分订单总数并将其加入定价层以获得相应数量的最佳方法是什么?
Customer | order_no | charges |
--------------------------------
Customer1 | 250 | 50 |
Customer1 | 150 | 45 |
您可以将层级视为间隔。
两个区间[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 |
+-----------+----------+-------+----------+----------+-------+-------------------+
对于定价数据,我会使用这样的 table 来简化数据维护
create table pricing_data
(
high_limit int,
price numeric
);
视图将使用 window 函数为您提供所需的时间间隔:
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
我一直在处理将一个指标拆分成几个波段的问题。为了给您一些背景信息,让我们以这个例子为例,每个客户都有一定数量的订单。现在,一个客户可以订购 n 种产品。让我们根据订单数量给客户一定的折扣。折扣是基于分层模型提供的。为了简单起见,我省略了多个产品类别。以下是 table 的一些示例。
订单table
Customer | order_no
----------------------------
Customer1 | 400
Customer2 | 1200
Customer3 | 40
Customer4 | 2000
Customer5 | 700
分层定价table
Tier | lower_th | higer_th | price |
--------------------------------------
Tier1 | 0 | 250 | 50 |
TIer2 | 251 | 500 | 45 |
Tier3 | 501 | 1000 | 40 |
TIer4 | 1001 | 10000 | 30 |
示例 1:我希望能够向客户 1 收取 250 件订单 50 美元的费用,并向总共 400 件产品中的其余 150 件收取 45 美元。
示例 2:我希望能够向 Customer5 收取 250 笔订单 50 美元,另外 250 笔订单收取 45 美元,其余 200 笔订单 40 美元,共计 700 件商品。
如何在 PostgreSQL 中实现这一点?对于 Customer1,我的输出需要如下所示。拆分订单总数并将其加入定价层以获得相应数量的最佳方法是什么?
Customer | order_no | charges |
--------------------------------
Customer1 | 250 | 50 |
Customer1 | 150 | 45 |
您可以将层级视为间隔。
两个区间[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 |
+-----------+----------+-------+----------+----------+-------+-------------------+
对于定价数据,我会使用这样的 table 来简化数据维护
create table pricing_data
(
high_limit int,
price numeric
);
视图将使用 window 函数为您提供所需的时间间隔:
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