0

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;
asked Aug 2, 2020 at 4:04

1 Answer 1

1

Use conditional aggregation:

SELECT u.id AS user_id, 
 sum(a.value) AS AS all_time
 sum(a.value) filter (au.inserted_at >= current_date - interval '3 months') as last_three_months 
FROM users u
 JOIN achievements_users au ON u.id = au.user_id 
 JOIN achievements a ON a.id = au.achievement_id
GROUP BY u.id;

You don't really need the users table though:

SELECT au.user_id, 
 sum(a.value) AS AS all_time
 sum(a.value) filter (au.inserted_at >= current_date - interval '3 months') as last_three_months 
FROM achievements_users au
 JOIN achievements a ON a.id = au.achievement_id
GROUP BY u.id;
answered Aug 2, 2020 at 6:55

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.