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;
Related
For a swimming school, parents pay tuition fees for their kids. On a monthly basis, I need to collect the payable fees from a table that holds open balances, but only if their child is marked as 'active'. Here is the catch: an open balance for an "inactive child" is also considered payable IF at least one other child in the same family is marked active.
I have the following MySQL query:
SELECT sum(b.amount) as amount, f.name, u.email
FROM balance b
LEFT JOIN student s ON b.user_id = s.user_id
LEFT JOIN student_family sf ON s.user_id = sf.user_id
LEFT JOIN family f ON sf.fam_id = f.fam_id
LEFT JOIN user u ON u.user_id = s.user_id
WHERE b.status = 'open' AND s.active = 1
GROUP BY u.email
I currently check s.active=1 but this part needs to be adjusted in such as way that we check if there is at least one student in the same family that have active=1.
My attempt (that didn't work):
...
WHERE b.status = 'open' AND ( SELECT max(active) from student s2 LEFT JOIN student_family sf2 ON sf2.user_id = s2.user_id WHERE sf2.fam_id = sf.fam_id ) = 1
How to accomplish this query?
Using the exists clause will resolve the issue:
SELECT sum(b.amount) as amount, f.name, u.email
FROM balance b
LEFT JOIN student s ON b.user_id = s.user_id
LEFT JOIN student_family sf ON s.user_id = sf.user_id
LEFT JOIN family f ON sf.fam_id = f.fam_id
LEFT JOIN user u ON u.user_id = s.user_id
WHERE b.status = 'open' AND exists
(select 1 from student s1
JOIN student_family sf1 ON s1.user_id = sf1.user_id
JOIN family f ON sf1.fam_id = f1.fam_id
where s1.active = 1 and f.fam_id=f1.fam_id)
GROUP BY u.email
You could use a IN clause in your WHERE to see if the student_family.user_id is in a family with at least one active student:
Something like:
SELECT
sum(b.amount) as amount,
f.name,
u.email
FROM
balance b
LEFT JOIN student s ON b.user_id = s.user_id
LEFT JOIN student_family sf ON s.user_id = sf.user_id
LEFT JOIN family f ON sf.fam_id = f.fam_id
LEFT JOIN user u ON u.user_id = s.user_id
WHERE
b.status = 'open'
AND (sf.user_id IN (SELECT sf.user_id FROM student_family sf INNER JOIN student s ON sf.user_id = s.user_id WHERE s.active = 1 GROUP BY sf.user_id ))
GROUP BY
u.email
I am trying to get a query to work with php. Basically it should first check that a specific booking has not been made then query database for all available vehicles and drivers.
This is the query so far:
IF EXISTS(SELECT * FROM bookings LEFT JOIN status WHERE bookings.status_id = '.$booking.')
BEGIN
select *
FROM bookings as b
LEFT JOIN drivers as d where b.driver_id !="d.driver_id"
LEFT JOIN vehicles as v where b.vehicle_id !="v.vehicle_id"
LEFT JOIN status as s where b.status_id !="'.$booking.'"
ORDER BY b.bookingDate DESC
END
Join bookings:
SELECT *
FROM bookings as b
LEFT JOIN drivers as d where b.driver_id !="d.driver_id"
LEFT JOIN vehicles as v where b.vehicle_id !="v.vehicle_id"
LEFT JOIN status as s where b.status_id !="'.$booking.'"
CROSS JOIN (
SELECT 1
FROM bookings
WHERE bookings.status_id = '.$booking.'
LIMIT 1
) t
ORDER BY b.bookingDate DESC
I currently have a DB with 4 table:
Customer
Sale (CustID & RoomID fk)
Room (ManID fk)
Manager
I am trying to get the cities in which the customers reside in as a percentage. However, I only want to show the the customers who made a purchase using a said manager. Currently this comes through as an amount either under or over 100% total.
When this query is isolated to the customer table, the result adds up to 100% correctly, however it does need to be taken from sales using the manager username as a parameter. This is what I currently have:
SELECT
(COUNT(c.city) / (SELECT Count(CustID) FROM Customer) * 100) AS percent, c.city AS City
FROM sale s
INNER JOIN customer c ON s.CustID = c.CustID
INNER JOIN room r ON s.RoomID = r.RoomID
INNER JOIN manager m ON r.ManID = m.ManID
WHERE m.UserName = 'manager123'
GROUP BY c.City;
EDIT:
I wish to add that the CustID may occur more than once within the sales table, additionally, it may be null. Not sure if is required to use distinct when counting.
Having isolated my issue to this, I felt it necessary to mention as I am still unable to exclude these from the result and an incomplete figure (totaling <100% still showing)
Here you go.
SELECT NoCity,COUNT(*)/percent *100 as percentOfCustomerBasedonManager,City
FROM (
SELECT m.UserName,COUNT(c.city) AS NoCity, COUNT(c.CustID) AS percent,c.city AS City FROM
sale s
INNER JOIN customer c ON s.CustID = c.CustID
INNER JOIN room r ON s.RoomID = r.RoomID
INNER JOIN manager m ON r.ManID = m.ManID
GROUP BY c.City ) cityTable WHERE cityTable.UserName = 'manager123'
This may help:
SELECT city, ((city_count)/(customer_count)*100) as Percent
(
SELECT city, COUNT(*) as city_count, customer_count
FROM
(
SELECT distinct c.city
FROM sale s
INNER JOIN customer c ON s.CustID = c.CustID
INNER JOIN room r ON s.RoomID = r.RoomID
INNER JOIN manager m ON r.ManID = m.ManID
WHERE m.UserName = 'manager123'
GROUP BY c.city
) as t1
LEFT JOIN
(
SELECT city as city2, COUNT(*) as customer_count
FROM
(
SELECT c.city, distinct c.CustID
FROM sale s
INNER JOIN customer c ON s.CustID = c.CustID
INNER JOIN room r ON s.RoomID = r.RoomID
INNER JOIN manager m ON r.ManID = m.ManID
WHERE m.UserName = 'manager123'
GROUP BY c.city
) as t2
) as t3 on t1.city=t3.city2
) as master
GROUP BY city
Ok, here is the solution I came to, with a huge thanks to Lim Neo who pointed me in the right direction.
SELECT
round(COUNT(c.city) /
(
SELECT Count(b.CustID)
FROM sale s
INNER JOIN customer c ON s.CustID = c.CustID
INNER JOIN room r ON s.RoomID = r.RoomID
INNER JOIN manager m ON r.ManID = m.ManID
WHERE m.UserName = 'Manager123'
) * 100)
AS percent, c.city AS City
FROM sale s
INNER JOIN customer c ON s.CustID = c.CustID
INNER JOIN room r ON s.RoomID = r.RoomID
INNER JOIN manager m ON r.ManID = m.ManID
WHERE m.UserName = 'Manager123'
GROUP BY c.City;
I've got a database set up as follows:
http://sqlfiddle.com/#!9/2c5fc
What I'm looking to do is get a list of each store, and their last budget amount (Each schedule's total in the budget * their apportionment for that schedule / 100).
Some stores might not have apportionments set for the last budget, so I need the last budget where they have an apportionment set, or NULL if no apportionment has been set or no budget exists.
I've got the following SQL query:
SELECT s.StoreID,s.CentreID, budgetcalc.amount, budgetcalc.BudgetDate FROM store as s
LEFT JOIN centre on s.CentreID = centre.CentreID
LEFT JOIN (SELECT BudgetDate, SUM(sch.Amount*appt.Percentage/100) as amount, appt.StoreID from budget b
INNER JOIN schedule as sch on b.BudgetID = sch.BudgetID
INNER JOIN apportionment as appt on sch.ScheduleID = appt.ScheduleID
GROUP BY appt.StoreID, b.BudgetID
ORDER BY STR_TO_DATE(b.BudgetDate,'%d-%m-%y') DESC
) as budgetcalc on s.StoreID = budgetcalc.StoreID
GROUP BY s.StoreID
ORDER BY s.StoreID, STR_TO_DATE(budgetcalc.BudgetDate,'%d-%m-%y') DESC;
However this has the issue of not returning the last year, it will return a previous year seemingly at random regardless of the order in which I return the subquery.
It is difficult to work out from just the table declarations, but you need to get the latest budget date for each store, then join that against your sub query that gets the budget details for each store / budget.
Untested but something like this:-
SELECT s.StoreID,
s.CentreID,
budgetcalc.amount,
budgetcalc.BudgetDate
FROM store as s
LEFT OUTER JOIN centre ON s.CentreID = centre.CentreID
LEFT OUTER JOIN
(
SELECT appt.StoreID,
MAX(BudgetDate) AS latestBudgetDate
FROM budget b
INNER JOIN schedule as sch ON b.BudgetID = sch.BudgetID
INNER JOIN apportionment as appt ON sch.ScheduleID = appt.ScheduleID
GROUP BY appt.StoreID
) latest_budget
ON s.StoreID = latest_budget.StoreID
LEFT OUTER JOIN
(
SELECT BudgetDate,
appt.StoreID,
SUM(sch.Amount*appt.Percentage/100) as amount
FROM budget b
INNER JOIN schedule as sch ON b.BudgetID = sch.BudgetID
INNER JOIN apportionment as appt ON sch.ScheduleID = appt.ScheduleID
GROUP BY appt.StoreID, b.BudgetID
) as budgetcalc
ON latest_budget.StoreID = budgetcalc.StoreID
AND latest_budget.latestBudgetDate = budgetcalc.BudgetDate
ORDER BY s.StoreID,
budgetcalc.BudgetDate DESC;
With help from Kickstart, this is the answer that works:
SELECT s.StoreID,s.CentreID, SUM(sch.Amount*appt.Percentage/100) as amount, b.BudgetDate FROM store as s
INNER JOIN centre on s.CentreID = centre.CentreID
LEFT JOIN budget as b on b.BudgetID =
(
SELECT budget.BudgetID from budget
INNER JOIN schedule on budget.BudgetID = schedule.BudgetID
INNER JOIN apportionment on schedule.ScheduleID = apportionment.ScheduleID
where CentreID = s.CentreID AND apportionment.StoreID = s.StoreID
ORDER BY BudgetDate DESC limit 1
)
LEFT JOIN schedule as sch on b.BudgetID = sch.BudgetID
LEFT JOIN apportionment as appt on sch.ScheduleID = appt.ScheduleID AND appt.StoreID = s.StoreID
GROUP BY s.StoreID
ORDER BY s.StoreID ASC;
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.