MySQL SUM of a column - mysql

I wish to return a users details including the total number of votes a user has for all their events.
I have the following query which returns a list of users events and the number of events they have.
SELECT u.user_uid, u.firstname, u.lastname,
( SELECT COUNT(ev.event_vote_id)
FROM event_vote ev
WHERE ue.event_uid = ev.event_uid
) AS votes,
( SELECT COUNT(ue.user_event_id)
FROM user_event ue
WHERE ue.user_uid = u.user_uid
) AS no_of_events
FROM user_event ue
JOIN user u
ON u.user_uid = '1'
WHERE ue.user_uid = '1'
But it returns a record for every event they have and I need a SUM of the votes column.
USER
| USER_UID | FIRSTNAME | LASTNAME |
1 bob smith
2 rob smithies
3 john clark
EVENT
| GUID | NAME |
101 event1
102 event2
103 event3
USER_EVENT
| USER_EVENT_ID | USER_UID | EVENT_UID |
1001 1 101
1002 2 102
1003 1 103
EVENT_VOTE
| EVENT_VOTE_ID | USER_UID | EVENT_UID |
2001 2 101
2002 3 101
2003 2 103
Expected Result
user_uid: 1
firstname: bob
lastname: smith
votes: 3 // 2 for 101, 1 for 103.
no_of_events: 2

You can either join all tables and aggregate then:
select
u.user_uid,
u.firstname,
u.lastname,
count(*) as votes,
count(distinct event_uid) as events
from user u
join user_event ue on ue.user_uid = u.user_uid
join event_vote ev on ev.event_uid = ue.event_uid
group by u.user_uid;
Or aggregate first and join then:
select
u.user_uid,
u.firstname,
u.lastname,
sums.votes,
sums.events
from user u
left join
(
select
ue.user_uid,
count(*) as votes,
count(distinct ev.event_uid) as events
from user_event ue
left join event_vote ev on ev.event_uid = ue.event_uid
group by ue.user_uid
) sums on sums.user_uid = u.user_uid
If you want to select users without events or without votes, too, you need outer joins and count(ev.event_vote_id) instead of count(*).

Related

Get list of groups user hasn't Joined, List of groups user didn't create

The user (Ben) has joined group 2 and group 3. How can I write this in a select query... I want to select from groups I haven't joined and groups I didn't create.
users_tbl table
user_id username
| 1 | ben
| 2 | betty
| 3 | tim
| 4 | jimmy
| 5 | sammy
user_groups table
user_id group_id
| 1 | 2
| 1 | 3
group_tbl table
group_id user_id
| 1 | 5
| 2 | 4
| 3 | 5
I am able to get the list of groups I didn't create using this query...
SELECT * FROM group_tbl LEFT JOIN users_tbl ON users_tbl.user_id = group_tbl.user_id WHERE group_tbl.user_id != ? ORDER BY RAND() LIMIT 10
How can I get the list of groups users hasn't joined?
You can do it if you do a LEFT join of group_tbl to users_tbl and return the unmatched rows of group_tbl:
SELECT g.*
FROM group_tbl g LEFT JOIN user_groups u
ON u.group_id = g.group_id AND u.user_id = 1
WHERE u.user_id IS NULL
Or with NOT EXISTS:
SELECT g.*
FROM group_tbl g
WHERE NOT EXISTS (
SELECT 1
FROM user_groups u
WHERE u.group_id = g.group_id AND u.user_id = 1
)
See the demo.
Results:
group_id
user_id
1
5

SUM two columns of two different tables in one result based group by id of another table

