Find out if user exists in a sum when joining multiple tables - mysql

I've got 3 tables:
submissions, submissions_votes, and users. My current query:
SELECT s.*, u.username, u.avatar, SUM(sv.up) helpfulVotes
FROM submissions s
INNER JOIN users u
ON s.user_id = u.id
LEFT JOIN submissions_votes sv
ON s.id = sv.submission_id
WHERE s.id = 23
GROUP BY s.id
Will return for me the submission details, the user who submitted it, and the amount of helpfulVotes on the submission (which comes from the submissions_votes table).
This works fine, but what I'd like to do is test for a condition within the SUM(sv.up) to see if the any of the users within that sum matches a particular user_id from submissions.
I was thinking of doing something like this but it doesn't work:
SELECT s.*, u.username, u.avatar, SUM(sv.up) helpfulVotes,
SUM (IF(sv.user_id = 15, count, 1)) as currentUserVoted, <---- ???
FROM submissions s
INNER JOIN users u
ON s.user_id = u.id
LEFT JOIN submissions_votes sv
ON s.id = sv.submission_id
WHERE s.id = 23
GROUP BY s.id
How can I see if there is a match within that sum to the current user's id (in this case, 15)?

Yes you can use condition in SUM() just like SUM (sv.user_id = 15) will give you the count for user 15
SELECT
s.*,
u.username,
u.avatar,
SUM(sv.up) helpfulVotes,
SUM(sv.user_id = 15) AS currentUserVoted
FROM
submissions s
INNER JOIN users u
ON s.user_id = u.id
LEFT JOIN submissions_votes sv
ON s.id = sv.submission_id
WHERE s.id = 23
GROUP BY s.id
or if you want to sum another column based on user id you can use CASE
SUM (CASE WHEN sv.user_id = 15 THEN sum_other_col ELSE 0 END) as currentUserVoted

Related

MySQL: Needing to return top 3 Users with the most votes. Results wanted in one column from the SUM of two subqueries. Java/Spring MVC

