Joining 2 sql queries in MySQL - mysql

I'm trying to join 2 sql queries in one query.
The first one gets the count of rooms per hotel.
The second one gets the count of checked guests in hotel.
I'm trying to get occupancy rate per hotel.
SELECT hotel_id, count(room_id)
FROM room
group by room.hotel_id
SELECT h.hotel_id, count(k.room_id)
FROM room_reservation as kr , room as k , hotel as h
where kr.room_id = k.room_id and k.hotel_id = h.hotel_id
group by k.hotel_id
How can i do this ?

select aux.hotel_id, ((coalesce(aux2.total, 0)*1.0)/aux.total)*100 as 'ocupancy rate'
from (SELECT hotel_id, count(room_id) as 'total'
FROM room
group by room.hotel_id) aux
LEFT OUTER JOIN (SELECT h.hotel_id, COUNT(k.room_id) as 'total'
FROM room_reservation as kr
INNER JOIN room as k ON (kr.room_id = k.room_id)
INNER JOIN hotel as h ON (k.hotel_id = h.hotel_id)
GROUP BY k.hotel_id) aux2 on aux.hotel_id = aux2.hotel_id

You can definitely do this with one query. One approach is just to union together your queries.
However, I think the following does what you want in one stroke:
SELECT r.hotel_id, count(distinct k.room_id) as numrooms,
count(distinct kr.room_id) as numreserved
FROM room k left outer join
room_reservation kr
on kr.room_id = k.room_id
group by r.hotel_id
I'm not positive, without knowing more about the tables. In particular, reservations have a time component which rooms and hotels don't have. How is this incorporated into your queries?

Join all your queries, aggregate to get the number of rooms/reservations per hotel, and divide:
SELECT hotel_id,
COUNT(DISTINCT r.room_id) / CONVERT(decimal, COUNT(*)) * 100.0 AS occupancy_rate
FROM hotel h
LEFT OUTER JOIN room r ON h.hotel_id = r.hotel_id
LEFT OUTER JOIN room_reservation rr ON r.room_id = rr.room_id
GROUP BY h.hotel_id

i hope this is self-explanatory:
select hotel_id, sum(guests)/count(room_id) occupancy_level
from (
select r.hotel_id, r.room_id, count(*) guests
from room r
left join room_reservation rr on rr.room_id = r.room_id
group by r.hotel_id, r.room_id
) temp
group by hotel_id
UPDATE - inspired by #Gordon Linoff to include unreserved rooms:
select r.hotel_id, count(*) / count(distinct r.room_id) occupancy_level
from room r
left join room_reservation rr on rr.room_id = r.room_id
group by r.hotel_id, r.room_id

It can be done very simply assuming that there will always be equal or less reservations than total hotel rooms at any given time in the room_reservation table and that a hotel room will only have 0 or 1 corresponding rows in the room_reservation table as previous reservations for a room are deleted (it seems that way because in your second query, you are not doing any kind of filtration like selecting only the most recent reservations per room, etc.):
SELECT
a.hotel_id,
(COUNT(b.room_id) / COUNT(*))*100 AS occupancy_rate
FROM
room a
LEFT JOIN
room_reservation b ON a.room_id = b.room_id
GROUP BY
a.hotel_id
If you need more details about the hotel beyond just the hotel_id, an additional INNER JOIN will be required.

Related

How can i find the row with the max value in sql?

I need to get which guest spent the most in a hotel throughout their lifetime. The room table has the price per individual room.
SELECT g.guest_id, g.name, g.email, sum(room_price) sumcost
FROM booking b
INNER JOIN guest g ON b.guest_id = g.guest_id
INNER JOIN room r ON r.room_id = b.room_id
GROUP BY g.guest_id;
The above query gives me a list of guests and gets me their sum they spent. Now I need to get only the guest who has the maximum sumcost instead of getting the whole list of all the guests. How can I accomplish this?
SELECT
s.ggid,
s.sumcost
FROM (SELECT
g.guest_id ggid,
sum(room_price) sumcost
FROM booking b
INNER JOIN guest g ON b.guest_id = g.guest_id
INNER JOIN room r ON r.room_id = b.room_id
GROUP BY g.guest_id
ORDER BY sumcost DESC) s
WHERE s.sumcost = ( SELECT sum(room_price) sumcost
FROM booking b
INNER JOIN guest g ON b.guest_id = g.guest_id
INNER JOIN room r ON r.room_id = b.room_id
GROUP BY g.guest_id
ORDER BY sumcost DESC
LIMIT 1
)
You can use a window function, e.g. MAX OVER:
SELECT g.guest_id, g.name, g.email, sums.sumcost
FROM guest g
JOIN
(
SELECT
b.guest_id, SUM(r.room_price) AS sumcost,
MAX(SUM(r.room_price)) OVER () AS maxsumcost
FROM booking b
INNER JOIN room r ON r.room_id = b.room_id
GROUP BY b.guest_id
) sums ON sums.guest_id = g.guest_id AND sums.sumcost = maxsumcost;

