我有这三张桌子:
酒店 |
---|
酒店_id |
酒店名称 |
房间 |
---|
房间_id |
酒店_id |
房间价格 |
---|
房间_id |
开始日期 |
价格 |
其中“start_date”存储价格更改为“price”的日期。
我知道如何查询数据库来获取给定 room_id 和给定酒店住宿日期的价格。所以我可以做到这一点,但是,当涉及到在很长的日期范围内执行此操作时,这变得不切实际。
因此,我如何查询数据库才能得到类似的输出
房间_id | 日期 | 价格 |
---|---|---|
1 | 签到 | x1 |
… | … | x2 |
… | … | … |
1 | 签出 | xm |
2 | 签到 | y1 |
… | … | y2 |
… | … | … |
2 | 签出 | ym |
… | … | … |
… | … | … |
… | … | … |
n | 签到 | z1 |
… | … | z2 |
… | … | … |
n | 签出 | zm |
这里的“check_in”是客户选择的第一晚,“check_out”是客户选择的最后一晚
这将使计算给定房间的总价格变得更加容易,而不必每天单独计算。
注意:我使用的是 MySQL 5.7
您可以动态创建日期系列,如 Bagus Tesa 建议的 答案 所示,或者维护日历表。鉴于日期在预订/预订应用程序中发挥着重要作用,我建议创建一个简单的日历表。
在本例中,我使用的日历表只有一个 dt (
DATE
) 列,您可以轻松地使用存储过程(或您选择的任何其他方法)填充该列:
CREATE TABLE calendar (dt DATE NOT NULL PRIMARY KEY);
DELIMITER $$
CREATE PROCEDURE `filldates`(dateStart DATE, dateEnd DATE)
BEGIN
WHILE dateStart <= dateEnd DO
INSERT INTO calendar (dt) VALUES (dateStart);
SET dateStart = dateStart + INTERVAL 1 DAY;
END WHILE;
END$$
DELIMITER ;
CALL filldates('2023-01-01', '2039-12-31');
然后是预订日期和房间日期之间的
CROSS JOIN
的简单情况,以及用于检索每天每间房间的价格的相关子查询:
SET @checkin = '2023-11-26', @checkout = '2023-12-15';
SELECT r.room_id, COUNT(*) AS nights, (
SELECT price
FROM room_price
WHERE room_id = r.room_id
AND start_date <= c.dt
ORDER BY start_date DESC
LIMIT 1
) AS price
FROM calendar c
CROSS JOIN room r
WHERE c.dt >= @checkin AND c.dt < @checkout
GROUP BY room_id, price;
注意:虽然关键字
CROSS
在MySQL中没有任何意义(它只是一个没有ON子句的INNER连接),但我认为它清楚地表达了意图。
这是一个db<>小提琴。
以下查询依赖于
join
和 group by
而不是子查询。它的灵感来自于生成日期系列作为日期参考。您可以将日期生成替换为具体的日期表,如 user1191247 答案建议。
select rp.room_id as room_id,
date_series.date as date,
any_value(rp.price) as price
from
(select *
from room_price
order by start_date desc
) as rp
join
(select * from
(select adddate('1970-01-01',t4*10000 + t3*1000 + t2*100 + t1*10 + t0) date from
(select 0 t0 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 t1 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 t2 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 t3 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(select 0 t4 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
where date between '2023-01-01' and '2023-05-31'
) as date_series
on rp.start_date <= date_series.date
group by rp.room_id, date_series.date
order by rp.room_id, date_series.date;
参见小提琴:https://www.db-fiddle.com/f/umGDya94iodhMcJbTdvkt6/0
规避
SELECT list is not in GROUP BY clause and contains non-aggregated column
错误的基石是使用 Any_Value()
。您可以通过在会话中或永久禁用ONLY_FULL_GROUP_BY
来达到类似的效果。
但是,请注意 Any_Value()
在 Mysql 5.7 和 Mysql 8.0 之间的行为不同 - 这有点令人惊讶。