I have a Spring MVC blog with functionality for Post and Comment voting. I want to return the top 3 users based on number of votes they've received on all their posts and comments.
tables:
users u [id, username]
posts p [id, u.id]
comments c [id, p.id, u.id]
post_votes pv [p.id, u.id, type (1 or -1)]
comment_votes cv [c.id, u.id, type (1 or -1)]
The following statement gives me total votes per user by querying two separate voting tables and then adding the totals together:
SELECT
(SELECT SUM(type)
FROM posts_votes pv
JOIN posts p ON p.id = pv.post_id
JOIN users u ON u.id = p.user_id
WHERE u.id LIKE ?1)
+
(SELECT SUM(type)
FROM comments_votes cv
JOIN comments c ON c.id = cv.comment_id
JOIN users u ON u.id = c.user_id
WHERE u.id LIKE ?1)
That works fine with a WHERE clause per user id... But now I'm trying to find just the top 3 users that have the most votes and I'm having too much difficulty. This is what I have so far:
SELECT u.id, u.username, IFNULL(SUM(pv.type), 0) AS totalPostVotes
FROM posts_votes pv
JOIN posts p ON p.id = pv.post_id
JOIN users u ON u.id = p.user_id
GROUP BY u.id ORDER BY totalPostVotes DESC LIMIT 3
That above statement works by itself giving me: u.id, u.username, and totalPostVote in descending order. So does the one below for comments:
SELECT u.id, u.username, IFNULL(SUM(cv.type), 0) AS totalCommentVotes
FROM comment_votes cv
JOIN comments c ON c.id = cv.comment_id
JOIN users u ON u.id = c.user_id
GROUP BY u.id ORDER BY totalCommentVotes DESC LIMIT 3
Great! But I want that third column SUM result to be essentially "totalVotes" and contain the sum of both of those subqueries. Then I'll GROUP BY u.id ORDER BY totalVotes DESC LIMIT 3.
Something like this:
SELECT u.id, u.username, SUM(
(SELECT IFNULL(SUM(pv.type), 0) AS totalPostVotes
FROM posts_votes pv
JOIN posts p ON p.id = pv.post_id
JOIN users u ON u.id = p.user_id
GROUP BY u.id ORDER BY totalPostVotes DESC LIMIT 1)
+
(SELECT IFNULL(SUM(cv.type), 0) AS totalCommentVotes
FROM comments_votes cv
JOIN comments c ON c.id = cv.comment_id
JOIN users u ON u.id = c.user_id
GROUP BY u.id ORDER BY totalCommentVotes DESC LIMIT 1))
AS totalVotes from users u
GROUP BY u.id, u.username ORDER BY totalVotes DESC LIMIT 3
id | username | totalVotes
2 user2 11
1 user1 11
29 user29 11
What's happening is the result of totalVotes is indeed the correct vote count, 11, for the "top" user, but none of those users are the real top user, and the correct vote is being repeated 3 times in the guise of other users. I'm not even sure how users are being sorted at that point because they're not in an order I recognize.
The subqueries work separately (they give me the correct user) when I add SELECT "u.id, u.username " IFNULL(SUM()) but then if I run the whole block, I get the error "Operand should contain 1 column(s)" So I delete them and revert to only SELECT IFNULL(SUM())
I'm also noticing the subqueries are only allowed LIMIT 1. How would I get the top 3, then? Should I do a UNION somewhere or is "+" sufficient? This is rather confusing. Can someone please help me with this? Any help is appreciated. Thanks in advance!
Updated code, thank you Peter:
SELECT
u.username,
pv_sum.total AS postTotal,
cv_sum.total AS commentTotal,
IFNULL(pv_sum.total, 0) + IFNULL(cv_sum.total, 0) as totalVotes
FROM users u
LEFT JOIN (
SELECT p.user_id, IFNULL(SUM(pv.type), 0) AS total
FROM posts p
JOIN posts_votes pv ON pv.post_id = p.id
GROUP BY p.user_id
) pv_sum ON pv_sum.user_id = u.id
LEFT JOIN (
SELECT c.user_id, IFNULL(SUM(cv.type), 0) AS total
FROM comments c
JOIN comments_votes cv ON cv.comment_id = c.id
GROUP BY c.user_id
) cv_sum ON cv_sum.user_id = u.id
GROUP BY u.username, postTotal, commentTotal
ORDER BY totalVotes DESC LIMIT 3;
Don't place your subqueries in your SELECT-part, but join them on the users-table:
SELECT
u.username,
pv_sum.total AS postTotal,
cv_sum.total as commentTotal,
IFNULL(pv_sum.total, 0) + IFNULL(cv_sum.total, 0) as totalVotes
FROM users u
LEFT JOIN (
SELECT p.user_id, IFNULL(SUM(pv.type), 0) AS total
FROM posts p
JOIN post_votes pv ON pv.post_id = p.id
GROUP BY p.user_id
) pv_sum ON pv_sum.user_id = u.id
LEFT JOIN (
SELECT c.user_id, IFNULL(SUM(cv.type), 0) AS total
FROM comments c
JOIN comment_votes cv ON cv.comment_id = c.id
GROUP BY c.user_id
) cv_sum ON cv_sum.user_id = u.id
GROUP BY u.id
ORDER BY totalVotes DESC
LIMIT 3;
Fiddle: http://sqlfiddle.com/#!9/980cb2/11

Merge query with two different WHERE clauses/conditions in one

I have three tables: users, accounts and scores. Each query is actually giving me the desired results:
-- This will return all user ids with a count of "calculated" scores
SELECT u.id AS user_id, count(1) AS total FROM scores s
INNER JOIN accounts a ON s.account_id = a.id
INNER JOIN user u ON a.user_id = u.id
WHERE s.status = 'CALCULATED'
GROUP BY user_id;
-- This will return all user ids with a count of non-calculated scores
SELECT u.id AS user_id, count(1) AS failures FROM scores s
INNER JOIN accounts a ON s.account_id = a.id
INNER JOIN user u ON a.user_id = u.id
WHERE s.status <> 'CALCULATED'
GROUP BY user_id;
But I would like to return something like this: user id, total, failures...all in one query!
This can be done with conditional aggregation. Conditions in SUM return 1 or 0 depending on the condition being satisfied.
SELECT u.id AS user_id,
SUM(s.status='CALCULATED'),
SUM(s.status<>'CALCULATED') AS total
FROM scores s
INNER JOIN accounts a ON s.account_id = a.id
INNER JOIN user u ON a.user_id = u.id
GROUP BY u.id;
As a note, you can simplify your query, because the user table is (presumably) not needed:
SELECT a.user_id,
SUM(s.status = 'CALCULATED') as num_calc,
SUM(s.status <> 'CALCULATED') AS num_notcalc
FROM scores s INNER JOIN
accounts a
ON s.account_id = a.id
GROUP BY a.user_id;
Your queries are giving the right answer, but you might also need to be careful about NULL values. If that's a concern, use <=> the NULL-safe equality operator:
SELECT a.user_id,
SUM(s.status = 'CALCULATED') as num_calc,
SUM(NOT s.status <=> 'CALCULATED') AS num_notcalc
FROM scores s INNER JOIN
accounts a
ON s.account_id = a.id
GROUP BY a.user_id;

