I have a couple of tables where I’m applying inner join to fetch matching rows:
event (id, url, name, eventStart, eventEnd)`
slot (id, event_id date, start_time, end_time,
location_type, location_id)`
Query:
select * from event as evnt
inner join slot as slot on evnt.id = slot.event_id
where location_type = 'type of location' and slot.location_id = '12345'
and slot.event_id = :eventId
This works fine. But sometimes a single event may have multiple slots. The result would be multiple rows for the same event. All column values in slot
are the same for the same event, except date, start_time, end_time
. I tried aggregating slot values like below, and this works fine:
select event_id,
array_agg(concat(date,':',start_time,':',end_time)) as slotDates
from slot
where event_id = 'event id' and location_id = '12345'
group by event_id
When I join to the main query, it gives me an error:
select event_id,
array_agg(concat(date,':',start_time,':',end_time)) as slotDates,
evnt.id as eventId, evnt.url as eventUrl,
evnt.name as eventName, evnt.event_start as eventStart,
evnt.event_end as eventEnd, slot.location_type as locationType
from event as evnt
inner join slot as slot on evnt.id = slot.event_id
where slot.location_type = 'type of location'
and slot.location_id = '12345'
and slot.event_id = 'event id goes here'
group by slot.event_id
ERROR: column "evnt.id" must appear in the GROUP BY clause or be used in an aggregate function
How to fix this?
3
Answers
The error occurred because many fields you used in the
SELECT
clause did not appear in theGROUP BY
clause. It is required because if a group has more than one row, it will not know which to keep and which to drop.One easy solution is to put everything in the
GROUP BY
clause. But I am totally against it. You can get relevant information by conducting a self-join.Schema (PostgreSQL v14)
Query
View on DB Fiddle
Or in case you need to get only one single aggregated column
I suggest a
LATERAL
subquery:db<>fiddle here
This always returns the event if its
id
exists. If there are no qualifying slots,locationtype
andslotdates
are NULL. That’s typically more practical.If you don’t want the event without slots, use
CROOS JOIN
instead ofLEFT JOIN
(and dropON true
). See:Either way, the point is to join after aggregating details, so we have no
GROUP BY
in the outerSELECT
to begin with, thereby avoiding your original problem – in cheaper fashion.