Hey Ray,

On Tue, Aug 11, 2026 at 2:33 PM Ray O'Donnell <[email protected]> wrote:

> Hi all,
>
> I need some help constructing a query...
>
> aircraft_reg |       slot_begin       |        slot_end        |
> booking_id | booking_priority | owner_uid
>
> --------------+------------------------+------------------------+------------+------------------+-----------
> EI-MCG       | 2026-08-22 09:00:00+01 | 2026-08-22 10:00:00+01 |
>        361 |                1 | rod
> EI-MCG       | 2026-08-22 10:00:00+01 | 2026-08-22 11:00:00+01 |
>        361 |                1 | rod
> EI-MCG       | 2026-08-22 11:00:00+01 | 2026-08-22 12:00:00+01 |
>        361 |                1 | rod
> EI-MCG       | 2026-08-22 12:00:00+01 | 2026-08-22 13:00:00+01 |
>        217 |                1 | jbloggs
> EI-MCG       | 2026-08-22 12:00:00+01 | 2026-08-22 13:00:00+01 |
>        361 |                2 | rod
> EI-MCG       | 2026-08-22 13:00:00+01 | 2026-08-22 14:00:00+01 |
>        217 |                1 | jbloggs
> EI-MCG       | 2026-08-22 13:00:00+01 | 2026-08-22 14:00:00+01 |
>        361 |                2 | rod
> EI-MCG       | 2026-08-22 14:00:00+01 | 2026-08-22 15:00:00+01 |
>        361 |                1 | rod
> EI-MCG       | 2026-08-22 15:00:00+01 | 2026-08-22 16:00:00+01 |
>        361 |                1 | rod
> EI-MCG       | 2026-08-22 16:00:00+01 | 2026-08-22 17:00:00+01 |
>        361 |                1 | rod
> EI-MCG       | 2026-08-22 17:00:00+01 | 2026-08-22 18:00:00+01 |
>        361 |                1 | rod
> EI-MCG       | 2026-08-22 18:00:00+01 | 2026-08-22 19:00:00+01 |
>        361 |                1 | rod
>
> So the key function for this work would be lag - it's a window function
which allows you to "look back" x rows (default 1) and see its data.

So lets start with a cte that tags new "groups"

with ordered as (SELECT *, lag(slot_end) over w,
           CASE
               WHEN slot_begin = lag(slot_end) OVER w
               THEN 0
               ELSE 1
           END AS new_group
    FROM slots
    WINDOW w AS (
        PARTITION BY aircraft_reg, booking_id, booking_priority, owner_uid
        ORDER BY slot_begin
    )
) select * from ordered

This will return all your rows with an additional column which indicates
whether or not this is a new group. So you use window to create groups of
records based on your 4 unique fields - order by slot_begin - then it walks
the rows and decides if the end time above it matches the start time and if
so it's not a new group. Obviously the first row for any set is a new group

Next up is placing each row in its group

so we start with our ordered cte above

, grouped AS (
    SELECT *,
           sum(new_group) OVER (
               PARTITION BY aircraft_reg, booking_id, booking_priority,
owner_uid
               ORDER BY slot_begin
           ) AS grp
    FROM ordered
) select * from grouped

This is sort of a trick but it's simple enough - create a window again
against all our unique fields and then the sum function will sum the
new_group field for any record up until the row in question. So every time
a new group was tagged the sum will go 1 higher for each subsequent row
until the next group is found and then it will increase again and so on and
so forth. Hard for me to explain - but the select * will show it nicely as
the last field.

Finally

SELECT
    aircraft_reg,
    min(slot_begin) AS slot_begin,
    max(slot_end) AS slot_end,
    booking_id,
    booking_priority,
    owner_uid
FROM grouped
GROUP BY
    aircraft_reg,
    booking_id,
    booking_priority,
    owner_uid,
    grp
ORDER BY slot_begin;

Simply pull your min and max for the begin and end based on your grp column.

Window functions are a very different beast but boy do they help when they
do!

Hope this moves you along.

John W Higgins

>

Reply via email to