I have 3 tables:
Users:
id | account_name
-------------------|----------------------|
18 | panic |
Deposits:
id | user_id | amount
-------------------|---------------------------|
1 | 18 | 100
2 | 18 | 100
Withdrawals:
id | user_id | amount
------------------------|--------------------------------|
1 | 18 | 200
2 | 18 | 200
and i'm trying to get a result like:
id | totalDeposits | totalWithdraws
------------------------|---------------------------|
18 | 200 | 400
Now when i try to get the totals for some reason they are cross adding themselves up, of course if there are no rows it should return 0.
SELECT t0.id,IFNULL(SUM(t1.amount),0) AS totalWithdrawals,
IFNULL(SUM(t2.amount),0) AS totalDeposits
FROM users t0
LEFT OUTER JOIN withdrawals t1 ON (t0.id = t1.user_id)
LEFT OUTER JOIN deposits t2 ON (t0.id = t2.user_id)
GROUP BY t0.id
Any idea how to do this cross join or where am i summing them wrong?
Try this-
SELECT A.id,
(SELECT SUM(amount) FROM Deposits WHERE user_id = A.id) totalDeposits,
(SELECT SUM(amount) FROM Withdrawals WHERE user_id = A.id) totalWithdraws
FROM users A
WHERE A.id = 18 -- WHERE can be removed to get all users details
You can try something along the lines of
SELECT u.id,
COALESCE(d.amount, 0) totalDeposits,
COALESCE(w.amount, 0) totalWithdrawals
FROM users u
LEFT JOIN (
SELECT user_id, SUM(amount) amount
FROM deposits
GROUP BY user_id
) d ON u.id = d.user_id
LEFT JOIN (
SELECT user_id, SUM(amount) amount
FROM withdrawals
GROUP BY user_id
) w ON u.id = w.user_id
SQLFiddle
Result:
| id | totalDeposits | totalWithdrawals |
|----|---------------|------------------|
| 18 | 200 | 400 |
The problem is that you are generating a Cartesian product. One solution is to aggregate first. Another method is to use UNION ALL and GROUP BY. I would structure this as:
SELECT u.id,
SUM(deposit) as deposits,
SUM(withdrawal) as withdrawal
FROM users u LEFT JOIN
((SELECT d.user_id, d.amount as deposit, 0 as withdrawal
FROM deposits d
) UNION ALL
(SELECT w.user_id, 0, w.amount
FROM withdrawals w
)
) dw
ON u.id = dw.user_id
GROUP BY u.id;

mysql select statement with multiple where/conditions