Subquery left join refer to parent ID

I am trying to make a query to fetch the newest car for each user:
select * from users
left join
(select cars.* from cars
where cars.userid=users.userid
order by cars.year desc limit 1) as cars
on cars.userid=users.userid
It looks like it says Unknown column "users.userid" in where clause
I tried to remove cars.userid=users.userid part, but then it only fetches 1 newest car, and sticks it on to each user.
Is there any way to accomplish what I'm after? thanks!!
For this purpose, I usually use row_number():
select *
from users u left join
(select c.* , row_number() over (partition by c.userid order by c.year desc) as seqnum
from cars c
) c
on c.userid = u.userid and c.seqnum = 1;
One option is to filter the left join with a subquery:
select * -- better enumerate the columns here
from users u
left join cars c
on c.userid = u.userid
and c.year = (select max(c1.year) from cars c1 where c1.userid = c.userid)
For performance, consider an index on car(userid, year).
Note that this might return multiple cars per user if you have duplicate (userid, year) in cars. It would be better to have a real date rather than just the year.
Maybe there are better and more efficient way to query this. Here is my solution;
select users.userid, cars.*
from users
left join cars on cars.userid = users.userid
join (SELECT userid, MAX(year) AS maxDate
FROM cars
GROUP BY userid) as sub on cars.year = sub.maxDate;

Trying to get a count in 3 tables on MySql

So I have 3 tables in a database
One is hotel which has hotel_id and status
one is partner which has partner_id and partner_name
and one is partner_hotel which has hotel_id and partner_id
What I am trying to get is the count for each partner that has a hotel with the status = 1
The closest I have gotten is
select p.partner_name,count(hotel_id)
from partner_hotel ph
join partner p on p.partner_id = ph.partner_id
group by ph.partner_id;
The problem is that does not limit to ones with a status of 1 and nothing I seem to be doing seems to work.
you should join hotel too if th hotel contain status and group by p.partner_id
select p.partner_name,count(*)
from partner_hotel ph
inner join partner p on p.partner_id = ph.partner_id
inner join hotel h on ph.hotel_id = h.hotel_id
where h.status = 1
group by p.partner_id;
or ig hotel use id
select p.partner_name,count(*)
from partner_hotel ph
inner join partner p on p.partner_id = ph.partner_id
inner join hotel h on ph.hotel_id = h.id
where h.status = 1
group by p.partner_id;
I guess you're trying to find the number of partners with one or more hotels having a status = 1. If my guess is correct, this gets you the one number.
SELECT COUNT(DISTINCT p.partner_id) partner_count
FROM partner p
JOIN partner_hotel ph ON p.partner_id = ph.partner_id
JOIN hotel h ON ph.hotel_id = h.hotel_id AND h.status = 1
If your requirement is to show one result row for each partner showing the number of hotels with status = 1, that will go like this.
SELECT COUNT(*) hotel_count,
p.partner_id
FROM partner p
JOIN partner_hotel ph ON p.partner_id = ph.partner_id
JOIN hotel h ON ph.hotel_id = h.hotel_id AND h.status = 1
GROUP BY p.partner_id
Pro tip: When preparing to write a query, it's helpful to imagine the result set you want. Ask yourself these questions:
How many rows must the result set have?
It should have one row per ____ (row per what? Hotel, Partner, Guest, Hotel with status = 1?).
Each row should show ____ values?
If you do this work of imagination, writing the query will be much easier.

Minimizing redundancy of MySQL query

