My goal is to obtain for each user a total lifetime achievement points as well as points obtained in ranges, i.e. for the last three months.
I have this working but I'm curious if my solution will scale or if theirs a better way of thinking about the problem, in particular, I'm using Postgres so particular features in that DB might be a good solution for this.
Table Structure
Users:
------
id: int
AchievementsUser
----------------
id: int
user_id: int
achievement_id: int
inserted_at: DateTime
Achievement
----------------
id: int
value: int
Solution description
- Left join on the pivot table to get the achievements per user. This is built as a CTE (
base
in the SQL) - Produce a further CTE for each date range I'm interested in. These provide the filters, and sum operations (i.e. later, I could do averages if I wanted)
SQL
WITH base AS (
SELECT
users.id AS user_id,
achievements.value AS value,
achievements_users.inserted_at AS inserted_at
FROM
users,
achievements_users,
achievements
WHERE
users.id = achievements_users.user_id
AND achievements.id = achievements_users.achievement_id
),
all_time AS (
SELECT
base.user_id,
SUM(base.value)
FROM
base
GROUP BY
base.user_id
),
last_three_months AS (
SELECT
base.user_id,
SUM(base.value)
FROM
base
WHERE
base.inserted_at > CURRENT_DATE - INTERVAL '3 months'
GROUP BY
base.user_id
)
SELECT
all_time.user_id,
all_time.sum AS all_time,
last_three_months.sum AS last_three_months
FROM
all_time,
last_three_months
WHERE
all_time.user_id = last_three_months.user_id;
Best Answer
Use conditional aggregation:
You don't really need the
users
table though: