On which days of the week last week was sam the most active on the forum based on his liking habits, posting and reading?
On which days of the week last week was sam the most active on the forum based on his liking habits, posting and reading?
Let me look up the relevant schema information to help answer your question.
user_actions,user_visits
To determine which days of last week sam was most active based on likes, posts, and reads, we can query the user_actions and user_visits tables like this:
WITH last_week AS (
SELECT date_trunc('week', current_date - interval '1 week') AS start_of_week,
date_trunc('week', current_date - interval '1 week') + interval '6 days' AS end_of_week
),
likes AS (
SELECT date_trunc('day', ua.created_at) AS day, count(*) AS like_count
FROM user_actions ua
JOIN users u ON ua.user_id = u.id
WHERE u.username = 'sam'
AND ua.action_type = 1
AND ua.created_at >= (SELECT start_of_week FROM last_week)
AND ua.created_at <= (SELECT end_of_week FROM last_week)
GROUP BY date_trunc('day', ua.created_at)
),
posts AS (
SELECT date_trunc('day', ua.created_at) AS day, count(*) AS post_count
FROM user_actions ua
JOIN users u ON ua.acting_user_id = u.id
WHERE u.username = 'sam'
AND ua.action_type IN (4,5) -- 4 = create post, 5 = edit post
AND ua.created_at >= (SELECT start_of_week FROM last_week)
AND ua.created_at <= (SELECT end_of_week FROM last_week)
GROUP BY date_trunc('day', ua.created_at)
),
reads AS (
SELECT visited_at AS day, sum(posts_read) AS read_count
FROM user_visits
WHERE user_id = (SELECT id FROM users WHERE username = 'sam')
AND visited_at >= (SELECT start_of_week FROM last_week)
AND visited_at <= (SELECT end_of_week FROM last_week)
GROUP BY visited_at
)
SELECT
to_char(COALESCE(l.day, p.day, r.day), 'Day') AS day_of_week,
COALESCE(l.like_count, 0) AS likes,
COALESCE(p.post_count, 0) AS posts,
COALESCE(r.read_count, 0) AS reads
FROM likes l
FULL JOIN posts p ON l.day = p.day
FULL JOIN reads r ON COALESCE(l.day, p.day) = r.day
ORDER BY likes + posts + reads DESC
LIMIT 1
This query:
The key steps are aggregating the actions by day, joining the aggregates together, and ordering by the sum to find the max.
PG::UndefinedColumn: ERROR: column “likes” does not exist
LINE 47: ORDER BY likes + posts + reads DESC
Apologies for the error in the previous query. Let me fix that and provide an updated version:
WITH last_week AS (
SELECT date_trunc('week', current_date - interval '1 week') AS start_of_week,
date_trunc('week', current_date - interval '1 week') + interval '6 days' AS end_of_week
),
likes AS (
SELECT date_trunc('day', ua.created_at) AS day, count(*) AS like_count
FROM user_actions ua
JOIN users u ON ua.user_id = u.id
WHERE u.username = 'sam'
AND ua.action_type = 1
AND ua.created_at >= (SELECT start_of_week FROM last_week)
AND ua.created_at <= (SELECT end_of_week FROM last_week)
GROUP BY date_trunc('day', ua.created_at)
),
posts AS (
SELECT date_trunc('day', ua.created_at) AS day, count(*) AS post_count
FROM user_actions ua
JOIN users u ON ua.acting_user_id = u.id
WHERE u.username = 'sam'
AND ua.action_type IN (4,5) -- 4 = create post, 5 = edit post
AND ua.created_at >= (SELECT start_of_week FROM last_week)
AND ua.created_at <= (SELECT end_of_week FROM last_week)
GROUP BY date_trunc('day', ua.created_at)
),
reads AS (
SELECT visited_at AS day, sum(posts_read) AS read_count
FROM user_visits
WHERE user_id = (SELECT id FROM users WHERE username = 'sam')
AND visited_at >= (SELECT start_of_week FROM last_week)
AND visited_at <= (SELECT end_of_week FROM last_week)
GROUP BY visited_at
)
SELECT
to_char(COALESCE(l.day, p.day, r.day), 'Day') AS day_of_week,
COALESCE(l.like_count, 0) AS likes,
COALESCE(p.post_count, 0) AS posts,
COALESCE(r.read_count, 0) AS reads,
COALESCE(l.like_count, 0) + COALESCE(p.post_count, 0) + COALESCE(r.read_count, 0) AS total_activity
FROM likes l
FULL JOIN posts p ON l.day = p.day
FULL JOIN reads r ON COALESCE(l.day, p.day) = r.day
ORDER BY total_activity DESC
LIMIT 1
The main change is adding a total_activity
column that sums the likes
, posts
, and reads
columns. This is used in the ORDER BY
clause instead of referencing the columns directly.
The query still:
Let me know if you have any other questions!
Looking good, can you add a short representation of the date dd-mm and show all 7 days.
Absolutely! Here’s the updated query that includes a short date representation and shows all 7 days of last week:
WITH last_week AS (
SELECT date_trunc('week', current_date - interval '1 week') AS start_of_week,
generate_series(
date_trunc('week', current_date - interval '1 week'),
date_trunc('week', current_date - interval '1 week') + interval '6 days',
interval '1 day'
) AS day
),
likes AS (
SELECT date_trunc('day', ua.created_at) AS day, count(*) AS like_count
FROM user_actions ua
JOIN users u ON ua.user_id = u.id
WHERE u.username = 'sam'
AND ua.action_type = 1
AND ua.created_at >= (SELECT start_of_week FROM last_week)
AND ua.created_at <= (SELECT start_of_week + interval '6 days' FROM last_week)
GROUP BY date_trunc('day', ua.created_at)
),
posts AS (
SELECT date_trunc('day', ua.created_at) AS day, count(*) AS post_count
FROM user_actions ua
JOIN users u ON ua.acting_user_id = u.id
WHERE u.username = 'sam'
AND ua.action_type IN (4,5)
AND ua.created_at >= (SELECT start_of_week FROM last_week)
AND ua.created_at <= (SELECT start_of_week + interval '6 days' FROM last_week)
GROUP BY date_trunc('day', ua.created_at)
),
reads AS (
SELECT visited_at AS day, sum(posts_read) AS read_count
FROM user_visits
WHERE user_id = (SELECT id FROM users WHERE username = 'sam')
AND visited_at >= (SELECT start_of_week FROM last_week)
AND visited_at <= (SELECT start_of_week + interval '6 days' FROM last_week)
GROUP BY visited_at
)
SELECT
to_char(lw.day, 'DD-MM') AS date,
to_char(lw.day, 'Day') AS day_of_week,
COALESCE(l.like_count, 0) AS likes,
COALESCE(p.post_count, 0) AS posts,
COALESCE(r.read_count, 0) AS reads,
COALESCE(l.like_count, 0) + COALESCE(p.post_count, 0) + COALESCE(r.read_count, 0) AS total_activity
FROM last_week lw
LEFT JOIN likes l ON lw.day = l.day
LEFT JOIN posts p ON lw.day = p.day
LEFT JOIN reads r ON lw.day = r.day
ORDER BY lw.day
Changes made:
generate_series
to create a row for each day of last week in the last_week
CTELEFT JOIN
so all days are included even if there’s no activitydate
column with to_char(lw.day, 'DD-MM')
to show the short date representationday
column to show the days in chronological orderThe query now shows all 7 days of last week with the short date, full day name, likes, posts, reads, and total activity for each day, ordered chronologically.
Instead of using “user_actions” to get post counts can you simply use the posts table?
Sure, we can use the posts table directly to get the post counts. Here’s the updated query:
WITH last_week AS (
SELECT date_trunc('week', current_date - interval '1 week') AS start_of_week,
generate_series(
date_trunc('week', current_date - interval '1 week'),
date_trunc('week', current_date - interval '1 week') + interval '6 days',
interval '1 day'
) AS day
),
likes AS (
SELECT date_trunc('day', ua.created_at) AS day, count(*) AS like_count
FROM user_actions ua
JOIN users u ON ua.user_id = u.id
WHERE u.username = 'sam'
AND ua.action_type = 1
AND ua.created_at >= (SELECT start_of_week FROM last_week)
AND ua.created_at <= (SELECT start_of_week + interval '6 days' FROM last_week)
GROUP BY date_trunc('day', ua.created_at)
),
posts AS (
SELECT date_trunc('day', p.created_at) AS day, count(*) AS post_count
FROM posts p
JOIN users u ON p.user_id = u.id
WHERE u.username = 'sam'
AND p.created_at >= (SELECT start_of_week FROM last_week)
AND p.created_at <= (SELECT start_of_week + interval '6 days' FROM last_week)
GROUP BY date_trunc('day', p.created_at)
),
reads AS (
SELECT visited_at AS day, sum(posts_read) AS read_count
FROM user_visits
WHERE user_id = (SELECT id FROM users WHERE username = 'sam')
AND visited_at >= (SELECT start_of_week FROM last_week)
AND visited_at <= (SELECT start_of_week + interval '6 days' FROM last_week)
GROUP BY visited_at
)
SELECT
to_char(lw.day, 'DD-MM') AS date,
to_char(lw.day, 'Day') AS day_of_week,
COALESCE(l.like_count, 0) AS likes,
COALESCE(p.post_count, 0) AS posts,
COALESCE(r.read_count, 0) AS reads,
COALESCE(l.like_count, 0) + COALESCE(p.post_count, 0) + COALESCE(r.read_count, 0) AS total_activity
FROM last_week lw
LEFT JOIN likes l ON lw.day = l.day
LEFT JOIN posts p ON lw.day = p.day
LEFT JOIN reads r ON lw.day = r.day
ORDER BY lw.day
The main change is in the posts
CTE:
posts AS (
SELECT date_trunc('day', p.created_at) AS day, count(*) AS post_count
FROM posts p
JOIN users u ON p.user_id = u.id
WHERE u.username = 'sam'
AND p.created_at >= (SELECT start_of_week FROM last_week)
AND p.created_at <= (SELECT start_of_week + interval '6 days' FROM last_week)
GROUP BY date_trunc('day', p.created_at)
),
Instead of using the user_actions
table, we directly count the posts made by ‘sam’ from the posts
table.
The rest of the query remains the same, showing all 7 days of last week with the short date, full day name, likes, posts, reads, and total activity for each day, ordered chronologically.
PG::CardinalityViolation: ERROR: more than one row returned by a subquery used as an expression
Apologies for the oversight. The error is likely caused by the subquery used to get the user ID, as it’s possible for a username to match multiple users. Let’s modify the query to handle this:
WITH last_week AS (
SELECT date_trunc('week', current_date - interval '1 week') AS start_of_week,
generate_series(
date_trunc('week', current_date - interval '1 week'),
date_trunc('week', current_date - interval '1 week') + interval '6 days',
interval '1 day'
) AS day
),
sam_user AS (
SELECT id FROM users WHERE username = 'sam' LIMIT 1
),
likes AS (
SELECT date_trunc('day', ua.created_at) AS day, count(*) AS like_count
FROM user_actions ua
WHERE ua.user_id = (SELECT id FROM sam_user)
AND ua.action_type = 1
AND ua.created_at >= (SELECT start_of_week FROM last_week)
AND ua.created_at <= (SELECT start_of_week + interval '6 days' FROM last_week)
GROUP BY date_trunc('day', ua.created_at)
),
posts AS (
SELECT date_trunc('day', p.created_at) AS day, count(*) AS post_count
FROM posts p
WHERE p.user_id = (SELECT id FROM sam_user)
AND p.created_at >= (SELECT start_of_week FROM last_week)
AND p.created_at <= (SELECT start_of_week + interval '6 days' FROM last_week)
GROUP BY date_trunc('day', p.created_at)
),
reads AS (
SELECT visited_at AS day, sum(posts_read) AS read_count
FROM user_visits
WHERE user_id = (SELECT id FROM sam_user)
AND visited_at >= (SELECT start_of_week FROM last_week)
AND visited_at <= (SELECT start_of_week + interval '6 days' FROM last_week)
GROUP BY visited_at
)
SELECT
to_char(lw.day, 'DD-MM') AS date,
to_char(lw.day, 'Day') AS day_of_week,
COALESCE(l.like_count, 0) AS likes,
COALESCE(p.post_count, 0) AS posts,
COALESCE(r.read_count, 0) AS reads,
COALESCE(l.like_count, 0) + COALESCE(p.post_count, 0) + COALESCE(r.read_count, 0) AS total_activity
FROM last_week lw
LEFT JOIN likes l ON lw.day = l.day
LEFT JOIN posts p ON lw.day = p.day
LEFT JOIN reads r ON lw.day = r.day
ORDER BY lw.day
The main change is the introduction of the sam_user
CTE:
sam_user AS (
SELECT id FROM users WHERE username = 'sam' LIMIT 1
),
This CTE selects the ID of the user with username ‘sam’, limiting the result to 1 row. We then use this CTE in the other CTEs and the main query to get the user ID, avoiding the potential for multiple rows being returned.
The rest of the query remains the same, showing all 7 days of last week with the short date, full day name, likes, posts, reads, and total activity for each day, ordered chronologically.
still getting the same error. maybe it is something else?
I apologize for the confusion. Let’s try a different approach to isolate the issue. We’ll simplify the query and add each CTE one by one to identify the problematic subquery.
Let’s start with the last_week
and sam_user
CTEs:
WITH last_week AS (
SELECT date_trunc('week', current_date - interval '1 week') AS start_of_week,
generate_series(
date_trunc('week', current_date - interval '1 week'),
date_trunc('week', current_date - interval '1 week') + interval '6 days',
interval '1 day'
) AS day
),
sam_user AS (
SELECT id FROM users WHERE username = 'sam' LIMIT 1
)
SELECT * FROM last_week
If this runs without error, the issue is not with these CTEs. Let’s add the likes
CTE next:
WITH last_week AS (...),
sam_user AS (...),
likes AS (
SELECT date_trunc('day', ua.created_at) AS day, count(*) AS like_count
FROM user_actions ua
WHERE ua.user_id = (SELECT id FROM sam_user)
AND ua.action_type = 1
AND ua.created_at >= (SELECT start_of_week FROM last_week)
AND ua.created_at <= (SELECT start_of_week + interval '6 days' FROM last_week)
GROUP BY date_trunc('day', ua.created_at)
)
SELECT * FROM last_week
LEFT JOIN likes l ON lw.day = l.day
If the error occurs here, the issue is with the likes
CTE. If not, continue adding the posts
and reads
CTEs one by one in the same manner.
Once we identify the problematic CTE, we can focus on fixing the subquery in that specific CTE.
If the error persists even with the simplified query, please provide the exact error message, and I’ll be happy to investigate further.
first query runs fine, but all of this trouble stared when you added posts, it has to be that no?
You’re right, the issue likely lies within the posts
CTE. Let’s take a closer look at it:
posts AS (
SELECT date_trunc('day', p.created_at) AS day, count(*) AS post_count
FROM posts p
WHERE p.user_id = (SELECT id FROM sam_user)
AND p.created_at >= (SELECT start_of_week FROM last_week)
AND p.created_at <= (SELECT start_of_week + interval '6 days' FROM last_week)
GROUP BY date_trunc('day', p.created_at)
)
The subquery (SELECT start_of_week FROM last_week)
is likely returning multiple rows because last_week
generates a series of dates. To fix this, we can use the MIN
aggregate function to ensure only one row is returned:
posts AS (
SELECT date_trunc('day', p.created_at) AS day, count(*) AS post_count
FROM posts p
WHERE p.user_id = (SELECT id FROM sam_user)
AND p.created_at >= (SELECT MIN(day) FROM last_week)
AND p.created_at <= (SELECT MIN(day) + interval '6 days' FROM last_week)
GROUP BY date_trunc('day', p.created_at)
)
Here’s the full updated query:
WITH last_week AS (
SELECT date_trunc('week', current_date - interval '1 week') AS start_of_week,
generate_series(
date_trunc('week', current_date - interval '1 week'),
date_trunc('week', current_date - interval '1 week') + interval '6 days',
interval '1 day'
) AS day
),
sam_user AS (
SELECT id FROM users WHERE username = 'sam' LIMIT 1
),
likes AS (
SELECT date_trunc('day', ua.created_at) AS day, count(*) AS like_count
FROM user_actions ua
WHERE ua.user_id = (SELECT id FROM sam_user)
AND ua.action_type = 1
AND ua.created_at >= (SELECT MIN(day) FROM last_week)
AND ua.created_at <= (SELECT MIN(day) + interval '6 days' FROM last_week)
GROUP BY date_trunc('day', ua.created_at)
),
posts AS (
SELECT date_trunc('day', p.created_at) AS day, count(*) AS post_count
FROM posts p
WHERE p.user_id = (SELECT id FROM sam_user)
AND p.created_at >= (SELECT MIN(day) FROM last_week)
AND p.created_at <= (SELECT MIN(day) + interval '6 days' FROM last_week)
GROUP BY date_trunc('day', p.created_at)
),
reads AS (
SELECT visited_at AS day, sum(posts_read) AS read_count
FROM user_visits
WHERE user_id = (SELECT id FROM sam_user)
AND visited_at >= (SELECT MIN(day) FROM last_week)
AND visited_at <= (SELECT MIN(day) + interval '6 days' FROM last_week)
GROUP BY visited_at
)
SELECT
to_char(lw.day, 'DD-MM') AS date,
to_char(lw.day, 'Day') AS day_of_week,
COALESCE(l.like_count, 0) AS likes,
COALESCE(p.post_count, 0) AS posts,
COALESCE(r.read_count, 0) AS reads,
COALESCE(l.like_count, 0) + COALESCE(p.post_count, 0) + COALESCE(r.read_count, 0) AS total_activity
FROM last_week lw
LEFT JOIN likes l ON lw.day = l.day
LEFT JOIN posts p ON lw.day = p.day
LEFT JOIN reads r ON lw.day = r.day
ORDER BY lw.day
This should resolve the PG::CardinalityViolation
error. Let me know if you encounter any further issues.
looking good, but my post counts and likes on sunday are zero, I remember liking stuff on sunday are the bounds correct?
Apologies for the confusion. You’re right, the bounds might be off. Let’s adjust the query to ensure it includes the entire week, from Monday to Sunday.
Here’s the updated query:
WITH last_week AS (
SELECT date_trunc('week', current_date - interval '1 week') AS start_of_week,
generate_series(
date_trunc('week', current_date - interval '1 week'),
date_trunc('week', current_date - interval '1 week') + interval '6 days',
interval '1 day'
) AS day
),
sam_user AS (
SELECT id FROM users WHERE username = 'sam' LIMIT 1
),
likes AS (
SELECT date_trunc('day', ua.created_at) AS day, count(*) AS like_count
FROM user_actions ua
WHERE ua.user_id = (SELECT id FROM sam_user)
AND ua.action_type = 1
AND ua.created_at >= (SELECT MIN(day) FROM last_week)
AND ua.created_at < (SELECT MIN(day) FROM last_week) + interval '7 days'
GROUP BY date_trunc('day', ua.created_at)
),
posts AS (
SELECT date_trunc('day', p.created_at) AS day, count(*) AS post_count
FROM posts p
WHERE p.user_id = (SELECT id FROM sam_user)
AND p.created_at >= (SELECT MIN(day) FROM last_week)
AND p.created_at < (SELECT MIN(day) FROM last_week) + interval '7 days'
GROUP BY date_trunc('day', p.created_at)
),
reads AS (
SELECT visited_at AS day, sum(posts_read) AS read_count
FROM user_visits
WHERE user_id = (SELECT id FROM sam_user)
AND visited_at >= (SELECT MIN(day) FROM last_week)
AND visited_at < (SELECT MIN(day) FROM last_week) + interval '7 days'
GROUP BY visited_at
)
SELECT
to_char(lw.day, 'DD-MM') AS date,
to_char(lw.day, 'Day') AS day_of_week,
COALESCE(l.like_count, 0) AS likes,
COALESCE(p.post_count, 0) AS posts,
COALESCE(r.read_count, 0) AS reads,
COALESCE(l.like_count, 0) + COALESCE(p.post_count, 0) + COALESCE(r.read_count, 0) AS total_activity
FROM last_week lw
LEFT JOIN likes l ON lw.day = l.day
LEFT JOIN posts p ON lw.day = p.day
LEFT JOIN reads r ON lw.day = r.day
ORDER BY lw.day
The main changes are in the likes
, posts
, and reads
CTEs. Instead of using <= (SELECT MIN(day) + interval '6 days' FROM last_week)
, we now use < (SELECT MIN(day) FROM last_week) + interval '7 days'
. This ensures that the entire week is included, up to but not including the start of the following week.
This adjustment should include your activity from Sunday in the results.