我正在为我们的办公桌/接待处创建一个酒店预订工具。我当前的
SELECT
不会计算“最终”sum()
/CASE
,这意味着只要每个 hb.booking_period && cp.valid_period
有超过一个重叠 (booking_ID
),数据中的 CASE
值产出仅代表总价的份额。如果您将这些份额相加,则它与(住宿的)总价格相匹配。我如何计算所有这些 CASE
结果/份额的总和(适合相关 booking_id
)?
SELECT
hb.booking_id,
hb.guest_name,
hb.room_category_id,
hb.booking_period,
cp.base_price,
CASE
WHEN upper(hb.booking_period * cp.valid_period) = upper(hb.booking_period) THEN
(upper(hb.booking_period * cp.valid_period) - lower(hb.booking_period * cp.valid_period)) * cp.base_price
ELSE
(upper(hb.booking_period * cp.valid_period) - lower(hb.booking_period * cp.valid_period) + 1) * cp.base_price
END
FROM
hotel_bookings hb
JOIN
category_prices cp ON hb.room_category_id = cp.room_category_id AND hb.booking_period && cp.valid_period
GROUP BY
hb.booking_id, hb.guest_name, hb.room_category_id, hb.booking_period, cp.base_price, cp.valid_period;
booking_period
和 valid_period
是日期范围数据类型。
CASE
确保正确计算intersection * base_price
,否则每个交叉路口都会丢失 1 个停留。
以下是表格定义:
CREATE TABLE room_categories
(
category_id SERIAL PRIMARY KEY,
category_name VARCHAR(25)
);
CREATE TABLE category_prices
(
category_price_id SERIAL PRIMARY KEY,
room_category_id INTEGER REFERENCES room_categories(category_id),
valid_period daterange,
base_price DECIMAL(10, 2)
);
CREATE TABLE hotel_bookings
(
booking_id SERIAL PRIMARY KEY,
guest_name VARCHAR(255),
room_category_id INTEGER REFERENCES room_categories(category_id),
booking_period daterange
);
数据:
INSERT INTO room_categories (category_name) VALUES
('single room'),
('double room');
INSERT INTO category_prices (room_category_id, valid_period, base_price) VALUES
(1, '[2023-01-01, 2023-01-31)', 80.00),
(1, '[2023-02-01, 2023-02-28)', 85.00),
(1, '[2023-03-01, 2023-03-31)', 88.00),
(2, '[2023-01-01, 2023-01-31)', 100.00),
(2, '[2023-02-01, 2023-02-28)', 105.00),
(2, '[2023-03-01, 2023-03-31)', 108.00);
INSERT INTO hotel_bookings (guest_name, room_category_id, booking_period) VALUES
('John Doe', 1, '[2023-01-15, 2023-01-20)'), --correct calced, 1 intersection
('Jane Smith', 1, '[2023-01-30, 2023-02-02)'), -- 2 shares (need/want a sumup)
('Jane Smith', 1, '[2023-02-25, 2023-03-03)'),
('Jordan Miller', 2, '[2023-01-30, 2023-03-02)'); -- 3 shares (need/want a sumup)
sum(case when a then b else c end)
是完全允许的。通过示例查看您的编辑,我的猜测是您分组得太深:期间及其价格会乘以您的预订行,为每个 hb.booking_period && cp.valid_period
交叉点生成一个单独的条目。要将每个预订的一笔有效金额折叠成一行,您可以将它们聚合起来,例如与 jsonb_object_agg()
。 db<>fiddle 的演示:
SELECT
hb.booking_id,
hb.guest_name,
hb.room_category_id,
hb.booking_period,
jsonb_pretty(jsonb_object_agg(cp.valid_period,cp.base_price)),
SUM(CASE WHEN upper(hb.booking_period * cp.valid_period) = upper(hb.booking_period)
THEN (upper(hb.booking_period * cp.valid_period) - lower(hb.booking_period * cp.valid_period)) * cp.base_price
ELSE (upper(hb.booking_period * cp.valid_period) - lower(hb.booking_period * cp.valid_period) + 1) * cp.base_price
END )
FROM hotel_bookings hb
JOIN category_prices cp
ON hb.room_category_id = cp.room_category_id
AND hb.booking_period && cp.valid_period
GROUP BY hb.booking_id,
hb.guest_name,
hb.room_category_id,
hb.booking_period;
预订_id | 客人姓名 | 房间类别_id | 预订_期间 | jsonb_pretty | 总和 |
---|---|---|---|---|---|
1 | 约翰·多伊 | 1 | [2023-01-15,2023-01-20) | {“[2023-01-01,2023-01-31)”: 80.00} | 400.00 |
2 | 简·史密斯 | 1 | [2023-01-30,2023-02-02) | {“[2023-01-01,2023-01-31)”: 80.00,“[2023-02-01,2023-02-28)”: 85.00 } |
245.00 |
3 | 简·史密斯 | 1 | [2023-02-25,2023-03-03) | {“[2023-02-01,2023-02-28)”: 85.00, “[2023-03-01,2023-03-31)”: 88.00} |
516.00 |
4 | 乔丹·米勒 | 2 | [2023-01-30,2023-03-02) | {"[2023-01-01,2023-01-31)": 100.00, "[2023-02-01,2023-02-28)": 105.00, "[2023-03-01,2023- 03-31)": 108.00} |
3248.00 |
该
case
声明的原因可能是您无意中为您的类别价格使用了 上限独占 范围。如果你让它们包含上限,你可以摆脱整个事情,只需将 booking_period*valid_period
交叉点长度乘以 base_price
: demo2
UPDATE category_prices
SET valid_period=daterange(lower(valid_period),upper(valid_period),'[]');
SELECT
hb.booking_id,
hb.guest_name,
hb.room_category_id,
hb.booking_period,
jsonb_pretty(jsonb_object_agg(cp.valid_period,cp.base_price)),
SUM(( upper(hb.booking_period * cp.valid_period)
- lower(hb.booking_period * cp.valid_period))
* cp.base_price )
FROM hotel_bookings hb
JOIN category_prices cp
ON hb.room_category_id = cp.room_category_id
AND hb.booking_period && cp.valid_period
GROUP BY hb.booking_id,
hb.guest_name,
hb.room_category_id,
hb.booking_period;
参见备忘单:
select raw,
dr AS canonicalized,
lower(dr),
lower_inc(dr),
upper(dr),
upper_inc(dr)
from (values ('(2023-01-01, 2023-01-31)'),
('(2023-01-01, 2023-01-31]'),
('[2023-01-01, 2023-01-31)'),
('[2023-01-01, 2023-01-31]'))AS a(raw),
lateral (select raw::daterange)AS b(dr);
原始 | 规范化 | 下 | lower_inc | 上 | upper_inc |
---|---|---|---|---|---|
(2023-01-01, 2023-01-31) | [2023-01-02,2023-01-31) | 2023-01-02 | t | 2023-01-31 | f |
(2023-01-01, 2023-01-31] | [2023-01-02,2023-02-01) | 2023-01-02 | t | 2023-02-01 | f |
[2023-01-01, 2023-01-31) | [2023-01-01,2023-01-31) | 2023-01-01 | t | 2023-01-31 | f |
[2023-01-01, 2023-01-31] | [2023-01-01,2023-02-01) | 2023-01-01 | t | 2023-02-01 | f |