Select specific records between two dateranges - php

I am trying to get unreserved hotel rooms according to a daterange which is given by user.
I have two tables
rooms with columns room_id, roomno
reservations with columns reserv_id, room_id, checkindate, checkoutdate
Im applying this query
$sql = "SELECT DISTINCT roomno
FROM rooms
LEFT JOIN reservation ON rooms.room_id = reservation.room_id
AND
(
(
(reservation.checkindate > '$newreservcheckin')
AND
(reservation.checkindate > '$newreservcheckout')
)
OR
(
(reservation.checkoutdate < '$newreservcheckin')
AND
(reservation.checkoutdate < '$newreservcheckout')
)
)";
$newreservcheckin and $newreservcheckout are date ranges provided by user
Let's say we have rooms with roomno as 100 and 101
now 100 is booked from 9-16-2016 to 9-18-2016
and 100 is also booked from 9-27-2016 to 9-29-2016
and room 101 is unreserved(meaning it has no record in reservation table)
Suppose if user gives dates 9-26-2016 to 9-30-2016 query will ignore the room no 100 entry which is from 9-27-2016 to 9-29-2016 but will show the room no 100 with dates from 9-16-2016 to 9-18-2016 and will show 101 room
How to mold the query so that it may not give the same room but the one with different dates?

You need to select the rooms which are either (A) Not booked or (B) Booked but outside the range you are querying for, below query should work:
select no from room r
where not exists (
select id from reservation where room_id = r.id)
union
select no from room r
where r.id not in (
select distinct room_id from reservation
where
checkindate between '2016-09-20' and '2016-09-22'
OR
checkoutdate between '2016-09-20' and '2016-09-22'
)
Here is the SQL Fiddle.

You can join every reservation that overlaps with new checking dates and select those rooms which have nothing to join. In my query I expect that new guests can checkin at the same date when previous would checkout. If not - change "<" to "<=" and ">" to ">=".
SELECT
r.roomno
FROM
rooms r
LEFT JOIN
reservations res ON res.room_id = r.room_id
AND res.checkindate < '$newreservcheckout'
AND res.checkoutdate > '$newreservcheckin'
WHERE res.reserv_id IS NULL

Related

Sum columns on different tables and multiply by value of a column on another table

I need to compute employees' monthly salaries based on meetings attended, deductions and bonuses given;
Employees have different pay per meeting based on their job position.
The solution is:
salary = (Pay_per_minute * meetings_attended) + bonuses - deductions ;
I have four tables:
Jobs: Id, title, pay_per_meeting
Employees: Id, Name, job_id
Bonuses: Id, amount, employee_id, date
Deductions: Id, amount, employee_id, date
Meetings: Id, employee_id, date
SELECT
COUNT(meetings.employee_id) as meetings_attended,
COUNT(deductions.amount) as debt,
COUNT(bonuses.amount) bonus,
(SELECT jobs.pay_per_attendance from jobs where jobs.id = (select job_id from employees where id=meetings.employee_id)) as pay,
((meetings_attended * pay) + bonus - debt) as salary
FROM meetings
JOIN deductions ON deductions.employee_id = meetings.employee_id
JOIN bonuses ON bonuses.employee_id = meetings.employee_id
WHERE meetings.employee_id = 1
GROUP BY MONTH(meetings.date), MONTH(deductions.date), MONTH(bonuses.date)
The above query returns many incorrect values whenever i remove the salary line but gives error of unknown column pay, meetings_attended, debt and bonus, am sure something is wrong with the grouping but i can't just see it.
You can't refer to column aliases in the same select list as they're defined, you need to refer to the underlying column. And a subquery can't access an aggregate calculated in the main query. You need to repeat the aggregate expression, or move everything into a subquery and do the calculation with it in an outer query.
Also, all your COUNT() expressions are going to return the same thing, since they're just counting rows (I assume none of the values can be NULL). You probably want COUNT(DISTINCT <column>) to get different counts, and you need to use a column that's unique, so they should be the primary key column, e.g. COUNT(DISTINCT deductions.id).
Another problem is that when you try to sum and count values when you have multiple joins, you end up with a result that's too high, because rows get duplicated in the cross product of all the tables. See Join tables with SUM issue in MYSQL. The solution is to calculate the sums from each table in subqueries.
SELECT m.month, m.meetings_attended, d.debt, b.bonus,
m.meetings_attended * j.pay_per_meeting + b.amount - d.amount AS salary
FROM (
SELECT MONTH(date) AS month, COUNT(*) AS meetings_attended
FROM meetings
WHERE employee_id = 1
GROUP BY month) AS m
JOIN (
SELECT MONTH(date) AS month, COUNT(*) AS bonus, SUM(amount) AS amount
FROM bonuses
WHERE employee_id = 1
GROUP BY month) AS b ON m.month = b.month
JOIN (
SELECT MONTH(date) AS month, COUNT(*) AS debt, SUM(amount) AS amount
FROM deductions
WHERE employee_id = 1
GROUP BY month) AS d ON m.month = d.month
CROSS JOIN employees AS e
JOIN jobs AS j ON j.id = e.job_id
WHERE e.employee_id = 1

