hotel reservation system SQL: identify any room available in date range - mysql

(In case this seems familiar: I asked a different question similar to this one, but I just got a note that the site design has changed and now the owners do want a way to search for any room that is available between a range of dates. So this is a new question...)
I'm working on a hotel reservation system and I need to find which rooms are available between a range of dates. The existing 'availability' table schema is simple, the columns are:
room_id
date_occupied - a single day that is occupied (like '2011-01-01')
So, if, for example, room #6 is occupied from January 1 to January 5, five rows are added to the availability table, one for each day that room is occupied.
I'm trying to figure out the query to find what rooms are available between a start and end date (while also filtering on some other parameters like non-smoking, bed size, etc.)... in pseudo-SQL it would be sort of like:
SELECT (a list of room IDs) FROM rooms, availability
WHERE rooms.nonsmoking = 1
AND rooms.singlebed = 1
AND nothing between start_date and end_date is in availability.date_occupied
As with my other question, I'm a bit stuck trying to determine the exact query and how to join the tables it in the most efficient way. Thanks for the help.

If I understood your db structure properly, you need to find a row in rooms with no corresponding rows in availability.
SELECT r.*
FROM rooms r
LEFT JOIN availability a ON (r.id = a.room_id
AND a.date_occupied BETWEEN :start_date AND :end_date)
WHERE a.id IS NULL

Related

MYSQL Test multiple dates in one record against a table with specific dates

FIrst time question so I will try and stay on point.
I have a system for recording staff attendance -
Tables:
tbl_Payperiod - (ID, Payperiod, StartDate)
tbl_Rota - (RotaID, PayperodID,EmployeeID, MonDate, Monstarttime, Monfinishtime, TuesDate, Tuesstarttime etc..)
The above works as I want it too and I can capture different variants of the working day e.g. annual leave, sickness etc.
The system is accessed through a browser using PHP (PHPRUNNER)
The Question: What I need to do is check if the date is a Public holiday.
I did this in previous setups in excel (using array and lookup) but I cannot figure out how to test it in MYSQL
I can create a table to hold the holiday dates and have this updated manually.
So how would I check and 'mark' the date in the tbl_rota.MonPH = True or false
Once I can 'mark' the date I can then apply the corresponding pay rate..
Thanks in advance for any assistance
Left join tbl_Rota with the holidays table 7 times, once for each day of the week. Then set each dayPH field to true or false depending on whether the join was successful.
UPDATE tbl_Rota AS r
JOIN tbl_Holidays AS hMon ON r.MonDate = hMon.date
JOIN tbl_Holidays AS hTue ON r.TueDate = hTue.date
...
SET r.MonPH = hMon.date IS NOT NULL,
r.TuePH = hTue.date IS NOT NULL,
...
Having separate columns in tbl_Rota for each day of the week really complicates this. If you had separate rows for each day, you could just do a single left join with the holiday table.
A normalised approach might look a little like this:
RotaID,
PayperodID,
EmployeeID
RotaID
Start_dt
Finish_dt

How to detect if cell value appears twice in one day

Apologies in advance for the vagueness of the title. This is an issue that is stumping me and I struggled to get any more specific.
First of all, to help visualise my problem I've uploaded a photo of my database to http://imgur.com/a/rTyn8.
Basically, I've been adding up payments in my database and have run into a complex (contextually to my understanding of MySQL, anyway, which is mediocre at best) problem.
I want to calculate the number of times any given customer (customer_id) has a job_id payment of both 17 & 12 in one day. If they do, I then want to calculate the added cost of them. However, I'd like to run this query throughout the whole database between 2 specific dates (eg. 2016-01-01 -- 2016-05-06) and generate the total income during this period.
In the picture I link to above, the customer with a customer_id of 1658 has two payments - one of them with a job_id of 12, one of them 17. Therefore, I would like to add the the cost of both these (6.00 + 19.80) together, as well as anyone else who falls under this criteria, and come to a total figure.
Just to clarify, the customer (with a customer_id of 1913) below the rows I refer to would also fall under this category.
I've tried my best at getting something together, but admittedly I'm completely lost.
Thanks in advance,
Liam
Join the table to itself, once for each job type:
select
count(*) quantity,
sum(a.cost + b.cost) total
from mytable a
join mytable b on b.customer_id = a.customer_id
and a.date = b.date
where a.date between '2016-01-01' and '2016-05-06'
and a.job_id = 17
and b.job_id = 12
If you want a breakdown by customer_id, add a.customer_id to the selected columns and add group by customer_id.

mysql query which needs to do calculations