Sub query within SELECT statement always returning NULL

I am trying to write an SQL SELECT statement with a sub query. There is no error returned but I don't get the results I am expecting. The value for r.related is always NULL.
SELECT
l.id,
u.id as user_id,
u.name,
r.related
FROM
list l
INNER JOIN user u ON u.id = l.user_id
LEFT JOIN (
SELECT COUNT(u.id) AS related, b.group_id
FROM user u
INNER JOIN booking b ON b.user_id = u.id
WHERE u.id != l.user_id
AND b. = 0) AS r ON r.group_id = l.group_id
WHERE
l.group_id = 22
GROUP BY l.id, u.id
ORDER BY l.id
I am writing the sub query correctly?
Here's the problem:
SELECT COUNT(u.id) AS related, b.group_id
FROM user u
INNER JOIN booking b ON b.user_id = u.id
WHERE u.id != b.user_id
AND b. = 0
Look, you are joining user and booking table on booking.user_id = user.id
and
then you are just discarding those matching rows between these two tables in your where condition WHERE user.id != booking.user_id;
It's more like you are looking the differences between Set A and Set B in A intersection B. So in this case you won't find any (i.e. NULL).

Sql count returns wrong numbers

i have an api which i make calls to and i need alot of data from different tables so i use joins on them, now the problem is that whilst i have 4 replies, and 5 interactions the data always returns 20 replies and 20 interactions this is the result:
screen_name screen_state replies interactions alerts
sjerd 0 20 20 0
i use this query to count the records and results:
SELECT u.screen_name,
u.screen_state,
count(r.id) AS replies,
count(i.id) AS interactions,
count(a.alerts) AS alerts
FROM users u
LEFT JOIN reply r ON u.id = r.user
LEFT JOIN interactions i ON u.id = i.user_id
LEFT JOIN alerts a ON u.id = a.user_id WHERE u.id ='2'
GROUP BY u.id, u.screen_state
can someone see why it's returning 20 while i only have 7 rows of replies in total in reply table,
and 5 rows of interactions in total in interaction table.
each row is 1 reaction or reply.
Your counts are always going to give the same result as all tables are joined at the same level.
You need to do your counts as inline sub-queries (or whatever - I can never remember the correct terminology):
SELECT u.screen_name,
u.screen_state,
(select count(*) from reply r where u.id = r.user) AS replies,
(select count(*) from interactions i where u.id = i.user_id) AS interactions,
(select count(*) from alerts a where u.id = a.user_id) AS alerts
FROM users u
WHERE u.id ='2'
SELECT u.screen_name, u.screen_state,
count(DISTINCT r.id) AS replies,
count(DISTINCT i.id) AS interactions,
count(DISTINCT a.alerts) AS alerts
FROM users u
LEFT JOIN reply r ON u.id = r.user
LEFT JOIN interactions i ON u.id = i.user_id
LEFT JOIN alerts a ON u.id = a.user_id WHERE u.id ='2'
GROUP BY u.id, u.screen_state

msql wrong total and sum on result

We have three different following tables...
users
id,
username
password
email
user_clubs
id,
user_id,
club_name
sales
id,
club_id,
amount,
admin_fees,
dnt
And we want to get total sum of admin_fees as outstanding for user_id(for example 55), so we tried following...
SELECT u.id, count(c.id), SUM(s.admin_fees) as total_admin_fees
FROM users u
LEFT JOIN user_clubs c ON c.user_id = u.id
LEFT JOIN sales s ON s.club_id = c.id
WHERE u.id = 55
GROUP BY u.id;
But its returning value of first row, so balance is not correct, Please help to resolve.
Try this:
SELECT g.id, count(g.club_id), SUM(g.admin_fees) AS total_admin_fees
FROM (
SELECT u.id, c.id AS club_id, s.admin_fees
FROM users u
LEFT JOIN user_clubs c ON c.user_id = u.id
LEFT JOIN sales s ON s.club_id = c.id
WHERE u.id = 55
) AS g
GROUP BY g.id;