I have two tables:
rooms (all the rooms)
id | title | ...
-----------------
1 |Room 1 |
2 |Room 2 |
3 |Room 3 |
user_rooms (in which room is every user, column user is user's id and it's primary column)
user | room | ...
------------------
20 | 3 |
14 | 1 |
35 | 3 |
So I want to select all the rooms from the 'rooms' table but to order them in that way to show the rooms with the most users in them and after that the rooms with less and less users. For example, I want to show room 3 first (because 2 users are in it), then room 1 (one user in it), and finally room 2 (since no users are in it). How to achieve that?
SELECT aa.id, aa.title
FROM rooms AS aa
LEFT JOIN (
SELECT room, COUNT(*) AS total_count
FROM user_rooms
GROUP BY room
) AS _aa
ON aa.id = _aa.room
ORDER BY _aa.total_count;
This would often be done without a subquery:
select r.id, r.title, count(ur.room) as numusers
from rooms r left join
user_rooms ur
on r.id = ur.room
group by r.id, r.title
order by numusers desc;
This would often be more efficient than a version using a subquery in the from clause because it can take advantage of an index on the join key.
Interestingly, the same index would be used for a correlated subquery in the select, which is an alternative approach:
select r.id, r.title,
(select count(*)
from user_rooms ur
where r.id = ur.room
) as numusers
from rooms r
order by numusers desc;
This might be the most efficient approach, because it removes the aggregation in the outer query.
select r.id, r.title, coalesce(t.cnt,0)
from rooms r left join
(select room, count(*) as cnt
from user_rooms
group by room) t on t.room = r.id
order by t.cnt desc
This will give you only rooms with users
SELECT title, count(user) As MostUsers
FROM Rooms R
INNER JOIN user_rooms U
ON R.?field? = U.?field?
GROUP BY title
ORDER BY MostUsers
You need to complete the query inserting the names of the fields that you can use to Join the tables
If you want all rooms you can use a Left Join:
SELECT title, count(user) As MostUsers
FROM Rooms R
LEFT JOIN user_rooms U
ON R.?field? = U.?field?
GROUP BY title
ORDER BY MostUsers
Please try the following query:
select * from rooms r
order by (select count(1)
from userroom ur
where ur.roomid = r.roomid ) desc
Related
I have to write a SQL query that, for every user, will return the name of the room that was the most frequently reserved by the user.
I created one of the three tables:
SELECT User.Name as user_name, Room.Name as room_reser
FROM Reservation
INNER JOIN User ON User.Id = Reservation.UserId
INNER JOIN Room ON Room.Id = Reservation.RoomId
Table of 3:
Name room_rese name common_room
Jack room_1
Anna room_2 I need => Jack room_1
Jack room_1
Anna room_1 Anna room_2
Jack room_2
Anna room_2
I tried something like this but I don't know how to use it in this case :
SELECT DISTINCT r.user_name, (
select b.room_reser
from Reservation b
where b.user_name = r.user_name
group by b.user_name, b.roo_reser
order by count(*) desc
limit 1
) as roo_reser from Reservation r)`
If you are running a database that supports window functions, you can do this with aggregation and window function rank():
select user_name, room_name
from (
select
us.name as user_name,
ro.name as room_name,
rank() over(partition by re.userid order by count(*) desc) rn
from reservation re
inner join user us on us.id = re.userid
inner join room ro on ro.id = re.roomid
group by re.userid, re.roomid, us.name, ro.name
) t
where rn = 1
The inner query aggregates by user name and room, and ranks rooms per user. The outer query filters on the top room per user. If there are ties (ie the two most reserved room of a user have the same number of reservations), then both will be displayed - if you want a single record even if there are ties, you can add another sorting criteria to break the tie.
If your database does not support window function, you could try and filter in the having clause with an aggregate correlated subquery:
select
us.name as user_name,
ro.name as room_name
from reservation re
inner join user us on us.id = re.userid
inner join room ro on ro.id = re.roomid
group by us.name, ro.name
having count(*) = (
select count(*)
from reservation re1
where re1.userid = re.userid
group by re1.roomid
order by count(*) desc
limit 1
)
SELECT DISTINCT User.Name as user_name, Room.Name as room_reser
FROM Reservation
INNER JOIN User ON User.Id = Reservation.UserId
INNER JOIN Room ON Room.Id = Reservation.RoomId
GROUP BY user_name, room_reser
ORDER BY COUNT(room_reser)
I have two tables users, orders
each table has below column
users(table)
id
orders(table)
user_id
How can i get the number of users whose order count is 1,2,3,4 ....n?
Like this?
users count | order count
999 | 1
100 | 2
80 | 3
70 | 4
60 | 5
50 | 6
What I have been trying so far is
SELECT cnt.uid as u_cnt, cnt.ocnt as or_cnt
FROM (
SELECT u.id as uid, COUNT(o.id) as o_cnt
FROM users as u
INNER JOIN orders o on u.id = o.user_id
) as cnt;
GROUP BY or_cnt
BUT I get only 1 u_cnt and summed or_cnt
You need two levels of group by clauses here: First, you need to group by user and count the number of orders each user has. Then, you need to take that result, group by the number of orders and count how many users have such an order count.
The easiest way to achieve this is probably with a subquery, where both the inner and outer query have a group by clause:
SELECT cnt.ocnt as or_cnt, COUNT(*) as user_count
FROM (
SELECT u.id as uid, COUNT(o.id) as o_cnt
FROM users as u
INNER JOIN orders o on u.id = o.user_id
GROUP BY u.id -- This was missing in your original query
) as cnt
GROUP BY or_cnt
You can use two levels of aggregation. More importantly, you do not need a JOIN. All the information you need is in orders:
SELECT o_cnt, COUNT(*) as user_count
FROM (SELECT o.user_id, COUNT(*) as o_cnt
FROM orders o
GROUP BY o.user_id
) u
GROUP BY o_cnt
ORDER BY o_cnt;
I have an sql query that returns a list of residential units, and a subquery that is supposed to get the last entered bill for that unit.
However when I add LIMIT 1 to the subquery, no bill entries are returned? If I leave it out, I get duplicate unit rows depending on the number of bill for the unit.
select * from unit u
left join (select id as billId, unit_id, added_on, end_reading, bill_type from bills
order by id desc) b ON unit_id = u.id
where community_Id = 1
and unit_section = 7
and unit_floor in (1,2,3,4,5)
order by unit_floor, display_order asc;
Anyone know how I can the subquery result limited to 1 bill?
When using joins that duplicate your results, add a group by statement. It's an alternative of the distinct from a simple select
select * from unit u
left join (select id as billId, unit_id, added_on, end_reading, bill_type from bills
order by id desc) b ON unit_id = u.id
where community_Id = 1
and unit_section = 7
and unit_floor in (1,2,3,4,5)
group by u.id
order by unit_floor, display_order asc;
Think you will need a sub query to get the first (lowest) id for each unit_id from the bills table. Then use that to join between the unit and bills table, getting the other matching columns from bills for that lowest id
SELECT u.*, bills.*
FROM unit u
LEFT OUTER JOIN
(
SELECT unit_id, MIN(id) AS min_id
FROM bills
GROUP BY unit_id
) b ON b.unit_id = u.id
LEFT OUTER JOIN bills
ON b.unit_id = bills.unit_id
AND b.min_id = bills.id
WHERE u.community_Id = 1
AND u.unit_section = 7
AND u.unit_floor in (1,2,3,4,5)
ORDER BY u.unit_floor, u.display_order asc;
I have a table of users which hold a a users id that they voted for like this:
uid | voted_for
1 | 3
2 | 3
3 | 1
What i'm aiming to do is order uid based on how many people have voted for that uid. But I have no idea how to do it.
So the end result would be:
uid | Total_Votes
3 | 2
1 | 1
2 | 0
Hope you can help explain the best way to structure the SQL for this.
Perhaps something like this will help joining the table on itself:
SELECT u.*, voted_for_cnt
FROM users u
LEFT JOIN (
SELECT voted_for, count(1) voted_for_cnt
FROM users
GROUP BY voted_for
) t ON u.uid = t.voted_for
ORDER BY t.voted_for_cnt DESC
SQL Fiddle Demo
This simple query will produce the output you requested:
select voted_for as uid, count(*) as total_votes
from users
group by 1
order by 2 desc
If you want all data about each user in the output, join users to itself:
select u.*, count(v.uid) as total_votes
from users u
left join users v on v.voted_for = u.uid
group by 1,2,3,4,5 -- put as many numbers here as there are columns in the users table
order by total_votes desc
This second query will give a total_votes score of zero if no one voted for the user.
Alternatively, you can select only those columns you want:
select u.uid, u.name, count(v.uid) as total_votes
from users u
left join users v on v.voted_for = u.uid
group by 1,2
order by 3 desc
```
To return only the winners, do this:
select u.uid, u.name, count(*) as total_votes
from users u
left join users v on v.voted_for = u.uid
group by 1,2
having count(*) = (
select max(c) from (
select count(*) as c from users group by voted_for))
order by 3 desc
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