I found it really hard to come up with a descriptive problem title, so I’ll give a bit of context.
We have a list of boats and a list of docks. Boats can be transferred from one dock to the other on a specific datetime, or it could be at no dock at all. I’m trying to find out which boats are at a specific dock at a given moment in time.
Here’s a simplified SQL to illustrate the data model
CREATE TABLE `stackoverflow` (
`boat_id` int(11) NOT NULL,
`dock_id` int(11) DEFAULT NULL,
`startingAt` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `stackoverflow` (`boat_id`, `dock_id`, `startingAt`)
VALUES
(1, 1, '2020-01-01 00:00:00'),
(1, 2, '2020-02-01 00:00:00'),
(1, 3, '2020-03-01 00:00:00'),
(2, 2, '2020-01-01 00:00:00'),
(2, NULL, '2020-03-01 00:00:00');
The expected output for dock 1 and date 2020-01-15 would be 1
.
The expected output for dock 2 and date 2020-02-15 would be 1, 2
.
I’ve tried a lot of things including fancy joins but cannot get it to work. Any help is greatly appreciated!
Advertisement
Answer
You can use a correlated subquery to determine which boat is where at a given point in time. The rest is just filtering:
select s.*
from stackoverflow s
where s.startingAt = (select max(s2.startingAt)
from stackoverflow s2
where s2.boat_id = s.boat_id and
s2.startingAt <= '2020-02-15' -- the time you care about
) and
s.dock_id = 2;
You can also use window functions to assign a time period for each boat at each dock:
select s.*
from (select s.*,
lead(startingAt) over (partition by boat_id order by startingAt) as endingAt
from stackoverflow s
) s
where s.startingAt <= '2020-02-15' and
(s.endingAt > '2020-02-15' or s.endingAt is null) and
s.dock_id = 2;
Here is a db<>fiddle.