I have the following two tables in mysql:
users:
+--------+-----------+
| userId | userName |
+--------+-----------+
| 1 | magnus |
| 2 | fabiano |
| 3 | alexander |
| 4 | veselin |
+--------+-----------+
games:
+--------+---------+---------+
| gameId | userId1 | userId2 |
+--------+---------+---------+
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 2 | 3 |
| 4 | 2 | 4 |
+--------+---------+---------+
How can I construct a single query such that I get this below output of say, fabiano's opponents:
output:
+--------+-----------+
| gameId | userName |
+--------+-----------+
| 1 | magnus |
| 3 | alexander |
| 4 | veselin |
+--------+-----------+
Edit1:
This was what I was trying and I wasn't able to get them into a single query:
select fabiano's opponents [select * from games where 2 in (userId1, userId2);]
read each of the rows, and check which of them is fabiano(2), and select the other userId
from the userIds of these opponents, get their name from users table
Edit2:
Inspired by the answers below, I wrote this (they work):
-- NO JOIN
select x.gameId, users.userName from
(
select gameId, userId2 as id from games where userId1=2
UNION
select gameId, userId1 as id from games where userId2=2
) as x, users
where users.userId = id;
-- NO JOIN, NO UNION
select x.gameId, users.userName from (
SELECT g.gameId,
CASE WHEN userId1 = 2
THEN userId2
WHEN userId2 =2
THEN userId1
END AS id
FROM games g) as x, users
where users.userId = id;
You can union the two sets of data together, viz all games where Fabiano is User 1, with all games that he is in the role of User 2:
SELECT x.Opponent
FROM
(
SELECT u.Name AS Opponent
FROM games g
INNER JOIN users u
ON g.userId2 = u.UserId
WHERE g.UserId1 = 2 -- Fabiano
UNION
SELECT u.Name
FROM games g
INNER JOIN users u
ON g.userId1 = u.UserId
WHERE g.UserId2 = 2 -- Fabiano
) AS x;
At this point as assume that Fabiano can't simultaneously both be User1 and User2, as we would need to consider UNION ALL vs UNION DISTINCT :)
This could also be tidied up a bit into:
SELECT x.Opponent
FROM
(
SELECT u.Name AS Opponent, g.UserId1 AS PlayerId
FROM games g
INNER JOIN users u
ON g.userId2 = u.UserId
UNION
SELECT u.Name, g.UserId2 AS PlayerId
FROM games g
INNER JOIN users u
ON g.userId1 = u.UserId
) AS x
WHERE x.PlayerId = 2; -- Fabiano
Try something like:
SELECT `gamess`.gameId, `users`.userName
FROM users INNER JOIN
(SELECT gameId, userId2 as userId
FROM games
WHERE userId1 = 2
UNION
SELECT gameId, userId1 as userId
FROM games
WHERE userId2 = 2) AS gamess
ON `gamess`.userId = `users`.userId
Doing this without a UNION clause will make it more performant
SELECT g.gameid,
CASE WHEN u1.userid = 2 -- fabino*
THEN u2.username
else u1.username END AS Opponent
FROM games g
LEFT JOIN users u1
ON g.userId1 = u1.UserId
LEFT JOIN users u2
on g.userid2 = u2.userid
WHERE (g.UserId1 = 2 OR g.userid2 = 2) -- fabino
SELECT us.*
FROM users us
INNER JOIN games gs ON us.userId = gs.userId1
GROUP BY us.userId ,us.userName
This query should be works all of common userid
SELECT x.Opponent
FROM
(
SELECT u.userName AS Opponent
FROM games g
INNER JOIN users u
ON g.userId2 = u.UserId
WHERE g.UserId1 in (select UserId from users where UserId in (select userid1 from games))
UNION
SELECT u.userName
FROM games g
INNER JOIN users u
ON g.userId1 = u.UserId
WHERE g.UserId2 in (select UserId from users where UserId in (select userid2 from games))
) AS x;

SQL join with aggregated data

I have two SQL tables.
USERS
ID | USERNAME
001 | Tom
002 | Jane
003 | Peter
004 | Mariah
PRODUCTS
ID | PR_NAME | USERID
01 | Apple | 002
02 | Pear | 001
03 | Tomato | 002
04 | Apple | 003
05 | Tomato | 001
06 | Apple | 001
If I select "Apple" in a form/list/app I want to see who bought "Apple" AND how many products those persons bought:
USERID | USERNAME | NUM_PRODUCTS_BOUGHT
001 | Tom | 3
002 | Jane | 2
003 | Peter | 1
What I tried is
SELECT
USERS.ID, USERS.USERNAME,
COUNT(PRODUCTS.PR_NAME) AS NUM_PRODUCTS_BOUGHT
FROM
USERS
LEFT JOIN
PRODUCTS ON PRODUCTS.USERID = USERS.ID
WHERE
PRODUCTS.PR_NAME = "Apple"
GROUP BY
USERS.ID
but it gives me wrong numbers from NUM_ALL (1) like
ID | USERNAME | NUM_PRODUCTS_BOUGHT
001 | Tom | 1
002 | Jane | 1
003 | Peter | 1
I can get this result in 3 ways but cannot get what I want.
I think you should try a having clause. I haven't tested this, but it should work:
SELECT
USERS.ID, USERS.USERNAME,
COUNT(PRODUCTS.PR_NAME) AS NUM_PRODUCTS_BOUGHT
FROM
USERS
LEFT JOIN
PRODUCTS ON PRODUCTS.USERID = USERS.ID
GROUP BY
USERS.ID
HAVING
SUM(CASE WHEN PRODUCTS.PR_NAME = "Apple" THEN 1 ELSE 0 END) > 0
If the problem is that you are missing "Mariah", this is because the where clause is undoing the left outer join. Move the condition to the on clause:
SELECT u.ID, u.USERNAME, COUNT(p.PR_NAME) AS NUM_PRODUCTS_BOUGHT
FROM USERS u LEFT JOIN
PRODUCTS p
ON p.USERID = u.ID AND
p.PR_NAME = 'Apple'
GROUP BY u.ID;
NOTE: In some databases, you would need to include u.USERNAME in the group by clause. However, assuming that USERS.ID is unique, this usage is consistent with the ANSI standard.
You can think about it in layers...
First, you have a product
--q1
select * from PRODUCTS where PR_NAME = "Apple"
Then you want to know users that brought that product
--q2
select * from users where
id in (
--q1
select USERID from PRODUCTS where PR_NAME = "Apple"
)
Now for a user you want to know how many products he/she bought
--q3
select userid, count(id) cnt from PRODUCT group by userid
And finally you want to combine output from q2 and q3:
select u.id, u.USERNAME, cnt.cnt from users u inner join
( --q3
select userid, count(id) cnt from PRODUCT group by userid
) cnt on u.ID = cnt.userid
where
u.id in (
--q1
select USERID from PRODUCTS where PR_NAME = "Apple"
)
optimization:
select u.id, u.USERNAME, cnt.cnt from users u inner join
( --q3
select userid, count(id) cnt from PRODUCT group by userid
) cnt on u.ID = cnt.userid
inner join product p on u.id = p.userid
where
p.pr_name = 'Apple'
Try this:
SELECT U.ID, U.USERNAME, COUNT(P2.PR_NAME) NUM_PRODUCTS_BOUGHT
FROM USERS U
JOIN PRODUCTS P1
ON U.ID = P1.USERID
JOIN PRODUCTS P2
ON P1.USERID = P2.USERID
WHERE P1.PR_NAME = 'Apple'
GROUP BY U.ID, U.USERNAME
Here is the SQL FIDDLE for SQL Server.