I'm having a bit of trouble trying to reduce the redundancy of a query in MySQL. I currently have it working, but it feels like I have too much overhead because it uses a redundant subquery. What I am trying to do is use a dvd rental database to find which store location has rented out more dvd's for each month in 2005.
Here is the working query
SELECT b.month, c.store_id, b.maxRentals
FROM
(SELECT a.month, MAX(a.rentalCount) as maxRentals
FROM
(SELECT MONTH(rental.rental_date) as month, inventory.store_id, count(1) as rentalCount
FROM rental
INNER JOIN inventory
ON rental.inventory_id = inventory.inventory_id
WHERE YEAR(rental.rental_date) = 2005
GROUP BY MONTH(rental.rental_date), inventory.store_id
) a
GROUP BY a.month
) b
INNER JOIN
(SELECT MONTH(rental.rental_date) as month, inventory.store_id, count(1) as rentalCount
FROM rental
INNER JOIN inventory
ON rental.inventory_id = inventory.inventory_id
WHERE YEAR(rental.rental_date) = 2005
GROUP BY MONTH(rental.rental_date), inventory.store_id
) c
ON b.maxRentals = c.rentalCount
GROUP BY b.month;
Notice how the subquery with the alias of "c" is the exact same subquery of alias "a". I'm not sure if there's a way to get rid of this, as I can't inner join on an alias. Am I just stuck with a giant query, or is there something else I can do?
I am 90% certain this query will achieve your intentions:
SELECT MONTH(r.rental_date), i.store_id, COUNT(*)
FROM rental r
LEFT JOIN inventory i ON r.inventory_id = i.inventory_id
WHERE YEAR(r.rental_date) = 2005
GROUP BY MONTH(r.rental_date), i.store_id
Let me know how it goes!
Edit: to answer the question which store location has rented out more dvd's for each month in 2005:
SELECT x.rental_month, x.store_id, MAX(x.rental_count) FROM (
SELECT MONTH(r.rental_date) AS rental_month, i.store_id AS store_id, COUNT(*) AS rental_count
FROM rental r LEFT JOIN inventory i ON r.inventory_id = i.inventory_id
WHERE YEAR(r.rental_date) = 2005
GROUP BY MONTH(r.rental_date), i.store_id) x
GROUP BY x.rental_month, x.store_id
I was explicit by using aliases everywhere, you could probably omit some. Hopefully this helps...
Edit: Dirty hack:
SELECT x.rental_month, x.store_id, MAX(x.rental_count) FROM (
SELECT MONTH(r.rental_date) AS rental_month, i.store_id AS store_id, COUNT(*) AS rental_count
FROM rental r LEFT JOIN inventory i ON r.inventory_id = i.inventory_id
WHERE YEAR(r.rental_date) = 2005
GROUP BY MONTH(r.rental_date), i.store_id
ORDER BY MONTH(r.rental_date) ASC, COUNT(*) DESC) x
GROUP BY x.rental_month
Ref:
http://kristiannielsen.livejournal.com/6745.html
But then does this satisfy you, seeing as you do already have a working query...

mySQL Sub Select needed

I have three tables, libraryitems, copies and loans.
A libraryitem hasMany copies, and a copy hasMany loans.
I'm trying to get the latest loan entry for a copy only; The query below returns all loans for a given copy.
SELECT
libraryitems.title,
copies.id,
copies.qruuid,
loans.id AS loanid,
loans.status,
loans.byname,
loans.byemail,
loans.createdAt
FROM copies
INNER JOIN libraryitems ON copies.libraryitemid = libraryitems.id AND libraryitems.deletedAt IS NULL
LEFT OUTER JOIN loans ON copies.id = loans.copyid
WHERE copies.libraryitemid = 1
ORDER BY copies.id ASC, loans.createdAt DESC
I know there needs to be a sub select of some description in here, but struggling to get the correct syntax. How do I only return the latest, i.e MAX(loans.createdAt) row for each distinct copy? Just using group by copies.id returns the earliest, rather than latest entry.
Image example below:
in the subquery , getting maximum created time for a loan i.e. latest entry and joining back with loans to get other details.
SELECT
T.title,
T.id,
T.qruuid,
loans.id AS loanid,
loans.status,
loans.byname,
loans.byemail,
loans.createdAt
FROM
(
SELECT C.id, C.qruuid, L.title, MAX(LN.createdAt) as maxCreatedTime
FROM Copies C
INNER JOIN libraryitems L ON C.libraryitemid = L.id
AND L.deletedAt IS NULL
LEFT OUTER JOIN loans LN ON C.id = LN.copyid
GROUP BY C.id, C.qruuid, L.title) T
JOIN loans ON T.id = loans.copyid
AND T.maxCreatedTime = loans.createdAt
A self left join on loans table will give you latest loan of a copy, you may join the query to the other tables to fetch the desired output.
select * from loans A
left outer join loans B
on A.copyid = B.copyid and A.createdAt < B.createdAt
where B.createdAt is null;
This is your query with one simple modification -- table aliases to make it clearer.
SELECT li.title, c.id, c.qruuid,
l.id AS loanid, l.status, l.byname, l.byemail, l.createdAt
FROM copies c INNER JOIN
libraryitems li
ON c.libraryitemid = li.id AND
li.deletedAt IS NULL LEFT JOIN
loans l
ON c.id = l.copyid
WHERE c.libraryitemid = 1
ORDER BY c.id ASC, l.createdAt DESC ;
With this as a beginning let's think about what you need. You want the load with the latest createdAt date for each c.id. You can get this information with a subquery:
select l.copyid, max(createdAt)
from loans
group by l.copyId
Now, you just need to join this information back in:
SELECT li.title, c.id, c.qruuid,
l.id AS loanid, l.status, l.byname, l.byemail, l.createdAt
FROM copies c INNER JOIN
libraryitems li
ON c.libraryitemid = li.id AND
li.deletedAt IS NULL LEFT JOIN
loans l
ON c.id = l.copyid LEFT JOIN
(SELECT l.copyid, max(l.createdAt) as maxca
FROM loans
GROUP BY l.copyid
) lmax
ON l.copyId = lmax.copyId and l.createdAt = lmax.maxca
WHERE c.libraryitemid = 1
ORDER BY c.id ASC, l.createdAt DESC ;
This should give you the most recent record. And, the use of left join should keep all copies, even those that have never been leant.