Question :
I have a table rooms
which contains information regarding events that take in rooms. This is the definition of the table:
create table room(
sdate date,--start date
stime time,--start time
rno int,--room no
bno int,--building no
eid int,--event id
duration interval,
primary key(sdate, stime, rno, bno, eid)
);
I want to find all events that are adjacent to one another, that is if event x
finishes at 10:00 and event y
starts at 10:00 (provided they’re in the same room, building and date of course) these events are adjacent in terms of time.
I’m new to SQL and I wrote a query to find such events using 5 subqueries and one union which seems to me to be over the top. I’d like to learn the best practices therefore is there a better, more concise way to perform such a query?
This is the query:
with
--calculate end times (etime)
schedule_w_etime(sdate, stime, rno, bno, etime) as (
select sdate, stime, rno, bno, (stime + duration) as etime from schedule
),
--find adjacent events
result(sdate, stime, rno, bno, etime) as (
select * from schedule_w_etime as S
where S.etime in (select stime from schedule_w_etime as T where S.sdate=T.sdate
and S.rno=T.rno
and S.bno=T.bno)
),
--get the last event from result
max_time(date, start_time, room_no, build_no, end_time) as (
select * from result
where etime=(select max(etime) from result)
),
--lookup the start time of the last event
lookup_max_time(sdate, stime, rno, bno, etime) as (
select * from schedule_w_etime as S, max_time as M
where S.stime=M.end_time and S.bno=M.build_no and S.rno=M.room_no
),
--format lookup_max_time to only relevant columns
last_event_final(sdate, stime, rno, bno) as (
select sdate, stime, rno, bno from lookup_max_time
)
--union of the last event and all the other events from result
(select * from last_event_final)
union
(select sdate, stime, rno, bno from result)
As you can see my main problem is with displaying the last event because in the result
view doesn’t contain the start time of the last event.
Answer :
You may simply do a self-join, like:
SELECT r1.sdate, r1.rno, r1.bno, r1.eid,
r2.* -- choose the r2 fields that are needed
FROM room r1 JOIN room r2
ON (r1.rno=r2.rno and r1.bno=r2.bno and r1.sdate=r2.sdate
and r1.stime + r1.duration = r2.stime);
This answers the query: what are the adjacent events?, by returning event x and event y where y follows x, as a relationship.
The proposed SQL statement in your question answers a question that seems slightly different, like: which events are involved in an adjacency relationship? The structure of the result in this case would be one row per such event, then.
One way of getting that is to build it on top of the above query and extract the r1’s and r2’s in separate rows, UNIONing the result (including the deduplication that UNION does). For example:
WITH list AS (
SELECT r1.sdate, r1.rno, ...
r2.sdate AS r2_sdate, r2.rno AS r2_rno, ...
FROM room r1 JOIN room r2
ON (r1.rno=r2.rno and r1.bno=r2.bno and r1.sdate=r2.sdate
and r1.stime + r1.duration = r2.stime)
)
SELECT sdate, rno, ... FROM list
UNION
SELECT r2_sdate, r2_rno, ... FROM list