我正在为我们的服务台/接待处创建一个wine 店预订工具.我当前的SELECT不计算"最终"sum()/CASE,这意味着只要每booking_ID有一个以上的重叠(hb.booking_period && cp.valid_period),数据输出中的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_periodvalid_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)是完全允许的.通过示例查看您的编辑,我的猜测是您的分组太深了:时段及其价格乘以您的预订行, for each hb.booking_period && cp.valid_period个交叉点产生一个单独的条目.要使用一个有效金额将它们合并到每个预订的一行中,您可以将它们聚合在一起,例如jsonb_object_agg().Demo at 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;
booking_id guest_name room_category_id booking_period jsonb_pretty sum
1 John Doe 1 [2023-01-15,2023-01-20) {"[2023-01-01,2023-01-31)": 80.00} 400.00
2 Jane Smith 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 Jane Smith 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 Jordan Miller 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声明的原因可能是您无意中为类别价格使用了upper bound exclusive个范围.如果将它们设为包含上限,则可以go 掉整个问题,只需将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);
raw canonicalized lower lower_inc upper 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

fiddle

Sql相关问答推荐

GROUP BY与多个嵌套查询T—SQL

如何转换和汇总行数

在SQL中返回缺省值,即使查询不返回任何结果

如何在SQL Server中拆分包含字符和数字的列?

如何在SQL Server中统计按备注分组的记录数

返回UPSERT中的旧行值

数组列的postgres更新查询

将一个数组反嵌套到另外两个数组SQL中(Athena/presto)

如何使用不重复的单个顶级字段(列)向json数组 Select 多行

无法访问级联删除导致的触发器中已删除的外键记录

从每月生成的系列中生成每日汇率

列(值不为空)到其他有序列

SQL 根据前一天的最大值计算每天的值数

按所选的值将记录分组到不同的列中

SQL Server: 将JSON对象数组转换为表格格式

使用 GROUP BY 时如何创建其他组?

计算 SQL 中的总体成功率:递归 CTE 还是替代方法?

过滤具有一对多关系的两个表之间的数据

PostgresQL-根据另一列找到 3 个最低值

如何使用子查询锁定此查询中的选定行?