我有两张名为Entry
和Booking
的牌桌。我的想法是:我创建一个entry
,其中start
和end
时间,然后我可以为此entry
创建预订。 booking
与entry
相关联,并且还有start
和end
。 booking
位于entry
的时间内,意为booking.start >= entry.start
和booking.end <= entry.end
。此外,一个bookings
entry
&#34;条目&#34;表
id | start | end
----------------------------------------------
1 | 2014-07-24 09:00:00 | 2014-07-24 11:20:00
&#34;预订&#34;表
id | start | end | entry_id
-----------------------------------------------------------
1 | 2014-07-24 09:10:00 | 2014-07-24 09:40:00 | 1
2 | 2014-07-24 09:50:00 | 2014-07-24 10:20:00 | 1
3 | 2014-07-24 10:50:00 | 2014-07-24 11:20:00 | 1
此示例大致如下所示:
<-------------- entry 1 interval ---------------------------------->
<-booking 1-> <-booking 2-> <-booking 3->
现在我想获得条目1尚未预订的所有时间间隔:
期望的结果:
entry_id | start | end
----------------------------------------------------
1 | 2014-07-24 09:00:00 | 2014-07-24 09:10:00
1 | 2014-07-24 09:40:00 | 2014-07-24 09:50:00
1 | 2014-07-24 10:20:00 | 2014-07-24 10:50:00
我如何使用SQL执行此操作? (我正在寻找一个通用的解决方案,虽然我将在MySQL中使用它)
答案 0 :(得分:1)
MS SQL
SELECT * FROM
(
SELECT ENTRY_ID,
b1.BOOK_END_DATE AS INTERVALL_START_DATE,
(SELECT TOP 1 b2.BOOK_START_DATE FROM Booking b2 WHERE b1.BOOK_ENTRY_ID = b2.BOOK_ENTRY_ID AND b2.BOOK_START_DATE > b1.BOOK_END_DATE ORDER BY b2.BOOK_START_DATE) AS INTERVALL_END_DATE
FROM Entry
JOIN Booking b1 ON ENTRY_ID = b1.BOOK_ENTRY_ID
) AS sub01
WHERE sub01.INTERVALL_START_DATE is not null AND sub01.INTERVALL_END_DATE is not null AND sub01.INTERVALL_START_DATE <> sub01.INTERVALL_END_DATE
UNION
(SELECT ENTRY_ID, ENTRY_START_DATE, (SELECT TOP 1 BOOK_START_DATE FROM Booking WHERE BOOK_ENTRY_ID = ENTRY_ID AND BOOK_START_DATE > ENTRY_START_DATE ORDER BY BOOK_START_DATE) FROM Entry)
UNION
(SELECT ENTRY_ID, (SELECT TOP 1 BOOK_END_DATE FROM Booking WHERE BOOK_ENTRY_ID = ENTRY_ID AND BOOK_END_DATE < ENTRY_END_DATE ORDER BY BOOK_END_DATE DESC), ENTRY_END_DATE FROM Entry)
<强>的MySQL 强>
SELECT * FROM
(
SELECT * FROM
(
SELECT ENTRY_ID,
b1.BOOK_END_DATE AS INTERVALL_START_DATE,
(SELECT b2.BOOK_START_DATE FROM Booking b2 WHERE b1.BOOK_ENTRY_ID = b2.BOOK_ENTRY_ID AND b2.BOOK_START_DATE > b1.BOOK_END_DATE ORDER BY b2.BOOK_START_DATE LIMIT 1) AS INTERVALL_END_DATE
FROM Entry
JOIN Booking b1 ON ENTRY_ID = b1.BOOK_ENTRY_ID
) AS sub01
WHERE sub01.INTERVALL_START_DATE is not null AND sub01.INTERVALL_END_DATE is not null
UNION
(SELECT e2.ENTRY_ID, e2.ENTRY_START_DATE, (SELECT b2.BOOK_START_DATE FROM Booking b2 WHERE b2.BOOK_ENTRY_ID = e2.ENTRY_ID AND b2.BOOK_START_DATE >= e2.ENTRY_START_DATE ORDER BY b2.BOOK_START_DATE LIMIT 1) FROM Entry e2)
UNION
(SELECT e3.ENTRY_ID, (SELECT b3.BOOK_END_DATE FROM Booking b3 WHERE b3.BOOK_ENTRY_ID = e3.ENTRY_ID AND BOOK_END_DATE <= e3.ENTRY_END_DATE ORDER BY b3.BOOK_END_DATE DESC LIMIT 1), e3.ENTRY_END_DATE FROM Entry e3)
) sub02
WHERE sub02.INTERVALL_START_DATE <> sub02.INTERVALL_END_DATE
答案 1 :(得分:1)
这是一个想法。而不是一系列“to”/“from”或“start”/“end”日期必须保持同步而没有重叠,而是使用只有一个日期的设计。
CREATE TABLE Entry (
ID INT NOT NULL,
EffDate datetime not null,
State smallint NOT NULL,
Comment varchar( 100 ),
primary key( ID, EffDate, State )
);
这里的关键是每个州的变化都有记录。国家基本上是预订(不可用)而未预订(可用)。因此,当您创建条目时,您会在条目开始时写一行,而在条目结束时写一行。
INSERT INTO Entry( ID, EffDate, State, Comment )
select 1, STR_TO_DATE( '2014-07-24 09:00', '%Y-%m-%d %h:%i' ), -2, 'Entry Open' union all
select 1, STR_TO_DATE( '2014-07-24 11:20', '%Y-%m-%d %h:%i' ), 0, 'Entry Closed';
这些状态为零或负值。它们可以是任何值,我只是选择它们,因为它们允许方便的分组。有一个状态-1,你马上就会看到。
有两个查询可以为您提供您想知道的内容:一个查询可以显示条目何时被预订,一个查询可以显示条目何时可用以及显示多长时间。
-- When is the entry available?
select e1.ID as Entry, e1.Comment as Availability, null as `Booking #`,
e1.EffDate as `From`, e2.EffDate as `To`
from Entry e1
left join Entry e2
on e2.ID = e1.ID
and e2.EffDate =(
select Min( EffDate )
from Entry
where ID = e1.ID
and State >= 0
and EffDate > e1.EffDate)
where e1.State <= 0;
此时,结果集如下所示:
ENTRY AVAILABILITY BOOKING # FROM TO
1 Entry Open (null) July, 24 2014 09:00 July, 24 2014 11:20
1 Entry Closed (null) July, 24 2014 11:20 (null)
您稍后会看到“预订#”列的内容。查询显示预订:
-- When is the entry booked?
select e1.ID as Entry, e1.Comment as Availability, e1.State as `Booking #`,
e1.EffDate as `From`, e2.EffDate as `To`
from Entry e1
join Entry e2
on e2.ID = e1.ID
and e2.EffDate =(
select Min( EffDate )
from Entry
where ID = e1.ID
and State in( -1, 0 )
and EffDate > e1.EffDate)
where e1.State > 0;
此时,这没有任何结果。还没有预订。所以让我们生成一个预订。如您所见,整个条目都可用,因此我们要做的是一组“预订开始”和“预订结束”状态行以及适当的时间。
INSERT INTO Entry( ID, EffDate, State, Comment )
select 1, STR_TO_DATE( '2014-07-24 09:10', '%Y-%m-%d %h:%i' ), 1, 'Booked' union all
select 1, STR_TO_DATE( '2014-07-24 09:40', '%Y-%m-%d %h:%i' ), -1, 'Booking Available';
预订的州值是预订号码(1,2等),预订结束是最终的负面状态,-1。现在让我们看一下我们的查询显示的内容,但让我们将它们结合在一起以获得完整的外观:
select *
from(
(first query)
union
(second query)
) B
order by B.Entry, B.`From`;
ENTRY AVAILABILITY BOOKING # FROM TO
1 Entry Open (null) July, 24 2014 09:00 July, 24 2014 09:10
1 Booked 1 July, 24 2014 09:10 July, 24 2014 09:40
1 Booking Available (null) July, 24 2014 09:40 July, 24 2014 11:20
1 Entry Closed (null) July, 24 2014 11:20 (null)
另有预订。我们可以看到时间段从9:00到9:10以及9:40到11:20。预订者在9:50到10:20做出决定,所以我们通过编写一对状态变更记录进行预订。
INSERT INTO Entry( ID, EffDate, State, Comment )
select 1, STR_TO_DATE( '2014-07-24 09:50', '%Y-%m-%d %h:%i' ), 2, 'Booked' union all
select 1, STR_TO_DATE( '2014-07-24 10:20', '%Y-%m-%d %h:%i' ), -1, 'Booking Available';
如果他们想在其他预订完成时立即开始,10:40,那么我们可以更新现有的“预订可用”到第二次预订结束,10:20,并插入预订2记录。预订开始后,当条目可用或另一个预订开始时结束。无关紧要。
ENTRY AVAILABILITY BOOKING # FROM TO
1 Entry Open (null) July, 24 2014 09:00 July, 24 2014 09:10
1 Booked 1 July, 24 2014 09:10 July, 24 2014 09:40
1 Booking Available (null) July, 24 2014 09:40 July, 24 2014 09:50
1 Booked 2 July, 24 2014 09:50 July, 24 2014 10:20
1 Booking Available (null) July, 24 2014 10:20 July, 24 2014 11:20
1 Entry Closed (null) July, 24 2014 11:20 (null)
最后,条目从10:50预订到条目结尾。我们写了Booked记录但是因为它到目前为止的结尾,我们不需要以预订可用行结束它。
INSERT INTO Entry( ID, EffDate, State, Comment )
select 1, STR_TO_DATE( '2014-07-24 10:50', '%Y-%m-%d %h:%i' ), 3, 'Booked';
现在我们可以看到完整的图片:
ENTRY AVAILABILITY BOOKING # FROM TO
1 Entry Open (null) July, 24 2014 09:00 July, 24 2014 09:10
1 Booked 1 July, 24 2014 09:10 July, 24 2014 09:40
1 Booking Available (null) July, 24 2014 09:40 July, 24 2014 09:50
1 Booked 2 July, 24 2014 09:50 July, 24 2014 10:20
1 Booking Available (null) July, 24 2014 10:20 July, 24 2014 10:50
1 Booked 3 July, 24 2014 10:50 July, 24 2014 11:20
1 Entry Closed (null) July, 24 2014 11:20 (null)
如果其他人想要预订,您可以查看此列表,找到预订#为空的位置并阅读以查看可用剩余时间:9:00-9:10,9:40-9:50,10 :20-10:50。或者您读下“从”和“可用性”列以查看操作顺序:条目在9:00“在线”,在9:10预订,在9:40可用,在9:50再次预订,可在10:20,在10:50预订,直到该条目在11:20进入“离线”。
此方法的一个很好的功能是您不必担心重叠间隔。一旦状态开始,它将一直有效,直到下一个状态发生变化。这使得重叠绝对不可能。
中查看整个行动答案 2 :(得分:0)
有点难看:
SELECT
e.id AS entry_id,
e.start AS start,
COALESCE(MIN(b1.start), e.end) AS end
FROM entry e
LEFT JOIN booking b1 ON b1.entry_id = e.id AND b1.start >= e.start AND b1.end <= e.end
GROUP BY e.id
HAVING start < end
UNION
SELECT
e.id AS entry_id,
b1.end AS start,
COALESCE(MIN(b2.start), e.end) AS end
FROM entry e
JOIN booking b1 ON b1.entry_id = e.id AND b1.start >= e.start AND b1.end <= e.end
LEFT JOIN booking b2 ON b2.entry_id = e.id AND b2.start >= b1.end AND b2.end <= e.end
GROUP BY b1.id
HAVING start < end;
针对MySQL进行测试;对于其他SQL-Servers,您可能需要在GROUP BY
术语中包含更多列。没有使用特定于MySQL的函数。
它做什么?
最多UNION
的声明决定了第一次预订前剩余的时间,每个条目最多返回一行。
UNION
之后的陈述选择了该条目的所有预订,并在预订后将所有预订与所有预订配对。
HAVING
,如果许多预订直接相邻,则只会破坏运行时间。
我这是一个稍微修改过的查询,适合轻松选择条目,预订不完全包含在条目和订购中:
SELECT
entry_id,
start,
end
FROM (
SELECT
e.id AS entry_id,
e.start AS start,
COALESCE(MIN(b1.start), e.end) AS end
FROM entry e
LEFT JOIN booking b1 ON b1.entry_id = e.id AND b1.start <= e.end AND b1.end >= e.start
GROUP BY e.id
UNION
SELECT
e.id AS entry_id,
b1.end AS start,
COALESCE(MIN(b2.start), e.end) AS end
FROM entry e
JOIN booking b1 ON b1.entry_id = e.id AND b1.start <= e.end AND b1.end >= e.start
LEFT JOIN booking b2 ON b2.entry_id = e.id AND b2.start >= b1.end AND b2.start <= e.end AND b2.end >= e.start
GROUP BY b1.id) iq
WHERE start < end AND entry_id = 1
ORDER BY start;