Re: SQL help:

From: John W Higgins <wishdev(at)gmail(dot)com>
To: "Ray O'Donnell" <ray(at)rodonnell(dot)ie>
Cc: pgsql-general <pgsql-general(at)postgresql(dot)org>
Subject: Re: SQL help:
Date: 2026-08-11 23:51:11
Message-ID: CAPhAwGy-zYVcZjj8w=iprk6XCEsEehiaAWyLUiMwunHtqXkTFQ@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-general

Hey Ray,

On Tue, Aug 11, 2026 at 2:33 PM Ray O'Donnell <ray(at)rodonnell(dot)ie> 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

>

In response to

  • SQL help: at 2026-08-11 21:32:52 from Ray O'Donnell

Responses

Browse pgsql-general by date

  From Date Subject
Next Message Ray O'Donnell 2026-08-12 11:48:32 Re: SQL help:
Previous Message Brent Wood 2026-08-11 22:59:04 Re: SQL help: