SELECTing SUM Across Two Tables for Each User - mysql

I have an application with a users table, credit_purchase table, and credit_spend table. I am trying to get the amount of credits the user currently has.
The credit tables are as follows:
credit_purchase
+----+---------+-------+---------------------+
| id | user_id | coins | timestamp |
+----+---------+-------+---------------------+
credit_spend
+----+---------+-------+---------------------+
| id | user_id | coins | timestamp |
+----+---------+-------+---------------------+
What I'm trying to do is select the SUM(credit_purchase.coins) - SUM(credit_spend.coins) for each user. Right now I have something like this:
SELECT users.*, COALESCE(SUM(credit_purchase.coins) - SUM(credit_spend.coins), 0)
FROM users
LEFT JOIN credit_purchase ON users.id = credit_purchase.user_id
LEFT JOIN credit_spend ON users.id = credit_spend.user_id
GROUP BY users.id
but, it the result is not the correct number. It seems to be SUMing the purchases properly, but multiplying the SUM from the spend by the number of purchases.
What am I doing wrong?
Thanks!

try this:
SELECT users.*,
COALESCE(SUM(credit_purchase.coins) - SUM(credit_spend.coins), 0)
FROM users
LEFT JOIN credit_purchase
ON users.id = credit_purchase.user_id
LEFT JOIN credit_spend
ON users.id = credit_spend.user_id
GROUP BY users.id

Related

Joining 3 tables and using SUM to calculate ratings of forum posts belonging to one thread

I'm doing a simple discussion board for my school project. I have these tables to store users, posts and ratings for those posts (I'll leave out columns and tables that are insignificant for this question.
+------------+
| User |
+------------+
|PK| id_user|
+------------+
| username|
| profile_pic|
+------------+
+------------+
| Post |
+------------+
|PK| id_post|
+------------+
| id_user|
| id_thread|
| content|
| date_posted|
| deleted|
+------------+
+------------+
|Post_ratings|
+------------+
|PK| id_voter|
|PK| id_post|
+------------+
| rating|
+------------+
What I want to do is select all rows from the Post table with a specific id_thread, join it with the User table to select the username and profile_pic of the poster of each post, and a sum of ratings given to each post, so the columns of the result should be id_post, id_user, content, date_posted, deleted, username, profile_pic, and rating.
I managed to come up only with this sloppy query:
SELECT * FROM Post p LEFT JOIN
(SELECT id_user, username, profile_pic FROM User) u
ON p.id_user = u.id_user LEFT JOIN
(SELECT id_post redundant, SUM(rating) rating FROM Post_ratings) pr
ON p.id_post = pr.redundant
WHERE id_thread = 5 AND deleted = 0
ORDER BY date_posted
This does return all posts belonging to one thread, but it shows post's ID twice (the redundant column) and displays SUM of all ratings across all threads in a row with the lowest id_post, shows NULL in other rows.
If anyone can help, thank you in advance.
I would use a join to associate the users and the posts table, and a subquery to sum the ratings:
select p.*, u.username, u.profile_pic,
(select sum(pr.rating) from post_ratings pr where pr.id_post = p.id_post) as rating
from posts p
inner join users u on u.id_user = p.id_user
where p.id_thread = 5 and p.deleted = 0
Of course, you can also use outer aggregation:
select p.*, u.username, u.profile_pic, sum(pr.rating) as rating
from posts p
inner join users u on u.id_user = p.id_user
inner join post_ratings pr on pr.id_post = p.id_post
where p.id_thread = 5 and p.deleted = 0
group by p.id_post, u.user_id
MySQL understand functionally-dependent columns, so it is sufficient to put the primary key of the posts and users table in the group by clause.

How to join the same table more than once to select different columns?

I need to find out users who have either made or received a booking.
I have two tables that look like this:
Users:
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
+----+
Bookings:
+----+-----+-----+
| id | rid | oid |
+----+-----+-----+
| 1 | 1 | 2 |
| 2 | 2 | 1 |
| 3 | 3 | 4 |
+----+-----+-----+
A booking has two users, a 'rider' (rid), and an 'owner' (oid).
The rider and owner can't be the same for each booking but riders can also be owners.
My output should be a list of user IDs that correspond with users who have made or received a booking.
So far I have written
select u.id, b1.rid, b2.oid
from users u
left join bookings b1
on u.id = b1.rid
left join bookings b2
on u.id = b2.oid;
And various other permutations, but I'm not getting the desired result. Any help would be appreciated.
You want all User IDs that are either in Bookings.rid or Bookdings.oid. So you could do something like:
select
users.id
from
users
where
users.id in (select bookings.rid from bookings)
or
users.id in (select bookings.oid from bookings);
You should be able to utilize a UNION clause here.
However, you don't define what the "time window" is, so I am not sure we can come up with a complete solution for you. However, try something like the following:
SELECT
users.id,
bookings.rid,
bookings.oid
FROM
users
LEFT JOIN bookings ON users.id = bookings.rid
UNION ALL
SELECT
users.id,
bookings.rid,
bookings.oid
FROM
users
LEFT JOIN bookings ON users.id = bookings.oid
My output should be a list of user IDs that correspond with users who have made or received a booking.
To do that, you only need to look at the bookings table :
SELECT DISTINCT rid id FROM bookings
UNION ALL SELECT DISTINCT oid FROM bookings
The DISTINCT removes the duplicates returned by each query, and the UNION ALL removes duplicates across both queries.
If you are looking to filter by time frame :
SELECT DISTINCT rid id FROM bookings WHERE some_date BETWEEN :start_date AND :end_date
UNION ALL SELECT DISTINCT oid FROM bookings WHERE some_date BETWEEN :start_date AND :end_date
Where some_date is the field that contains the booking date, and :start_date/end_date are the beginning and the end of the date interval.
I guess there is a name column in Users table.
If you want this too then:
select users.id, users.name from (
select rid userid from bookings
union
select oid userid from bookings
) t inner join users
on users.id = t.userid
group by users.id, users.name
See the demo
If not you only need to scan the bookings table:
select distinct userid from (
select rid userid from bookings
union
select oid userid from bookings
) t
See the demo

