I have 3 tables in my mysql DB to query.
Users_rates (fields: id,userid,raterid,rate,eventid) containing all of the rates(rate) that have been assigned to users(userid), participating to specific events(eventid), by other users(raterid)
Events_participants (fields:id,userid,eventid) containing all of the users(userid) participating to each event(eventid)
Users (fields:id,name,lastname)containing all the user relative data
I need to query those three tables to retrieve an event-specific rank for the users' rates.
Ex. John,Erik and Mark participated to 'eventid=31'.
John received 1 rate from Mark, and 2 from Erik.
Mark received 1 rate from Erik.
Nobody has rated Erik though.
I need to retrieve for each user name,lastname and the sum of the rates received for eventid=31
I tried with this:
SELECT events_participants.userid,users.name,users.lastname,
(SELECT SUM(rate)FROM users_rates WHERE users_rates.eventid=31 AND users_rates.userid=events_participants.userid)AS rate
FROM(( events_participants INNER JOIN users ON events_participants.userid=users.id)
LEFT OUTER JOIN users_rates ON events_participants.userid=users_rates.userid )
WHERE events_participants.eventid=31
But I receive:
userid | name | lastname | rate
1 | luca | silvestro | 1
3 | claudio | buricchi | 6
3 | claudio | buricchi | 6
What's the right query?
Thanks
Luca
Try this:
SELECT users.userid, users.name, users.lastname, temp.sum as rate
FROM users LEFT JOIN (
SELECT userid, SUM(rate) as sum FROM users_rates WHERE eventid = 31 GROUP BY userid
) as temp USING (userid)
It might give an error, this might work instead:
SELECT users.userid, users.name, users.lastname, temp.sum as rate
FROM users, (
SELECT userid, SUM(rate) as sum FROM users_rates WHERE eventid = 31 GROUP BY userid
) as temp WHERE users.userid = temp.userid
I don't know if I got the problem right, but maybe something like:
SELECT u.id, u.name, u.lastname, SUM(ur.rate) AS rate
FROM users AS u
INNER JOIN users_rates AS ur ON ur.userid = u.id
WHERE ur.eventid = 31
GROUP BY u.id
edit: If you want to receive a list with all users regardless of whether they have any rates at all, you could also join the users_participants table and replace the INNER JOIN of users_rates by a LEFT JOIN. The WHERE clause has to reference events_participants then (not users_rates anymore as it could be NULL):
SELECT u.id, u.name, u.lastname, SUM(ur.rate) AS rate
FROM users AS u
INNER JOIN events_participants AS ep ON ep.userid = u.id
LEFT JOIN users_rates AS ur ON ur.userid = u.id AND ur.eventid = ep.eventid
WHERE ep.eventid = 31
GROUP BY u.id
Related
I need to find the percentage of users who only visited a mobile and the percentage of users that were on both mobile/web.
Table 1: User
user_id | page
6684 | home_page
Table 2: MobileData
user_id | page
1210 | page_6_mobile
Table 3: WebData
user_id | page
129 | page_1_web
To find all the users:
SELECT user_id FROM User
To find all the users who have been on mobile:
SELECT DISTINCT user_id FROM MobileData
We use DISTINCT so that we only get one row for each user.
To find all the users who have been on both mobile and web:
SELECT DISTINCT u.user_id
FROM User u
JOIN MobileData m ON m.user_id = u.user_id
JOIN WebData w ON w.user_id = u.user_id;
Now we just need to count all the results and compute the percentages:
SELECT total_users,
mobile_users / total_users * 100 AS percent_mobile,
mobile_and_web_users / total_users * 100 AS percent_both
FROM (SELECT COUNT(*) AS total_users,
(SELECT COUNT(DISTINCT user_id) FROM MobileData) AS mobile_users,
(SELECT COUNT(DISTINCT u.user_id)
FROM User u
JOIN MobileData m ON m.user_id = u.user_id
JOIN WebData w ON w.user_id = u.user_id) AS mobile_and_web_users
FROM User) u
SQLFiddle Demo
Update
Here is a much cleaner version of the query. It uses a LEFT JOIN from User to MobileData to find all users who have been on mobile (while retaining the entire list of users). Then it uses another LEFT JOIN from MobileData to WebData to find users who have been on mobile and web. By doing this we can simplify the counting to a neat COUNT(DISTINCT user_id) on each table:
SELECT total_users,
mobile_users / total_users * 100 AS percent_mobile,
mobile_and_web_users / total_users * 100 AS percent_both
FROM (SELECT COUNT(DISTINCT u.user_id) AS total_users,
COUNT(DISTINCT m.user_id) AS mobile_users,
COUNT(DISTINCT w.user_id) AS mobile_and_web_users
FROM User u
LEFT JOIN MobileData m ON m.user_id = u.user_id
LEFT JOIN WebData w ON w.user_id = m.user_id) u
SQLFiddle Demo
I have the following tables:
users
id
username
first_name
email
students
id
user_id
payments
id
student_id
is_paid
date
And I have the following query:
SELECT users.username, users.first_name, users.email,
COUNT(payments.id) as pending_payments
FROM users
LEFT JOIN students ON users.id = students.user_id
LEFT JOIN payments ON payments.student_id = students.id AND payments.is_paid = 0
WHERE users.username LIKE 'testuser';
When the username exists and there are not pending payments, it returns:
| username | first_name | email | pending_payments |
| testuser | test | testuser#example.com | 0 |
When the username exists and there are 2 pending payments, it returns:
| username | first_name | email | pending_payments |
| testuser2| test2 | testuser#2example.com | 2 |
But when the username does not exists, it returns:
| username | first_name | email | pending_payments |
| NULL | NULL | NULL | 0 |
Instead, when the username does not exists, I want as result:
Empty set.
How can I modify my query to get the expected results?
(keep the behavior when user exists, but returns Empty set when does not exists, instead of a row will NULL values).
See SQLfiddle: http://sqlfiddle.com/#!9/aab424/11
EDIT
A working solution was provided:
http://sqlfiddle.com/#!9/aab424/72
Aggregate functions like COUNT(*) always generate one row if no GROUP BY clause is used. So use a GROUP BY clause, if you want an empty result:
SELECT users.username, users.first_name, users.email,
COUNT(payments.id) as pending_payments
FROM users
LEFT JOIN students ON users.id = students.user_id
LEFT JOIN payments ON payments.student_id = students.id AND payments.is_paid = 0
WHERE users.username LIKE 'testuser3'
GROUP BY users.username, users.first_name, users.email;
Note that you will get one row per user with that username. If username is not UNIQUE and you want to get one row for all users with the same username, you can use the HAVING clause instead of GROUP BY to "remove" the "empty" row.
SELECT users.username, COUNT(payments.id) as pending_payments
FROM users
LEFT JOIN students ON users.id = students.user_id
LEFT JOIN payments ON payments.student_id = students.id AND payments.is_paid = 0
WHERE users.username LIKE 'testuser3'
HAVING username IS NOT NULL;
SQLFiddle
This has nothing to do with the LEFT or any JOIN. It is just how aggregate functions work (in MySQL), and you will see the same behavior with a simple example without joins like
SELECT username, COUNT(*)
FROM users
WHERE username = 'testuser3';
| username | COUNT(*) |
|----------|----------|
| (null) | 0 |
Note that this query is not SQL standard compliant, and in strict mode you will get an error, because username is selected without beeng listed in the GROUP BY clause. (SQLFiddle).
The documentation says:
If you name columns to select in addition to the COUNT() value, a
GROUP BY clause should be present that names those same columns.
Otherwise, the following occurs:
If the ONLY_FULL_GROUP_BY SQL mode is enabled, an error occurs:
[...]
If ONLY_FULL_GROUP_BY is not enabled, the query is processed by
treating all rows as a single group, but the value selected for each
named column is indeterminate. The server is free to select the value
from any row:
[...]
Again: "The server is free to select the value from any row". But since there is no matching row, it returns NULL.
The fact that you receive "null" response is due to the LEFT JOIN statements. A Left join gets done, even if parameters do not match in that table, because they might make up columns in another (joined) table. To show only results that match criteria, use an INNER JOIN statement.
Also, you wish to COUNT(*) payments due by username, you should GROUP BY username I should think.
Try the following (untested, sorry, not in front of pc with editor on it)
SELECT u.username, u.first_name, u.email, COUNT(p.is_paid) as pending_payments
FROM users u
INNER JOIN students s ON u.id = s.user_id
INNER JOIN payments p ON s.user_id = p.student_id
WHERE u.username LIKE '%testuser%'
AND u.username IS NOT NULL
GROUP BY u.username
ORDER BY p.is_paid DESC, u.username ASC
Additionally, if you'd want to show only the results that have open pending payments you could add a HAVING statement after the GROUP BY, such as
HAVING u.pending_payments > 0
Tested with the following setup:
Below showing the query results. Second query does not have the HAVING statement.
I think the NULL values found for username etc. is mostly because of the missing group by in your queries.
I think it should work using:
SELECT users.username,
users.first_name,
users.email,
SUM(COALESCE( p.is_paid = 0, 0)) as pending_payments
FROM users
LEFT JOIN students s on s.user_id = users.id
LEFT JOIN payments p on p.student_id = s.id
WHERE users.username LIKE 'testuser3'
GROUP BY users.username,
users.first_name,
users.email;
And when leaving out the WHERE line, it will list for all the users:
SELECT users.username,
users.first_name,
users.email,
SUM(COALESCE( p.is_paid = 0, 0)) as pending_payments
FROM users
LEFT JOIN students s on s.user_id = users.id
LEFT JOIN payments p on p.student_id = s.id
GROUP BY users.username,
users.first_name,
users.email;
It will mention all users, even those who don't have any payment (pending or not) or users who are not listed in the Students table
You obviously has users with NULL username in users table. You can filter out the NULL value in the WHERE clause.
SELECT users.username, users.first_name, users.email,
COUNT(payments.is_paid = 0) as pending_payments
FROM users
LEFT JOIN students ON users.id = students.user_id
LEFT JOIN payments ON payments.student_id = students.id
WHERE users.username LIKE 'testuser' AND users.username IS NOT NULL;
Why cant use IS NOT NULL ?
SELECT users.username, users.first_name, users.email,
COUNT(payments.id) as pending_payments
FROM users
LEFT JOIN students ON users.id = students.user_id
LEFT JOIN payments ON payments.student_id = students.id AND payments.is_paid = 0
WHERE users.username LIKE 'testuser' and users.username is NOT NULL;
I have a renting schema in my database.
It has 3 tables, user, rentingTrack and lock.
user has as unique element RFIDCode.
in rentingTrack I may have the user(he or she rented at least once) or not (he/she never rented). If the user rented I need the returnLock for the last rent, as rentingID is autoincremented, it is max(rentingID).
With lock table I got the parkingID, the data I need.
The problem is how to get the parkingID, I have half query:
SELECT u.userID, l.parkingID
FROM locks l, user u
LEFT JOIN (SELECT r1.* FROM rentingTrack r1 INNER JOIN
(SELECT userID, MAX(rentingID) maxRentingID FROM rentingTrack GROUP BY userID) r2
on r1.rentingID = r2.maxRentingID) r on u.userID = r.userID
WHERE u.userCodeRFID= 3166681355
What i need is, if userID isn't in the rentingTrack table I got:
userID | parkingID
----------+-----------
34 | NULL
And if userID is in the rentingTrack
userID | parkingID
----------+-----------
34 | 5
how can I complete the query to get parkingID?
Try This. (Need some sample data for duplicates)
select distinct u.userID,l.parkingID
from users
left join rentingTrack r on u.userID=r.userID
left join locks l on r.returnLock=l.lockid
SELECT u.userID, l.parkingID
FROM user u
LEFT JOIN (SELECT r1.userID, r1.returnLock
FROM rentingTrack r1
INNER JOIN
(SELECT userID, MAX(rentingID) maxRentingID
FROM rentingTrack
GROUP BY userID) r2
ON r1.rentingID = r2.maxRentingID) r on u.userID = r.userID
LEFT JOIN locks l ON r.returnLock = l.lockID
WHERE u.userCodeRFID= 3166681355
I have the following database:
user_id and contact_id from Contacts table are PK and FK to the user_id field in the Users table. How can i select all contacts (or friends) of a specific user including the number of contacts of all contacts of this user. I tried different SELECT queries but either the number of contacts is incorrect or the contact_status is printed wrong. I use COUNT() as a function to print the number of contacts. I use this query but the contact_status is printed wrong:
SELECT COUNT(Contacts.contact_id), Users.user_id, Users.user_name, Users.name, Users.user_picture, Users.mood_message, Users.phone_number, Users.email, Users.country, Users.city, Users.website, Users.birth_date, Users.gender, Users.language, Users.about_me, Users.online_status, Users.privacy, Contacts.contact_status
FROM Contacts JOIN Users ON Contacts.contact_id = Users.user_id
WHERE Users.user_name IN (
SELECT Users.user_name
FROM Users
WHERE Users.user_id IN (
SELECT Contacts.contact_id
FROM Contacts
WHERE Contacts.user_id = 12
)
)
GROUP BY Users.user_name;
Users Table:
user_id, user_name, ...
12 John ...
13 Matt ...
14 Jack ...
Contacts Table:
user_id, contact_status, contact_id
12 1 13
13 1 12
12 2 14
If i want to print all Contacts of John the result should consist:
COUNT(Contacts.contacts_id), Users.user_name, Users. ... , Contacts.contact_status
1 Matt ... 1
0 Jack ... 2
The above query prints 1, 1 as a contact_status instead of 1, 2.
Can you help me with this query? Thanks in advance.
If I understood what's your goal, this should work:
SELECT c.contact_status, cc.contactCount, u.*
FROM Contacts c JOIN Users u ON (c.contact_id = u.user_id)
JOIN (
SELECT user_id, COUNT(1) contactCount FROM Contacts
GROUP BY user_id
) cc ON (c.user_id = cc.user_id)
WHERE c.user_id = 12
I hope I understand ur requirements, but its much simpler to use a subquery, like so:
SELECT
u.* -- Whatever fields you need
,(
select count(*) from Contacts c
where c.contactid = co.userid -- refers to outer record
) as contact_count
FROM
Users u Inner Join Contacts co
on co.UserId = u.UserId
WHERE
U.UserId = 12
It must be pretty easy, but i can't think of any solution nor can I find an answer somewhere...
I got the table 'users'
and one table 'blogs' (user_id, blogpost)
and one table 'messages' (user_id, message)
I'd like to have the following result:
User | count(blogs) | count(messages)
Jim | 0 | 3
Tom | 2 | 3
Tim | 0 | 1
Foo | 2 | 0
So what I did is:
SELECT u.id, count(b.id), count(m.id) FROM `users` u
LEFT JOIN blogs b ON b.user_id = u.id
LEFT JOIN messages m ON m.user_id = u.id
GROUP BY u.id
It obviously doesn't work, because the second left join relates to blogs not users. Any suggestions?
First, if you only want the count value, you could do subselects:
select u.id, u.name,
(select count(b.id) from blogs where userid = u.id) as 'blogs',
(select count(m.id) from messages where userid = u.id) as 'messages'
from 'users'
Note that this is just a plain sql example, I have no mysql db here to test it right now.
On the other hand, you could do a join, but you should use an outer join to include users without blogs but with messages. That would imply that you get several users multiple times, so a group by would be helpful.
If you use an aggregate function in a select, SQL will collapse all your rows into a single row.
In order to get more than 1 row out you must use a group by clause.
Then SQL will generate totals per user.
Fastest option
SELECT
u.id
, (SELECT(COUNT(*) FROM blogs b WHERE b.user_id = u.id) as blogcount
, (SELECT(COUNT(*) FROM messages m WHERE m.user_id = u.id) as messagecount
FROM users u
Why you code does not work
SELECT u.id, count(b.id), count(m.id)
FROM users u
LEFT JOIN blogs b ON b.user_id = u.id <<-- 3 matches multiplies # of rows *3
LEFT JOIN messages m ON m.user_id = u.id <<-- 5 matches multiplies # of rows *5
GROUP BY u.id
The count will be off, because you are counting duplicate items.
Simple fix, but will be slower than option 1
If you only count distinct id's, you will get the correct counts:
SELECT u.id, count(DISTNICT b.id), count(DISTINCT m.id)
FROM users u
LEFT JOIN blogs b ON b.user_id = u.id
LEFT JOIN messages m ON m.user_id = u.id
GROUP BY u.id