SQL reservation hotel room query - php

I want to create a reservation hotel's room using PHP.
My tables are: rooms and reservations:
rooms
room_id | seats
And I have 4 records in this table. 2x 2 seats room and 2x 4 seats room.
reservation
reservation_id | room_id | from_time | to_time
I want to choose free rooms at any given time.
My query did not take into account every second room.
SELECT *
FROM `rooms`
LEFT JOIN `reservation` ON `rooms.room_id` =`reservation.room_id`
WHERE
`seats` = 2
AND '01/01/2016'BETWEEN `reservation.from_time` AND `reservation.to_time`
or '11/02/2016' BETWEEN `reservation.from_time` AND `reservation.to_time`

Use backticks around the fields that have reserved names and also use braces around the conditions in the where clause
SELECT *
FROM `rooms` r
left outer join `reservation` rv on r.`room_id`=rv.`room_id`
where `seats`=2
AND (
'01/01/2016'BETWEEN rv.`from` AND rv.`to`
or
'11/02/2016' BETWEEN rv.`from` AND rv.`to`
)

Related

Performing a query on the basis of the results of another SQL query

I have two tables with the name of customers and installments.i.e.
Customer Table:
id | name |contact |address
1 | xyz |33333333|abc
2 | xrz |33322333|abcd
Installments Table:
id | customer_id |amount_paid |amount_left | date
1 | 1 |2000 |3000 | 13/05/2017
2 | 1 |2000 |1000 | 13/06/2017
Now, I want to fetch the latest installment row from installments table for every user, I can use that with the following query,
SELECT * FROM installments WHERE customer_id=1 ORDER BY `id` DESC LIMIT 1
Now, the issue is that I want to do it for all the customer ids. I tried sub query thing but that doesn't supports multiple rows. I tried to store all customer IDs in an array in PHP and used "IN" but because the number of customers is more than 3000, it takes too long and returns an error of execution time exceeded.
Any kind of help or tip will be really appreciated. Thanks
SELECT
Customers.CustomerId,
Customers.Name,
Customers.Contact,
Customers.Address,
Installments.InstallmentId,
Installments.AmountPaid,
Installments.AmountLeft,
Installments.Date
FROM
Customers
INNER JOIN
(
SELECT
MAX( InstallmentId ) AS MaxInstallmentId,
CustomerId
FROM
Installments
GROUP BY
CustomerId
) AS LatestInstallments ON Customers.CustomerId = LatestInstallments.CustomerId
INNER JOIN Installments ON LatestInstallments.MaxInstallmentId = Installments.InstallmentId
you can do something like this
SELECT c.*,I.* FROM Customer_Table c LEFT JOIN Installments_Table I ON c.id=I.customer_id ORDER BY c.id DESC LIMIT 1
If You wants to add limit of list then only set limit else leave limit part. Untill I got Your Question this will be help you. else your problem can be something else.
SELECT cust.*,inst_disp.* FROM Customer AS cust
LEFT JOIN (
SELECT MAX(id) AS in_id, customer_id
FROM installments
GROUP BY customer_id
) inst
ON inst.customer_id = cust.id
LEFT JOIN installments as inst_disp ON inst_disp.id = inst.in_id

Counting field total against another table column value

room | beds available | ****table room****
==================================
room1 | 4
room2 | 2
room3 | 4
room | occupant | ****table occupant****
==================================
room1 | arnold
room1 | berry
room2 | charles
room2 | daisy
room3 | eric
room3 | frank
room3 | greg
I looking to get the following output:
No. of rooms with beds available: 2 || Rooms with beds available: room 1, room 3
i reckon i need to
store count* for each unique room and store the count as an array
subtract beds available for each room against this array
display the room name when there's a result > 0
How should the php snippet code look like?
These SQL queries will do the trick.
Remember, you can remove unwanted columns to save the amount of data you're processing...
select r.id AS 'room',
r.beds as 'total_beds',
count(o.occupant) as 'taken_beds',
r.beds-count(o.occupant) as 'free_beds'
FROM room r LEFT JOIN occupant o ON r.id = o.room
GROUP BY r.id
HAVING r.beds > count(o.occupant)
This will only return the rooms that are not full.
If at any other point, you wish to return full rooms too, simply remove the "HAVING" clause
select r.id AS 'room',
r.beds as 'total_beds',
count(o.occupant) as 'taken_beds',
r.beds-count(o.occupant) as 'free_beds'
FROM room r LEFT JOIN occupant o ON r.id = o.room
GROUP BY r.id
I think this all can be done with a simple SQL query that should look something like this:
SELECT room.beds AS beds, COUNT(occupant.occupant) AS beds_occupied
FROM room
LEFT JOIN occupant ON occupant.room = room.room
GROUP BY room
HAVING (beds - beds_occupied) > 0;
You can use the 'HAVING' clause which is similar to the WHERE clause but works with aggregates. I haven't run this exact query on your exact tables so there might be a typo, however, I hope the idea what the query is supposed to do is clear.
So something like this where you can change how many available beds you need..
SELECT r.*, COUNT(*) AS occupied, r.beds - COUNT(*) AS rest
FROM room r
LEFT JOIN occupant o ON r.room = o.room
GROUP BY r.room
HAVING r.beds - occupied >= 1
MySQL offers GROUP_CONCAT to aggregate strings:
select
count(*) as number_of_rooms,
group_concat(room) as rooms
from room r
where beds_available >
(
select count(*)
from occupant o
where o.room = r.room
);
This selects rooms with more beds available than occupied and then aggregates the resulting rows to one row containing the number of available rooms and a string with the room names comma-separated.