MYSQL Join three tables and group by joined date

I am a MySQL beginner so please be patient with me!
I have three tables
1-Order
Order Id | Order Date
2-users
user Id | Registration date
3-connection
user Id | Order ID
I am trying to calculate conversion rate grouped by date
The conversion rate, example, 300 users registered in January 2013, and 100 of them made one or more purchases, the conversion rate of the registered user to purchaser is 33%
so the final output should be
table
date | number of registered users | number of orders | Conversion rate
Jan-2011| 300 | 100 | 33%
many thanks for the help!
I think something like this is that you need:
select u.registrationDate as date,
count(distinct u.id) as number_of_registered_users,
count(distinct o.id) as number_of_orders,
round(count(distinct o.id)/count(distinct u.id)*100) as conversion_rate
from users u
left join `Order` o on u.registrationDate = o.orderDate
left join connection c on c.orderId = o.id and c.usersId = u.id
group by u.registrationDate;
See SQL Fiddle.

mysql join table with 2 fk to same table

Table join:
|ID|admin|user |data|
|1 |00001|00002|XXXX|
admin(fk) = users.id,
user(fk) = users.id.
Table users:
|id |name|pass|type |
|00001|root|1234|admin|
|00002|user|1235|user |
select join.*,users.name as admin,users.name as user from join
left join users on users.id=join.admin
left join users on users.id=join.user
where grrrrrrr
How can I do this?
Original query, I'm trying to run:
SELECT
visits.id,
visits.patient AS patient_id,
visits.doctor AS doctor_id,
visits.date,
visits.time_booked,
visits.time_arrived,
visits.time_start,
visits.time_end,
visits.type_id,
visits.complain,
visits.diagnosis,
visits.note,
visits.stats,
(personal.name WHERE personal.id=visits.patient and personal.role='patient') AS pt_name,
(personal.name WHERE personal.id=visits.doctor and personal.role='doctor') AS dr_name
FROM
visits ,
personal
You have to alias the table users with different aliases. Something like
select
a.*,
u1.name as admin,
u2.name as user
from `join` a
left join users u1 on u1.id = a.admin
left join users u2 on u2.id = a.`user`;
Also you have to escape the table name join, user since they are reserved keywords in MySQL. Try to avoid those names as object names.
SQL Fiddle Demo
This will give you:
| ID | ADMIN | USER | DATA |
----------------------------
| 1 | 1 | 2 | XXXX |
Update
For your query after you updated your question, you have to do this the same way, like this:
SELECT
v.id,
v.patient AS patient_id,
v.doctor AS doctor_id,
v.date,
v.time_booked,
v.time_arrived,
v.time_start,
v.time_end,
v.type_id,
v.complain,
v.diagnosis,
v.note,
v.stats,
pationts.name AS pt_name,
doctors.name AS dr_name
FROM visits v
LEFT JOIN personal pationts ON pationts.id = v.patient
AND pationts.role ='patient'
LEFT JOIN personal doctors ON doctors.id = v.patient
AND doctors.role ='doctor';
Updated SQL Fiddle Demo

How to write inner query that returns latest message for a given user?

I have some tables like this:
USERS TABLE:
| id | created | active | fname | lname |
MESSAGES TABLE:
| id | userId| active | date | content |
I am trying to return some user information, along with the most recently added message for a given user.
Below is the structure of the results that I am rying to achieve:
| userId | userCreated | latestMessageDate| latestMessageContent |
The following query returns the user information:
SELECT
user.id,
user.created
FROM user
WHERE user.active = 1
... But how do I now attach the date of the latest message, along with the actual latest message?
I believe using an inner query is one such approach, but how do you write such a query??
SELECT u.fname, u.lname, m.id, m.userID, m.datem, m.content
FROM USERS AS u
LEFT JOIN ( SELECT id, userID, date, content
FROM MESSAGES
WHERE active
ORDER BY date DESC) AS m
ON u.id = m.userId
WHERE u.active
# AND u.id = {$int_user_id}
GROUP BY u.id
Maybe something like this:
SELECT
Users.id AS userId,
Users.created AS userCreated,
LatestMessage.LatestMessageDate,
MESSAGES.content AS latestMessageContent
FROM
Users
LEFT JOIN
(
SELECT
MAX(date) AS LatestMessageDate,
MESSAGES.userId
FROM
MESSAGES
GROUP BY
MESSAGES.userId
) AS LatestMessage
ON Users.id=LatestMessage.userId
LEFT JOIN MESSAGES
ON LatestMessage.LatestMessageDate=MESSAGES.date
AND LatestMessage.userId=MESSAGES.userId