I'm not so great with sql but I think what I want should be possible, I'm just not sure how to do it.
I have a site which allows users to list their youth hostel. Potential customers can then browse these hostels and place bookings.
Tables:
hostels
-------
max_guests
bookings
-------
hostel_id
check_in
check_out
guests
When users search the site I allow them to select a checkin date, a checkout date, a place name, and the number of guests. Note that each hostel has a maximum number of guests which they can accommodate, for example 20.
So say previously a booking has been made at a particular hostel for 8 people during the dates selected for the search, then I need to know that the maximum number of guests that particular hostel can accommodate between those dates is now 12, and it would only appear in searches for <= 12 guests.
Likewise if that hostel had a couple more bookings and was at it's maximum capacity, then it shouldn't turn up in the search at all.
At the moment I have a query which gets all the hostels but if there's any bookings for a hostel between the selected dates, then it's excluded from the results. I'd like to be able to get all the bookings for each hostel during the selected dates, count the numbers of guests booked in, and if that number subtracted from the max_guests of the hostel gives a result which is lower than the number of guests specified in the search, then exclude it from the results.
at the moment my query looks like:
SELECT DISTINCT `Hostel`.`id`, `Hostel`.*, `Hostel`.`id`
FROM `hostelsdatabase`.`hostels` AS `Hostel`
LEFT JOIN `hostelsdatabase`.`bookings` AS `bookings` ON (`bookings`.`hostel_id` = `Hostel`.`id`)
LEFT JOIN `hostelsdatabase`.`users` AS `User` ON (`Hostel`.`user_id` = `User`.`id`)
WHERE ((`Hostel`.`address_one` LIKE '%london%') OR (`Hostel`.`address_two` LIKE '%london%') OR (`Hostel`.`city` LIKE '%london%'))
AND `Hostel`.`maxguests` >= 4
AND NOT EXISTS (SELECT * FROM bookings WHERE `bookings`.`hostel_id` = `Hostel`.`id` AND NOT('2013-07-31' > `bookings`.`checkout` OR '2013-08-02' < `bookings`.`checkin`))
LIMIT 10
Can anyone tell me how I could modify that query to make it do what I need?

MySQL Count from another table with condition

I'm building upon a solution I developed a while ago - ticket booking. I now need to add a feature to allow tickets to be "held" during purchasing to make them appear as sold to another customer coming to the site (preventing double-booking).
Details on the original tables are in my previous question, MySQL - Trying to count tickets sold for an event. The tickets table now has a held_until DateTime value, and a paid tinyint (bool) value.
I need the events.capacity - COUNT(tickets.id) AS available to now only count those tickets which either:
Have a paid value of 1
Have a paid value of 0 and held_until > now()
I've tried adding WHERE and HAVING clauses, but these prevent events where no tickets have been sold from showing. I also had a play around with nested queries but didn't get anywhere with that either...
Any suggestions?
Modify Andomar's suggested query (in the other topic) as below :
change
ON events.id = tickets.event_id
to
ON ( events.id = tickets.event_id AND ( tickets.paid = 1 OR tickets.held_until > now() )
Even if all the required information can be found in your other topic, it would probably have been a good idea to repeat some relevant details here.

Tricky Rails3/mysql query

In rails 3 (also with meta_where gem if you feel like using it in your query), I got a really tricky query that I have been banging my head for:
Suppose I have two models, customers and purchases, customer have many purchases. Let's define customers with at least 2 purchases as "repeat_customer". I need to find the total number of repeat_customers by each day for the past 3 months, something like:
Date TotalRepeatCustomerCount
1/1/11 10 (10 repeat customers by the end of 1/1/11)
1/2/11 15 (5 more customer gained "repeat" status on this date)
1/3/11 16 (1 more customer gained "repeat" status on this date)
...
3/30/11 150
3/31/11 160
Basically I need to group customer count based on the date of creation of their second purchase, since that is when they "gain repeat status".
Certainly this can be achieved in ruby, something like:
Customer.includes(:purchases).all.select{|x| x.purchases.count >= 2 }.group_by{|x| x.purchases.second.created_at.to_date }.map{|date, customers| [date, customers.count]}
However, the above code will fire query on the same lines of Customer.all and Purchase.all, then do a bunch of calculation in ruby. I would much prefer doing selection, grouping and calculations in mysql, since it is not only much faster, it also reduces the bandwith from the database. In large databases, the code above is basically useless.
I have been trying for a while to conjure up the query in rails/active_record, but have no luck even with the nice meta_where gem. If I have to, I will accept a solution in pure mysql query as well.
Edited: I would cache it (or add a "repeat" field to customers), though only for this simplified problem. The criteria for repeat customer can change by the client at any point (2 purchases, 3 purchases, 4 purchases etc), so unfortunately I do have to calculate it on the spot.
SELECT p_date, COUNT(customers.id) FROM
(
SELECT p_date - INTERVAL 1 day p_date, customers.id
FROM
customers NATURAL JOIN purchases
JOIN (SELECT DISTINCT date(purchase_date) p_date FROM purchases) p_dates
WHERE purchases.purchase_date < p_date
GROUP BY p_date, customers.id
HAVING COUNT(purchases.id) >= 2
) a
GROUP BY p_date
I didn't test this in the slightest, so I hope it works. Also, I hope I understood what you are trying to accomplish.
But please note that you should not do this, it'll be too slow. Since the data never changes once the day is passed, just cache it for each day.