Retrieving available rooms and their beds for a given date range from database

I have read carefuly through all the posts on here about this topic, but my question has one extra catch, where I need to get available beds of each room because its a hostel and some rooms are shared.
Now I have tried doing this in PHP, but then realized I haven't even taken in account the date range. So now I am thinking since I already have a query that retrieves all reservations occurring within a given date range and then compare the retrieved rooms and their beds with the room table and show only rooms and their beds that are not occupied. But I can't figure out how to work around the beds because they are not an entity, only a count of total beds in each room. But then a reservation says for which bed in the room that reservation is made..
Here are my tables
Rooms
Reservations
Now the query that I use to retrieve all reserved rooms and their beds is
SELECT rooms_id, bed
FROM reservations
WHERE `to` > '2016-02-18' AND `from` < '2016-02-24'
The first date if my input variable $from and the second date is input variable $to and it allows to retrieve not only rooms that have a reservation that starts within $from and $to but also all reservations that started before the date range and end inside, started inside and end after the date range and finally reservations that started before and end after the date range. So this exact query above would return the following table
which I can then visualize in my app like this
But this is where I get stuck. I have no idea how to match the data I have in order to find all available rooms BUT ALSO THE BEDS.
The desired table AVAILABLE ROOMS for the date range from '2016-02-18' and to '2016-02-24' should look like this:
|||||||||||||||||||||||||||||||||
|| rooms_id || bed_number ||
||||||||||||||||||||||||||||
|| 1 || 1 ||
----------------------------
|| 1 || 2 ||
----------------------------
|| 2 || 5 ||
----------------------------
|| 2 || 6 ||
----------------------------
|| 2 || 7 ||
----------------------------
|| 2 || 8 ||
----------------------------
You can see this in the picture where I show how it looks in my app. The only rooms and beds that are available between the two dates are the Luxury Room and its bed number 1 and bed number 2 and the Dorm rooms beds 5,6,7,8 because 1-4 have a reservation occurring on at least on one of the desired dates
The only idea I had was using NOT IN, but that only works if I didn't care about the beds and also the output is of here it is
SELECT *
FROM `rooms`
WHERE `id` NOT IN
(SELECT rooms_id FROM reservations WHERE `to` > '2016-02-18' AND `from` < '2016-02-24')
instead of what I "sketched" above
I'd appreciate any tips and ideas on how approach this.
Part of me worries that this will all come down to me not treating the beds as entities and having to do that, even though as I will never be storing any sort of information on the beds such as their color, position, quality, price, etc...
REPLY TO #Paul-Spiegel
That is amazing, but is there any way to get the free beds as numbers as well instead of total number. Because then when the person makes a reservation I have to assign it to on of the beds. So if the result could be
| room_id | title | beds_total | available_bed_nrs |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1 | luxury room | 2 | 1, 2 |
| 2 | dorm room | 8 | 5, 6, 7, 8 |
instead of
You can get a number of free rooms (and the reserved room numbers) with this query:
set #from := '2016-02-18';
set #to := '2016-02-24';
set #beds := 1;
SELECT rm.id, rm.title, rm.beds,
rm.beds - IFNULL(rv.num_reserved_beds, 0) AS num_free_beds,
rv.reserved_bed_nrs
FROM rooms rm
LEFT JOIN (
SELECT rv.rooms_id,
COUNT(1) as num_reserved_beds,
GROUP_CONCAT(rv.bed) as reserved_bed_nrs
FROM reservations rv
WHERE rv.from < #to
AND rv.to > #from
GROUP BY rv.rooms_id
) rv ON rv.rooms_id = rm.id
HAVING num_free_beds >= #beds
You can now parse reserved_bed_nrs, loop over all beds per room and pick the beds that are not in reserved_bed_nrs.
Explaination:
Get all beds reserved within date range (excluding):
SELECT *
FROM reservations r
WHERE r.from < #to
AND r.to > #from;
Group by room, count the number of reserved rooms and store all numbers of reserved rooms in one string field:
SELECT rv.rooms_id,
COUNT(1) as num_reserved_beds,
GROUP_CONCAT(rv.bed) as reserved_bed_nrs
FROM reservations rv
WHERE rv.from < #to
AND rv.to > #from
GROUP BY rv.rooms_id
Join (LEFT JOIN) rooms with the given result calculate the number of free beds and compare it with the number of beds you want to book.
Update How to get free (not reserved) beds:
If you don't have a table with all existing beds, you will need some kind of sequece numbers. Assuming a room can have a maximum of 100 beds you can create a sequence table with 100 numbers:
CREATE TABLE `sequence` (
`nr` TINYINT(3) UNSIGNED NOT NULL,
PRIMARY KEY (`nr`)
) select d1.d*10 + d0.d + 1 as nr from
(select 0 d union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) d0,
(select 0 d union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) d1
Now it's possible to list all existing beds by cross joining the tables rooms and sequence:
SELECT *
FROM rooms rm
CROSS JOIN sequence seq
WHERE seq.nr <= rm.beds
To list all not resverd beds you can combine it with a query for reserved beds (select all beds that are not reserved within the booking date range):
SELECT *
FROM rooms rm
CROSS JOIN sequence seq
WHERE seq.nr <= rm.beds
AND (rm.id, seq.nr) NOT IN (
SELECT rv.rooms_id, rv.bed
FROM reservations rv
WHERE rv.from < '2016-02-24'
AND rv.to > '2016-02-18'
)
This can also be done with NOT EXISTS or excluding LEFT JOIN.
You also can skip the creation of the sequence table use the creation code as subselect:
SELECT *
FROM rooms rm
CROSS JOIN (
select d1.d*10 + d0.d + 1 as nr
from
(select 0 d union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) d0,
(select 0 d union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) d1
) seq
WHERE seq.nr <= rm.beds
AND (rm.id, seq.nr) NOT IN (
SELECT rv.rooms_id, rv.bed
FROM reservations rv
WHERE rv.from < '2016-02-24'
AND rv.to > '2016-02-18'
)
http://sqlfiddle.com/#!9/a0d61/5

