Searching between two dates in mysql - php

I have two tables and this table name is "rooms" and other one is "bookings"
I joined two tables now, I want values when i will search between book_form = "2016-12-30" and book_to = "2016-12-31" it will be return true because this two dates does not exists in the "bookings" table, and when search between book_form = "2016-12-30" and book_to = "2017-01-05" or book_form = "2017-01-03" and book_to = "2017-01-15" it will be return false because this date exists in bookings table.
This is my query.
select * from rooms join room_book on rooms.room_id = room_book.room_id where status = 'available' and room_book.book_from NOT BETWEEN '2016-12-30' AND room_book.book_to NOT BETWEEN '2016-12-31'
NOTE: Sorry actually the column book_from date is 2017-01-01 in the bookings table.

select *
from rooms
join room_book on rooms.room_id = room_book.room_id
where status = 'available'
and room_book.book_from >= '2016-12-30'
AND room_book.book_to <= '2016-12-31'
Try like this.

A simple SQL query should return only those rooms without a booking that includes the supplied date:
SELECT *
FROM rooms
LEFT JOIN room_book
ON room_book.room_id = rooms.room_id
AND 'search_date' BETWEEN room_book.book_from AND room_book.book_to
WHERE rooms.room_id IS NULL
AND rooms.status = 'available'
Substitute the date you are searching for search_date above.
By using a LEFT JOIN, you will get all of the records in rooms. The IS NULL test in the where clause eliminates those rows that don't have a matching row in room_book.

Related

Optimizing the SQL Query to get data from large amount MySQL database