LEFT JOIN 3 columns to get username

I have three columns I need to join which comes from 3 different tables,
Contributions table:
+-----------+---------------------+
| record_id | contributor_user_id |
+-----------+---------------------+
| 1 | 2 |
+-----------+---------------------+
| 1 | 5 |
+-----------+---------------------+
Members table:
+--------------+---------+
| username | user_id |
+--------------+---------+
| Test | 1 |
+--------------+---------+
| Test2 | 5 |
+--------------+---------+
| Test3 | 6 |
+--------------+---------+
Records table:
+---------+-----------+
| user_id | record_id |
+---------+-----------+
| 28 | 1 |
+---------+-----------+
For what I need to return is the username and user_id for displaying the record owner. Also, display the username and the user_id, but this can be multiple (more than 1+ user). I've tried this:
SELECT usr.username,
usr.user_id,
rec.record_id,
contrib.record_id,
contrib.contributor_user_id
FROM
(
records rec
INNER JOIN members usr ON rec.user_id = usr.user_id
# this returns records as NULL
LEFT OUTER JOIN contributions contrib ON rec.record_id = contrib.record_id AND contrib.contributor_user_id = usr.user_id
# this works, but I need the username to be displayed too
LEFT OUTER JOIN contributions contrib ON rec.record_id = contrib.record_id
)
WHERE rec.record_id = 1
Try nesting the join for contributing users inside of the left join to contributions.
SELECT u.username, u.user_id, r.record_id, u2.username as ContributorName, u2.user_id as ContributorId
FROM records r
INNER JOIN members u
ON r.user_id = u.user_id
LEFT JOIN contributions c
INNER JOIN members u2
ON c.contributor_user_id = u2.user_id
ON r.record_id = c.record_id
WHERE r.record_id = 1
SELECT
usr.username AS record_owner
, usr.user_id AS record_owner_id
, rec.record_id
, con.contributor_user_id AS contributor_id
, contributors.username AS contributor_name
FROM
records rec
INNER JOIN
members usr
ON rec.user_id = usr.user_id
LEFT OUTER JOIN
contributions con
ON rec.record_id = con.record_id
INNER JOIN
members contributors
ON con.contributor_user_id = contributors.user_id
WHERE
rec.record_id = 1