我正在使用 PostgreSQL 16 为我们的前台/接待处创建一个酒店预订工具。为了回答预订请求,我需要总价和相关的每日住宿价格。
当前设置+虚拟数据:dbfiddle.uk
表格简短概述:
category_prices
:base_prices
。在任何时候,每个类别都只有一个“活动”数据集。 => 没有重叠,每个日期和类别只有一个价格。
price_adjustments
:我当前的尝试计算每次住宿/间隔的总
base_price
和总price_adjustment
并将它们相加,但我不知道如何计算每日价格(甚至从哪里开始):
SELECT
hb.booking_id,
hb.guest_name,
hb.room_category_id,
hb.booking_period,
SUM((
upper(hb.booking_period * cp.valid_period) -
lower(hb.booking_period * cp.valid_period)
) * cp.base_price) + subquery.booking_price_adjustment AS total_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
LEFT JOIN (
SELECT
hb.booking_id,
COALESCE(SUM((
upper(hb.booking_period * pa.valid_period) -
lower(hb.booking_period * pa.valid_period)
) * pa.price_adjustment), 0) AS booking_price_adjustment
FROM
hotel_bookings hb
LEFT JOIN price_adjustments pa ON hb.room_category_id = pa.room_category_id
AND hb.booking_period && pa.valid_period
GROUP BY
hb.booking_id
) subquery ON hb.booking_id = subquery.booking_id
GROUP BY
hb.booking_id,
hb.guest_name,
hb.room_category_id,
hb.booking_period,
subquery.booking_price_adjustment;
我对数据库相当陌生(这是我的第一个子查询)。
我试图找出一种方法来生成包含/返回类似内容的对象/数据集/数组/SELECT:
{ interval_1 : relevant_base_price + sum(relevant_price_adjustments)
, interval_2 : relevant_base_price + sum(relevant_price_adjustments)
, ...
, interval_n : relevant_base_price + sum(relevant_price_adjustments)}
其中间隔没有重叠,并且它们的并集等于初始间隔/日期范围,但我每次尝试都失败了。
您询问每日价格:
SELECT bp.booking_id, bp.guest_name, bp.room_category_id
, d.the_day::date
, bp.base_price
, COALESCE(pa.sum_price_adjustment, 0) AS sum_price_adjustment
, COALESCE(sum(bp.base_price) OVER (PARTITION BY bp.booking_id), 0)
+ COALESCE(sum(pa.sum_price_adjustment) OVER (PARTITION BY bp.booking_id), 0) AS total_price_of_booking
FROM (
-- days not covered in category_prices are lost!
SELECT hb.booking_id
, hb.guest_name, hb.room_category_id
, hb.booking_period * cp.valid_period AS bp_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
) bp
CROSS JOIN generate_series(lower(bp.bp_period)::timestamp
, (upper(bp.bp_period) - 1)::timestamp
, interval '1 day') AS d(the_day)
CROSS JOIN LATERAL ( -- can be INNER JOIN because agg always produces row
SELECT sum(price_adjustment) AS sum_price_adjustment
FROM price_adjustments pa
WHERE pa.room_category_id = bp.room_category_id
AND pa.valid_period @> d.the_day::date
) pa
ORDER BY bp.booking_id, d.the_day;
第 1 步:在子查询
bp
中计算预订价格和基本价格之间的交集。 (你已经有了。)参见:
第 2 步:在(隐式)
LATERAL
子查询d
中为每个交叉点生成一系列天。参见:
第 3 步:在
sum_price_adjustment
子查询中每天计算 LATERAL
。
第 4 步:(可选)使用窗口函数添加每次预订的总价。
每次预订每天一行,包含底价和价格调整总和。
加上预订的总价(可选)。
日期范围存储时包含包含下限和排除上限。参见:
如果
category_prices
中的条目不完整或重叠,我的查询会产生不正确/缺失的结果。确保基础数据一致。
此外,请务必拥有适用的索引以加快速度。