Combining two SQL queries PDO

I'm quite stuck on the following issue. I have a series of tables:
What I want to do is get all the information on a room, assuming that the amount of bookings don't exceed the room number available for that Room.
So to get my Room details my SQL is this:
SELECT Rooms.RoomID as RoomID,
RoomName, NumOfRooms,
MaxPeopleExistingBeds,
MaxExtraBeds,
MaxExtraPeople,
CostPerExtraPerson,
MaximumFreeChildren,
IncludeBreakfast,
MinRate
FROM Rooms, RoomDetails
WHERE Rooms.AccommodationID = :aid AND
Rooms.RoomID = RoomDetails.RoomID
GROUP BY RoomName
Which upon return gets me a list of details for those rooms as follows:
I then use this query to get the number of bookings, and the ID of the room:
SELECT Booking.RoomID,
count(Booking.RoomID) as Bookings
FROM Booking
WHERE ArriveDate >= :aDate AND
DepartDate <= :dDate AND
AccommodationID = :aid
GROUP BY RoomID
I then combine both and feed the two arrays back in one array using this function:
public function get_availability($aid, $aDate, $dDate) {
$stmt = $this->db->prepare('SELECT Rooms.RoomID as RoomID, RoomName, NumOfRooms, MaxPeopleExistingBeds, MaxExtraBeds, MaxExtraPeople, CostPerExtraPerson, MaximumFreeChildren, IncludeBreakfast, MinRate FROM Rooms, RoomDetails WHERE Rooms.AccommodationID = :aid AND Rooms.RoomID = RoomDetails.RoomID GROUP BY RoomName');
$stmt->bindValue(':aid', $aid);
$stmt->execute();
$rooms = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt2 = $this->db->prepare('SELECT Booking.RoomID, count(Booking.RoomID) as Bookings FROM Booking WHERE ArriveDate >= :aDate AND DepartDate <= :dDate AND AccommodationID = :aid GROUP BY RoomID');
$stmt2->bindValue(':aid', $aid);
$stmt2->bindValue(':aDate', $aDate);
$stmt2->bindValue(':dDate', $dDate);
$stmt2->execute();
$bookings = $stmt2->fetchAll(PDO::FETCH_ASSOC);
$room = array($rooms, $bookings);
return (!empty($room)) ? $room : false;
}
The thing is, what I actually want to do is only return the room details where NumOfRooms is less than the number of Bookings.
So for instance where I have $bookings, if it tells me that for room ID 4, I have 3 bookings for a set period, and my NumOfRooms is 1. Then I know that I have no capacity that week to take any more bookings on. If however I have 1 booking and one capacity then that is still full. But if I have NumOfRooms of 2, and bookings amount to 1, I know I have room.
So basically if NumOfRooms > BookingCount then the room is available.
How can I amalgamate both queries and simplify my code to make this possible?
I.E to put it simply, how do I select all of the info from RoomDetails given an ArriveDate in Booking and a DepartDate and a RoomID, where NumOfRooms > count(Booking.RoomID) (Where it is within those dates and the room id is equal to the room id of Rooms).
Your problem can be solved by simply updating the SQL statement itself:
SELECT r.RoomID AS RoomID,
RoomName,
NumOfRooms,
MaxPeopleExistingBeds,
MaxExtraBeds,
MaxExtraPeople,
CostPerExtraPerson,
MaximumFreeChildren,
IncludeBreakfast,
MinRate
FROM Rooms r
JOIN RoomDetails rd
ON r.RoomID = rd.RoomID
JOIN (
SELECT b.RoomID,
AccommodationID,
count(b.RoomID) AS Bookings
FROM Booking b
WHERE ArriveDate >= :aDate
AND DepartDate <= :dDate
GROUP BY RoomID
) t
ON t.AccommodationID = r.AccommodationID
WHERE r.AccommodationID = :aid
AND t.Bookings < NumOfRooms
GROUP BY RoomName
You can select out all of the booking counts per room for the desired date range as a subquery, and then LEFT JOIN that subquery against the list of your rooms filtered by your desired AccommodationID and the desired NumOfRooms > BookingCount criteria. The key here is in the join type used for this subquery, as an inner join would limit your results to only rooms that actually had bookings.
SELECT Rooms.RoomID as RoomID,
RoomName, NumOfRooms,
MaxPeopleExistingBeds,
MaxExtraBeds,
MaxExtraPeople,
CostPerExtraPerson,
MaximumFreeChildren,
IncludeBreakfast,
MinRate,
BookingCount
FROM Rooms
INNER JOIN RoomDetails on Rooms.RoomID = RoomDetails.RoomID
LEFT JOIN (
SELECT Booking.RoomID,
count(Booking.RoomID) as BookingCount
FROM Booking
WHERE ArriveDate >= :aDate AND
DepartDate <= :dDate
GROUP BY Booking.RoomID
) RoomBookings ON Rooms.RoomID = RoomBookings.RoomID
WHERE Rooms.AccommodationID = :aid
AND NumOfRooms > BookingCount
GROUP BY RoomName