I am having a problem getting data from a large amount MySQL database.
With the below code it is ok to get the list of 10K patients and 5K appointments which is our test server.
However, on our live server, the number of patients is over 100K and the number of appointments is over 300K and when I run the code after a while it gives 500 error.
I need the list of the patients whose patient_treatment_status is 1 or 3 and has no appointment after one month from their last appointment. (The below code is working for small amount of patients and appointments)
How can I optimise the first database query so there will be no need the second database query in the foreach loop?
<?php
ini_set('memory_limit', '-1');
ini_set('max_execution_time', 0);
require_once('Db.class.php');
$patients = $db->query("
SELECT
p.id, p.first_name, p.last_name, p.phone, p.mobile,
LatestApp.lastAppDate
FROM
patients p
LEFT JOIN (SELECT patient_id, MAX(start_date) AS lastAppDate FROM appointments WHERE appointment_status = 4) LatestApp ON p.id = LatestApp.patient_id
WHERE
p.patient_treatment_status = 1 OR p.patient_treatment_status = 3
ORDER BY
p.id
");
foreach ($patients as $row) {
$one_month_after_the_last_appointment = date('Y-m-d', strtotime($row['lastAppDate'] . " +1 month"));
$appointment_check = $db->single("SELECT COUNT(id) FROM appointments WHERE patient_id = :pid AND appointment_status = :a0 AND (start_date >= :a1 AND start_date <= :a2)", array("pid"=>"{$row['id']}","a0"=>"1","a1"=>"{$row['lastAppDate']}","a2"=>"$one_month_after_the_last_appointment"));
if($appointment_check == 0){
echo $patient_id = $row['id'].' - '.$row['lastAppDate'].' - '.$one_month_after_the_last_appointment. '<br>';
}
}
?>
First off, this subquery likely does not do what you think it does.
SELECT patient_id, MAX(start_date) AS lastAppDate
FROM appointments WHERE appointment_status = 4
Without a GROUP BY clause, that subquery will simply take the maximum start_date of all appointments with appointment_status=4, and then arbitrarily pick one patient_id. To get the results you want you'll need to GROUP BY patient_id.
For your overall question, try the following query:
SELECT
p.id, p.first_name, p.last_name, p.phone, p.mobile,
LatestApp.lastAppDate
FROM
patients p
INNER JOIN (
SELECT patient_id,
MAX(start_date) AS lastAppDate
FROM appointments
WHERE appointment_status = 4
GROUP BY patient_id
) LatestApp ON p.id = LatestApp.patient_id
WHERE
(p.patient_treatment_status = 1
OR p.patient_treatment_status = 3)
AND NOT EXISTS (
SELECT 1
FROM appointments a
WHERE a.patient_id = p.patient_id
AND a.appointment_status = 1
AND a.start_date >= LatestApp.lastAppDate
AND a.start_date < DATE_ADD(LatestApp.lastAppDate,INTERVAL 1 MONTH)
)
ORDER BY
p.id
Add the following index, if it doesn't already exist:
ALTER TABLE appointments
ADD INDEX (`patient_id`, `appointment_status`, `start_date`)
Report how this performs and if the data appears correct. Provide SHOW CREATE TABLE patient and SHOW CREATE TABLE appointments for further assistance related to performance.
Also, try the query above without the AND NOT EXISTS clause, together with the second query you use. It is possible that running 2 queries may be faster than trying to run them together, in this situation.
Note that I used an INNER JOIN to find the latest appointment. This will result in all patients that have never had an appointment to not be included in the query. If you need those added, just UNION the results those found by selecting from patients that have never had an appointment.

mysql select equal data with same date wise

I'm trying to run a mysql join query fetching data from 3 tables.
Table 1: user
Table 2: Outtiming
Table 3: Intiming
I want to show same date data from outtiming and intiming tables with same dates.
e.g: 2017-09-13 = 2017-09-13
But I'm not getting data with same dates.
$query = "SELECT ur.username, ur.user_department, it.*, ot.*
FROM users ur
INNER JOIN intiming it ON ur.staff_id=it.staff_id
INNER JOIN outtiming ot ON ur.staff_id=ot.staff_id
WHERE it.staff_id=".$employee."
AND it.date >= '$startDate' AND ot.date <= '$endDate'";
Result
mysql query result
Add an addition condition in your WHERE statement to check matching dates: it.date = ot.date . Full query:
$query = "SELECT ur.username, ur.user_department, it.*, ot.*
FROM users ur
INNER JOIN intiming it ON ur.staff_id=it.staff_id
INNER JOIN outtiming ot ON ur.staff_id=ot.staff_id
WHERE it.staff_id=".$employee."
AND it.date >= '$startDate' AND ot.date <= '$endDate'
AND it.date = ot.date ";

I want to search between two dates in sql

I have two tables and this table name is "rooms" and other one is "bookings"
I joined two tables now, I want values when i will search between
book_form = "2016-12-30" and book_to = "2016-12-31"
it will be return true because this two dates does not exists in the "bookings" table, and when search between
book_form = "2016-12-30" and book_to = "2017-01-05"
or book_form = "2017-01-03" and book_to = "2017-01-15"
it will be return false because this date exists in bookings table.
This is my query.
select * from rooms join room_book on rooms.room_id = room_book.room_id
where status = 'available' and room_book.book_from NOT BETWEEN '2016-12-30'
AND room_book.book_to NOT BETWEEN '2016-12-31'
The answer from Kishan Patel returns all records between two dates. So if you want a true or false if a room is available between two dates, you could do something like this:
SELECT IF(COUNT(*) > 0, TRUE, FALSE) AS NewResult
from rooms
join room_book on rooms.room_id = room_book.room_id
where status = 'available'
and room_book.book_from >= '2016-12-30' AND room_book.book_to <= '2016-12-31'
Try this one return all record between two dates
select * from rooms
join room_book on rooms.room_id = room_book.room_id
where status = 'available'
and room_book.book_from >= '2016-12-30' AND room_book.book_to <= '2016-12-31'
make sure its useful to you

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

PHP select timediff with select query and group by user

i have this SQL Query:
$sql="SELECT *, COUNT(assigned_to) AS my_groupcount from tickets where deleted = '' and DAY(datetime) = '".$_GET["d"]."' and MONTH(datetime) = '".$_GET["m"]."' and YEAR(datetime) = '".$_GET["Y"]."' group by assigned_to order by datetime ASC ";
$rs=mysql_query($sql,$conn);
while($result=mysql_fetch_array($rs))
{
//work out the total time taken
$sql3="SELECT *, TIMEDIFF( timeend, timestart ) AS support_time_used FROM ticket_updates WHERE ticket_seq = '".$result["ticketnumber"]."' ";
$rs3=mysql_query($sql3,$conn) or die (mysql_error());
$totaltime = 0;
while($result3=mysql_fetch_array($rs3))
{
$totaltime = $totaltime+substr($result3["support_time_used"],0,2)*60+substr($result3["support_time_used"],3,2);
}
$hours=intval($totaltime / 60);
$minutes=$totaltime -($hours * 60);
$total_ticket_time = $hours.'h '.$minutes.'m';
echo $result["assigned_to"].' ('.$result["my_groupcount"].') - Time: '.$total_ticket_time.'<br>';
}
so its selecting all the rows from the tickets table and grouping by the asigned_to column.
it then works out the time used for each user from the ticket_updates table.
the ticket_updates.ticket_seq column links to the tickets.ticketnumber column and there may be just one or multiple rows in the ticket_updates table for 1 row in the tickets table so it adds all the differences up in the ticket_updates table.
im trying to list the assigned_to (one each) from the tickets table and put next to each one how much time they have used but its only selecting it form one ticket.
how can i make it select from all the tickets?
you don't need to do it in two steps, do you.
select assigned_to, sum(TIMEDIFF( timeend, timestart )) as total_time, count(distinct ticket_id) as ticket_count from ticket_update where ticket_id in (select ticket_id from ticket where condition)
group by assigned_to
edit: you actually need:
select ticket.assigned_to,
sum(TIMEDIFF( ticket_update_timeend, ticket_update.timestart )) as total_time, count(distinct ticket.ticket_id) as ticket_count
from
ticket_update
inner join ticket
on ticket.ticket_id=ticket_update.ticket_id
where ticket.deleted = '' and DAY(ticket.datetime) = '".$_GET["d"]."' and MONTH(ticket.datetime) = '".$_GET["m"]."' and YEAR(ticket.datetime) = '".$_GET["Y"]."'
group by ticket.assigned_to

Categories