我有这张桌子:
表___Bookings
:
|--------|------------|--------------|------------|------------|
| BOO_Id | BOO_RoomId | BOO_ClientId | BOO_DateCI | BOO_DateCO |
|--------|------------|--------------|------------|------------|
| 1 | 9 | 45 | 2018-01-02 | 2018-01-03 |
| 2 | 4 | 46 | 2017-12-30 | 2018-01-07 |
| 3 | 3 | 2 | 2018-12-31 | 2018-01-01 |
| 4 | 9 | 98 | 2018-01-05 | 2018-01-10 |
|--------|------------|--------------|------------|------------|
我希望每天在出发,抵达或内部展示预订。
所需的输出应该是这样的:
2017-12-30 = Booking #2 in arrival.
2017-12-31 = Booking #3 in arrival.
= Booking #1 in arrival.
= Booking #2 in house.
2018-01-01 = Booking #3 in departure.
= Booking #2 in house.
2018-01-02 = Booking #1 in arrival.
= Booking #2 in house.
2018-01-03 = Booking #1 in departure.
= Booking #2 in house.
2018-01-04 = Booking #2 in house.
2018-01-05 = Booking #4 in arrival.
= Booking #2 in house.
2018-01-06 = Booking #2 in house.
= Booking #4 in house.
2018-01-07 = Booking #2 in departure.
= Booking #4 in house.
2018-01-08 = Booking #4 in house.
2018-01-09 = Booking #4 in house.
2018-01-10 = Booking #4 in departure.
我已经尝试过:
SELECT *,
CASE
WHEN BOO_DateCI = '2017-12-31' THEN 'In Arrival'
WHEN BOO_DateCO = '2018-01-10' THEN 'In Departure'
WHEN '2017-12-31' > BOO_DateCI AND '2018-01-10' < BOO_DateCO THEN 'In House'
END
FROM ___Bookings
WHERE BOO_DateCI = '2017-12-31'
OR BOO_DateCO = '2018-01-10'
OR :today BETWEEN BOO_DateCI AND BOO_DateCO;
但我无法获得in house
预订,也无法根据状态arrival
,departure
或in-house
对预订进行分组。
答案 0 :(得分:3)
这是我的解决方案。一般的想法是生成一个您感兴趣的日期列表,然后JOIN
此列表与您的预订一起,条件是特定客人访问期间的特定日期。
我假设这里有连续的日期。如果要查询多个非连续范围,可以为每个连续部分重复该过程,或者仍然可以查询整个范围,然后过滤掉您不感兴趣的日期。
首先,您需要设置要为其生成该列表的日期范围。为方便起见,我将它们存储在两个变量中。
SELECT MIN(BOO_DateCI)
FROM Bookings
INTO @first_date;
SELECT MAX(BOO_DateCO)
FROM Bookings
INTO @last_day;
这将为您提供第一次登记入住和最后一次退房(包括两个日期)之间的整个范围。当然,您可以通过将这些变量设置为某个特定日期来轻松选择日期:
SET @first_day := '2018-01-02';
SET @last_day := '2018-01-06';
下一步是设置一个新变量,作为生成日期列表的计数器:
SELECT DATEDIFF(@last_day,@first_day) + 1 INTO @i;
现在一切都准备就绪了:
SELECT
Datelist.date,
Bookings.BOO_Id,
CASE
WHEN Bookings.BOO_DateCI = Datelist.date THEN 'In Arrival'
WHEN Bookings.BOO_DateCO = Datelist.date THEN 'In Departure'
WHEN Datelist.date > Bookings.BOO_DateCI AND Datelist.date < Bookings.BOO_DateCO THEN 'In House'
END AS status
FROM
(SELECT DATE(@first_day + INTERVAL @i:=@i-1 DAY) AS date
FROM (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) dummy1,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) dummy2
HAVING @i > 0) Datelist
INNER JOIN Bookings
ON Datelist.date BETWEEN Bookings.BOO_DateCI AND Bookings.BOO_DateCO
ORDER BY Datelist.date, BOO_Id;
如果您希望看到该野兽在行动,请检查 SQL Fiddle
现在,该查询有一部分,我觉得需要更多解释,这就是Datelist
的生成方式:
SELECT DATE(@first_day + INTERVAL @i:=@i-1 DAY) AS date
FROM AnyTable
HAVING @i > 0
这将生成一个日期列表,从@last_day
开始到first_day
(因为@i
正在倒计时,而不是向上)。 AnyTable
这里只是数据库中的任何表。这里需要注意的是,您不能省略FROM
子句,因为它需要行,因此在每一行上都执行@i:=@i-1
。但这意味着,您的日期列表最多包含与AnyTable
相同的行数。所以,如果你只有短表(如你的例子),这将失败。为了解决这个问题,我们可以自己生成一些行:
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) dummy1,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) dummy2
作为AnyTable
的替代,这将生成两个派生表,每行4行并交叉连接,生成16行的结果,因为2个表,每行4行交叉连接,为您提供4x4 = 16行。如果您需要更多,可以使用更多UNION
个,更多交叉连接或两者,例如:
(SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) dummy1,
(SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3) dummy2,
(SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) dummy3
将为您提供4x3x5 = 60行。
请注意:这并不意味着您的最终结果只能包含60行,这意味着您生成的日期列表最多可包含60个日期。
正如您在 Fiddle 中看到的,生成16行的查询显然比生成100000行的查询要快。性能上的差异并不是很大,我多次运行该测试,16行的时间在2ms到8ms之间,10万次的时间在16ms到60ms之间。但是,如果您需要大量行并且您的代码经常执行,那么您可能会感觉更好,创建一个只包含100000个不同数字的虚拟表,并在每次运行查询时使用它而不是生成派生表(旁注:100000天差不多是274年,所以我很确定在你的特殊情况下你不需要那么多,但仍然值得注意的是,如果性能问题还有其他选择)
另一方面,如果您完全确定Bookings
(在实际情况下)的行数多于您需要的行数,则可以使用Bookings
代替AnyTable
答案 1 :(得分:0)
您好DamDam您需要使用存储过程来满足此要求,因为我们肯定需要使用循环来查找每天到达,离开和内部状态的数量。检查下面的代码。
这里我最初计算了check in date column (BOO_DateCI)
的MIN日期作为循环的开始日期。 Loop将一直运行到今天的日期。
在if循环中,我正在做一个select语句 1.第一列是循环中的日期 2.第二栏是您需要的预订#+预订ID。 3.第三列是状态计算
如果BOO_DateCI == dateFromTheLoop =&gt;这意味着当天到达。
如果BOO_DateCO == dateFromTheLoop =&gt;这意味着当天离开。
如果BOO_DateCI&lt; dateFromTheLoop和BOO_DateCO&gt; dateFromTheLoop =&gt; 这意味着当时还在家里
然后按日期排序这些结果。
完成后,我将在循环日期中添加1天,以便在下一次迭代中使用。
CREATE PROCEDURE findBookings(d1 DATE)
begin
LABEL1:
LOOP
SET d1 = Select MIN(BOO_DateCI) from ___Bookings;
IF d1 <= Date(NOW()) THEN
SELECT d1 as date,
Concat('Booking #', boo_id),
CASE
WHEN BOO_DateCI = d1 THEN 'In Arrival'
WHEN BOO_DateCO = d1 THEN 'In Departure'
WHEN BOO_DateCI < d1 AND BOO_DateCO > d1 THEN 'In House'
end
FROM ___Bookings Order by date;
d1 = DATE_ADD(d1, INTERVAL 1 DAY);
ITERATE label1;
end IF;
LEAVE label1;
end LOOP label1;
end;
希望这有帮助。