MySQL Need to show 0 when no value in right hand table of join, with cumulative

I have one table with a list of number of sales per month against product code and another with a list of months that can extend before or after the months that had a sale in. I need to results to show 0 sales if there were no sales in the month and for the cumulative to add this up. I have tried using case and if and getting it to put 0 if sales.sales was null but this did not work and I still just had blanks.
create table summary as (SELECT
q1.productid As productid,
q1.date AS Month_View,
q1.sales AS Monthly_Units_Sold,
(#runtot_sales := #runtot_sales + q1.sales) AS Cumulative_Sales
FROM
(SELECT
sales.productid,
dates.date,
if(sales.date is null,0,sales.sales) as sales
from
dates
left join sales on dates.date = sales.date
where
sales.productid = '$input1'
group by dates.date
ORDER BY date) AS q1);
";
Try COALESCE() function to return the first non-NULL value of a list Also see demo here
CREATE TABLE summary AS
(SELECT
q1.productid AS productid,
q1.date AS Month_View,
q1.sales AS Monthly_Units_Sold,
(
#runtot_sales := #runtot_sales + q1.sales
) AS Cumulative_Sales
FROM
(SELECT
sales.productid,
dates.date,
COALESCE(sales.sales, 0) AS sales
FROM
dates
LEFT JOIN sales
ON dates.date = sales.date
WHERE sales.productid = '$input1'
GROUP BY dates.date
ORDER BY DATE) AS q1) ;
MySQL COALESCE() function
You are misusing GROUP BY and therefore getting indeterminate results. See this: http://dev.mysql.com/doc/refman/5.5/en/group-by-extensions.html
If you're aggregating your items by product and date you probably want something like this.
SELECT sales.productid,
dates.date,
SUM(sales.sales) as sales
FROM dates
LEFT JOIN sales ON dates.date = sales.date
WHERE sales.productid = '$input1'
GROUP BY sales.productid, dates.date
ORDER BY /* i'm not sure what you're trying to do with the running total */
Note that SUM(sales.sales) handles the NULL values from your LEFT JOIN correctly. If the date doesn't join a sales row then sales.sales will be NULL.
If you're trying to do a month-by-month summary you need more logic than you have. See this writeup: http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/

Select available rooms between two dates

I have above mysql table with available dates and prices. Second table includes room details. How can I join two tables to get available rooms between two dates and not get duplicate content.
This is hard to come up with a complete answer for you here, as you are only showing us the table which contains the bookings - we cannot know what range of rooms are available.
SQL which returns the room_id's for rooms which are booked for at least part of the selected period could be:
SELECT `room_id` , COUNT(*)
FROM `bookings`
WHERE `dt` BETWEEN "[start date]" AND "[end date]"
GROUP BY `room_id`;
If you had a table of rooms (rather than bookings), it would be possible for you to return a list of any rooms not booked during that period with:
SELECT `id`
FROM `rooms`
WHERE `id` NOT IN (
SELECT DISTINCT( `room_id` )
FROM `bookings`
WHERE `dt` BETWEEN "[start date]" AND "[end date]"
);
AMENDMENT
Based on the feedback by OP, the assumptions are now:
The table contains details of rooms which are available for a period starting on the date in column dt and ending the following day (ie hotel rooms)
The query should return any rooms which are available for the entirity of the period entered (so only rooms which are available from DAY A to DAY B will be returned.
As such, the amended code is:
SELECT room_id
FROM available_rooms
WHERE dt BETWEEN "[start date]" AND DATE_SUB("[end date]",INTERVAL 1 DAY)
GROUP BY room_id
HAVING COUNT(*)=ABS(DATEDIFF("[start date]","[end date]"));
SELECT available.* , rooms.* FROM available, rooms
WHERE available.room_id = rooms.room_id AND
available.dt BETWEEN '2005-01-01' AND '2005-12-31'
Assuming the table you showed us is called rooms_dates with two other tables rooms and room_details:
select room.id, room_details.xxxxxxx from rooms
inner join room_details on rooms.id = room_details.room_id
where rooms.id in
(
select distinct room_id from rooms_dates
where dt >= 'xxxx-xx-xx' and dt <= 'yyyy-yy-yy'
);

Search for Room Availability in Bookings [PHP/MySQL]

I am programming a Hotel Reservation module. Now I have to connect an existing application to get the available rooms on a given range of dates. Please help me in this.
I am here posting the DB schema of the existing app (where the rooms and bookings info were stored).
rooms:
id
room_type
room_count
people
hotel_id
bookings:
id
from
to
name
email
people
dt
hotel_id
bookings_rooms
booking_id
room_id
quantity
I will give 3 inputs
Hotel ID
From Date
To Date
What I need is a list of room_id and max_qty_available
So, rooms aren't actually rooms, they are a type with a count. Does clear up a few things, although by rooms not being seperate entities it is very hard to prevent a change of rooms being eligable.
SELECT r.id, r.room_count - SUM(br.quantity ) AS max_qty_available
FROM rooms r
LEFT JOIN bookings b
ON b.hotel_id = r.hotel_id
AND b.from < <Date END>
AND b.to > <Date START>
LEFT JOIN booking_rooms br
ON br.room_id = r.id
AND br.booking_id = br.id
WHERE r.hotel_id = <Hotel ID>
I`m guessing what some of this parameters are, but here it goes:
Select id as room_id, room_Count as max_qty_available From Rooms Where
id Not in (Select room_id From Bookings_rooms Where booking_id
In (Select id From bookings Where from>=#FromDate And to <=#ToDate And hotel_id=#HotelID)
) And hotel_id = #HotelID
You can replace IN and NOT IN with Exists and Not exists if there is a performance issue. But not sure if Exists are supported in mysql.

Categories