My question is similar to possible reference jsonb_array_length in a where clause, however I cannot use ORDER BY
.
I have following tables:
CREATE TABLE IF NOT EXISTS games
(
id SERIAL PRIMARY KEY,
...
)
CREATE TABLE IF NOT EXISTS users
(
id SERIAL PRIMARY KEY,
username CHARACTER VARYING(100) NOT NULL UNIQUE,
...
)
CREATE TABLE IF NOT EXISTS game_users
(
game_id INTEGER REFERENCES games (id) NOT NULL,
user_id INTEGER REFERENCES users (id) NOT NULL,
joined_at TIMESTAMPTZ NOT NULL,
UNIQUE (game_id, user_id)
);
I want to query games that have less than 4 participants.
SELECT gm.*,
array_agg(usr.id) AS users_id,
array_agg(gmUsr.joined_at) AS users_joined,
array_agg(usr.username) AS users_username
FROM games AS gm
LEFT JOIN game_users AS gmUsr ON gmUsr.game_id = gm.id
LEFT JOIN users AS usr ON gmUsr.user_id = usr.id
WHERE cardinality(array_length(users_id)) < 4
GROUP BY gm.id
I already saw in an answer that the WHERE
clause is executed before the SELECT one. So my query will not work.
However I want to keep GROUP BY
user id
because I want aggregated data as:
type GamesJoinedUsers = {
id: number,
users_id: number[],
users_joined: Date[],
users_username: string[],
}
How to query such data at least somehow?
Is it possible to do it in one query without subquery?
1 Answer 1
It seems to me you just need to replace your WHERE
clause with a HAVING
clause like this:
HAVING
COUNT(isr.id) < 4
The HAVING
clause is the appropriate place for a filter that needs to apply to an aggregated result. In this case, you want a filter on the number of users per game, which is an aggregate result given your GROUP BY
. Therefore, use HAVING
.
-
Thanks! This works. Using
HAVING
withcardinality(array_length(users_id))
still does not work. However the trick withCOUNT(usr.id)
is more elegant in this case!Alexey Vol– Alexey Vol2023年02月21日 10:26:14 +00:00Commented Feb 21, 2023 at 10:26
having
to apply filters on the result of aggregate functionsHAVING
also evaluated before the SELECT clause postgresqltutorial.com/postgresql-tutorial/postgresql-havingHAVING
is evaluated beforeSELECT
, this is part of the SQL standard. Here's a simple proof dbfiddle.uk/ZsF7NFLpHAVING
is the solution to the original question.