我正在尝试计算某些住宿的分级费用。假设我们有每周,半周和每天的房价。
period_name | nights | rate
-------------------------------------
WEEK | 7 | 100
HALFWEEK | 3 | 50
DAY | 1 | 25
我该如何查询总夜晚数,并弄清哪些时段符合条件(从最长到最短)?一些示例结果
10晚
我们将10分为(7天)+(3天)。这7天将以周速率(100)进行。这三天的价格为半周(50)。在这里,它有资格获得(1周@ 100)+(1半周@ 50)
period_name | nights | rate | num | subtotal
----------------------------------------------
WEEK | 7 | 100 | 1 | 100
HALFWEEK | 3 | 50 | 1 | 50
4晚
我们将4分为(3天)+(1天)。这三天的价格为半周(50)。 1天将按DAY费率(25)。在这里它符合(1半周@ 50)+(1天@ 25)
period_name | nights | rate | num | subtotal
----------------------------------------------
HALFWEEK | 3 | 50 | 1 | 50
DAY | 1 | 25 | 1 | 25
16晚
我们将16分为(14天)+(2天)。这14天将以周率(乘以2)(100 * 2)为准。 2天将以DAY费率(2 x 25)。在这里,它有资格获得(2周@ 100)+(2天@ 25)
period_name | nights | rate | num | subtotal
----------------------------------------------
WEEK | 7 | 100 | 2 | 200
DAY | 1 | 25 | 2 | 50
我考虑过使用lag窗口函数,但是现在确定如何跟踪上一时期已经应用的日期。
答案 0 :(得分:1)
您可以使用CTE RECURSIVE查询来做到这一点。
http://sqlfiddle.com/#!17/0ac709/1
层级表(可以动态扩展):
id name days rate
-- --------- ---- ----
1 WEEK 7 100
2 DAYS 1 25
3 HALF_WEEK 3 50
4 MONTH 30 200
天数:
id num
-- ---
1 10
2 31
3 30
4 19
5 14
6 108
7 3
8 5
9 1
10 2
11 7
结果:
num_id num days total_price
------ --- ----------------------------------------------- -----------
1 10 {"MONTH: 0","WEEK: 1","HALF_WEEK: 1","DAYS: 0"} 150
2 31 {"MONTH: 1","WEEK: 0","HALF_WEEK: 0","DAYS: 1"} 225
3 30 {"MONTH: 1","WEEK: 0","HALF_WEEK: 0","DAYS: 0"} 200
4 19 {"MONTH: 0","WEEK: 2","HALF_WEEK: 1","DAYS: 2"} 300
5 14 {"MONTH: 0","WEEK: 2","HALF_WEEK: 0","DAYS: 0"} 200
6 108 {"MONTH: 3","WEEK: 2","HALF_WEEK: 1","DAYS: 1"} 875
7 3 {"MONTH: 0","WEEK: 0","HALF_WEEK: 1","DAYS: 0"} 50
8 5 {"MONTH: 0","WEEK: 0","HALF_WEEK: 1","DAYS: 2"} 100
9 1 {"MONTH: 0","WEEK: 0","HALF_WEEK: 0","DAYS: 1"} 25
10 2 {"MONTH: 0","WEEK: 0","HALF_WEEK: 0","DAYS: 2"} 50
11 7 {"MONTH: 0","WEEK: 1","HALF_WEEK: 0","DAYS: 0"} 100
想法:
首先,我使用此查询来计算一个值(19)的结果:
SELECT
days / 7 as WEEKS,
days % 7 / 3 as HALF_WEEKS,
days % 7 % 3 / 1 as DAYS
FROM
(SELECT 19 as days) s
在这里您可以看到模块操作的递归结构以整数除法终止。因为应该有一个更通用的版本,所以我考虑了一个递归版本。使用PostgreSQL WITH RECURSIVE子句,这是可能的
https://www.postgresql.org/docs/current/static/queries-with.html
所以这就是最终查询
WITH RECURSIVE days_per_tier(row_no, name, days, rate, counts, mods, num_id, num) AS (
SELECT
row_no,
name,
days,
rate,
num.num / days,
num.num % days,
num.id,
num.num
FROM (
SELECT
*,
row_number() over (order by days DESC) as row_no -- C
FROM
testdata.tiers) tiers, -- A
(SELECT id, num FROM testdata.numbers) num -- B
WHERE row_no = 1
UNION
SELECT
days_per_tier.row_no + 1,
tiers.name,
tiers.days,
tiers.rate,
mods / tiers.days, -- D
mods % tiers.days, -- E
days_per_tier.num_id,
days_per_tier.num
FROM
days_per_tier,
(SELECT
*,
row_number() over (order by days DESC) as row_no
FROM testdata.tiers) tiers
WHERE days_per_tier.row_no + 1 = tiers.row_no
)
SELECT
num_id,
num,
array_agg(name || ': ' || counts ORDER BY days DESC) as days,
sum(total_rate_per_tier) as total_price -- G
FROM (
SELECT
*,
rate * counts as total_rate_per_tier -- F
FROM days_per_tier) s
GROUP BY num_id, num
ORDER BY num_Id
WITH RECURSIVE包含递归UNION,递归部分的起点。起点只是获得层(A)和数字(B)。为了按天排序,请添加行数(C;仅当相应的ID如我的示例中所示的顺序不正确时才需要。如果添加其他层,则可能会发生这种情况。)
递归部分获取先前的SELECT结果(存储在days_per_tier
中),并计算下一个余数和整数除法(D,E)。所有其他列仅用于保存原点值(递增的行计数器除外,它负责递归本身)。
递归后,计数和费率相乘(F),然后与产生总和(G)的原始编号id分组
编辑: 添加了rate函数和sqlfiddle链接。
答案 1 :(得分:0)
这里您需要做的是首先触发一条SQL命令以检索所有条件并为您的业务逻辑写下功能。
例如。
我将在查询下面向数据库中触发。
按晚上的夜晚顺序从table_name顺序中选择*
结果,我将获得按夜晚降序排列的数据,这意味着首先是7,然后是3,然后是1。
假设我需要找11天。
我将获取第一个记录7,并检查它为11。
if(11 > 7){// execute this if in a loop till it's greater then 7, same for 3 & 1
days = 11-7;
price += price_from_db;
package += package_from_db;
}else{
// goto fetch next record and check the above condition with next record.
}
注意:我写下一种算法,而不是特定于语言的代码。