Use commaseparated mySQL column value in LIKE

I have column in my database named as foodtype . Table name is restaurant and the column foodtype has comma separated values (as Indian,Chinese).
Now, when a person select Chinese then the i want mysql query should return restaurant where foodtype is Chinese.
Query should become like as below:
SELECT * FROM restaurant WHERE cityname='Chicago' and foodtype
LIKE ('%Chinese%')
And when a person select Indian then the i want mysql query should return restaurant where foodtype is Indian.
Query should become like as below:
SELECT * FROM restaurant WHERE cityname='Chicago' and foodtype
LIKE ('%Indian%')
And when a person select Indian and Chinese both then the i want mysql query should return restaurant where foodtype is Indian and Chinese.
Query should become like as below:
SELECT * FROM restaurant WHERE cityname='Chicago' and foodtype
LIKE ('%Indian%,%Chinese%')
Please let me know how can i achieve this.
Use FIND_IN_SET()
SELECT *
FROM restaurant
WHERE cityname='Chicago'
and find_in_set(foodtype, 'Indian') > 0
and find_in_set(foodtype, 'Chinese') > 0
But actually you are better off by chaning your table structure. Never, never, never store multiple values in one column!
To achieve that you can add 2 other tables to your DB
foodtypes table
---------------
id
name
restaurant_foodtypes
--------------------
restaurant_id
foodtype_id
Example data:
foodtypes
id | name
1 | chinese
2 | indian
restaurant_foodtypes
restanrant_id | foodtype_id
1 | 1
1 | 2
Then you can select restaurants having both foodtypes like this
select r.name
from restaurants r
join restaurant_foodtypes rf on rf.restaurant_id = r.id
join foodtypes f on rf.foodtype_id = f.id
where f.name in ('indian','chinese')
group by r.name
having count(distinct f.name) = 2

MySQL selecting row for attendance case

I have these tables:
table 1 : attendance
-------------------------------
ID | DATE | EMPLOYEE_ID |
-------------------------------
1 2013-09-10 1
2 2013-09-10 2
3 2013-09-10 3
-------------------------------
table 2: employee
---------------
ID | NAME |
---------------
1 Smith
2 John
3 Mark
4 Kyle
5 Susan
6 Jim
---------------
My actual code to show employee option.
while($row=mysql_fetch_array($query)){
echo "<option value='$row[employee_id]'>$row[first_name] $row[last_name]</option>";
}
?>
How can i show the list of employee that not registered in table 1?
The condition is if an employee already registered in table 1, they won't appear on the option.
I want to show the list in <option>'s of <select> element. So it will return: kyle, susan, jim.
Please tell me the correct query or if there is any better option, it'll be good too. Please give some solution and explain. Thank you very much
UPDATE / EDIT:
it also based on current date, if in table 1 have no latest date e.g. today it's 2013-09-15. It will show all of employee.
You can do this with a left join and then checking for no matches:
select e.*
from employee e left outer join
attendance a
on e.id = a.employee_id
where a.employee_id is null;
This is probably the most efficient option in MySQL.
EDIT:
To include a particular date, add the condition to the on clause:
select e.*
from employee e left outer join
attendance a
on e.id = a.employee_id and a.date = date('2013-09-20')
where a.employee_id is null;
If I understood correctly this should work, get all employees whose ID is not in the attendance table.
SELECT * FROM employee WHERE employee.ID NOT IN
(SELECT EMPLOYEE_ID FROM attendance)

Categories