I have two tables in mysql (shop_details,booking)
shop_details
id shop_id open_time close_time
1 1 09:00Am 09:00Pm
2 5 10:00Am 09:00Pm
booking
id shop_id start_time end_time
1 1 10:30am 11:10Am
1 5 02:00pm 02:45pm
Now I want if I want to know about shop_id (1) then I want to get booked time (after shop open time) and avaliable time (till shop close) with every half hour, for example I want following result
shop_id start_time end_time status
1 09:00am 09:30am avaliable
1 09:30am 10:00am avaliable
1 10:00am 10:30am avaliable
1 10:30am 11:10am booked
1 11:10am 11:40am avaliable
...
1 08:30am 09:00pm booked
You need a list of times -- then you can get the information using a join or correlated subquery:
select t.tme,
(case when exists (select 1
from booking b
where b.start_time <= t.tme and
b.end_time > t.tme
)
then 'booked' else 'available'
end) as status
from (select cast('00:00' as time) as tme union all
select cast('00:30' as time) as t union all
select cast('01:00' as time) as t union all
. . .
select cast('23:50' as time) as t
) t join
(select sd.*
from shop_details sd
where sd.shop_id = 1
) sd1
on t.tme >= sd1.open_time and
t.tme <= sd1.close_time;
first, use while function to create a loop that will repeat the code until reaching close time,
inside while function, u can use IF statement to make Availability status base on dates
goold luck
Related
I have 2 tables,
prices (pcode, date, priceperweek)
1 record covers 1 week
booked (pcode, date)
1 record = 1 day, because shortbreaks are available
And a form consists of 3 fields
startdate
flexibility (+/- 1/2/3 weeks)
duration (3 / 4 / 5 / 6 / 7 / 14)).
The below query should be selecting prices & dates and checking to see if the selected start date appears in the prices table and that each day from the startdate doesn't appear in the bookings table.
SELECT SUM(priceperweek) AS `ppw`, prices.date AS `startdate`
FROM `prices` LEFT JOIN `booked` ON prices.pcode=booked.pcode
WHERE prices.pcode='A2CD59GH'
AND (prices.date IN ('20131221', '20131228')
AND booked.date NOT IN ('20131221', '20131222', '20131223',
'20131224', '20131225', '20131226', '20131227', '20131228',
'20131229', '20131230', '20131231', '20140101', '20140102',
'20140103')
)
OR (prices.date IN ('20131214', '20131221')
AND booked.date NOT IN ('20131214', '20131215', '20131216',
'20131217',
'20131218', '20131219', '20131220', '20131221', '20131222',
'20131223', '20131224', '20131225', '20131226', '20131227')
)
OR (prices.date IN ('20131228', '20140104') AND booked.date NOT IN
('20131228', '20131229', '20131230', '20131231', '20140101',
'20140102', '20140103', '20140104', '20140105', '20140106',
'20140107', '20140108', '20140109', '20140110')
)
GROUP BY prices.date
ORDER BY prices.date ASC
VALUES GIVEN TO QUERY...
startdate = 20131221
duration = 14
property = A2CD59GH
plusminus = 1
My problem is that this query returns records even if some of the dates in a range appear in the "bookings" table AND the ppw value is alot more than i would have expected.
The reason for using SUM(ppw) is when a duration of 14 is specified the price will sum both weeks together.
Thanks for any help on this
The problem with your approach is that the startdate will only be filtered from the results if every single record in the booked table for the given pcode falls within the booking period. Obviously this won't be the case if the property has been booked on some other date.
I'd suggest performing an anti-join along the following lines:
SELECT t.date, SUM(prices.priceperweek) FROM prices JOIN (
SELECT prices.date
FROM prices LEFT JOIN booked
ON booked.pcode = prices.pcode
AND booked.date BETWEEN prices.date
AND prices.date + INTERVAL 14 DAY
WHERE booked.pcode IS NULL
AND prices.pcode = 'A2CD59GH'
AND prices.date BETWEEN '20131221' - INTERVAL 1 WEEK
AND '20131221' + INTERVAL 2 WEEK
) t ON prices.date BETWEEN t.date AND t.date + INTERVAL 13 DAY
GROUP BY t.date
Hi i have this mysql table
id amount substart years subend
1 200 2012-01-10 1 2013-01-09
2 250 2012-02-15 2 2014-02-14
3 100 2012-02-11 1 2013-02-10
4 260 2012-03-22 3 2015-03-21
What i want is that to give notification a month before the end date. The current query is:
select count(subend) as count,curdate()
from subdur where status='active'
and (date_sub(subend,interval 1 month))>=curdate()
and (date_sub(subend,interval 1 month))<date_add(curdate(),interval 1 month)
order by subend;
The query is not giving me proper answer.
Thanks in advance
Try this::
select
count(subend) as count,
curdate()
from subdur
where status='active' and
subend BETWEEN (date_sub(curdate(),interval 1 month)) and curdate()
order by subend
Another way would be using date_diff:
select count(subend) as count, curdate()
from subdur
where status='active'
and date_diff(subend, curdate()) = 30 // >= 30 days or more, = 30 days exact
order by subend
;
Hotel_id Room_id Room_type Start_date End_date Price
----------------------------------------------------------------
13 2 standard 2012-08-01 2012-08-15 7000
13 2 standard 2012-08-16 2012-08-31 7500
13 2 standard 2012-09-01 2012-09-30 6000
13 3 luxury 2012-08-01 2012-08-15 9000
13 3 luxury 2012-08-16 2012-08-31 10000
13 3 luxury 2012-09-01 2012-09-30 9500
Hi this is the structure and data of my table.
I need to create a mysql query for hotel booking, that would match in database user entered data:
Date when they want to checkin and checkout
Room type
For Ex:
If user selects Hotel with luxury room based on these dates (2012-08-30 to 2012-09-04)
the total cost would be (10000*2) for 30th and 31st Aug + (9500*3) for 1st,2nd and 3rd Sep(4th checkout day don't include)
that means total price will be 20000+28500=48500
So query should filter total price based on the Hotel_id,Room_id,Start_date,End_date and Price
Thanks
Use this solution:
SELECT SUM(
CASE WHEN a.Start_date = b.min_sd AND a.Start_date <> b.max_sd THEN
(DATEDIFF(a.End_date, '2012-08-30')+1) * a.Price
WHEN a.Start_date = b.max_sd AND a.Start_date <> b.min_sd THEN
DATEDIFF('2012-09-04', a.Start_date) * a.Price
WHEN (a.Start_date,a.Start_date) IN ((b.min_sd,b.max_sd)) THEN
(DATEDIFF('2012-09-04', '2012-08-30')+1) * a.Price
WHEN a.Start_date NOT IN (b.min_sd, b.max_sd) THEN
(DATEDIFF(a.End_date, a.Start_date)+1) * a.Price
END
) AS totalprice
FROM rooms a
CROSS JOIN (
SELECT MIN(Start_date) AS min_sd,
MAX(Start_date) AS max_sd
FROM rooms
WHERE Room_type = 'luxury' AND
End_date >= '2012-08-30' AND
Start_date <= '2012-09-04'
) b
WHERE a.Room_type = 'luxury' AND
a.End_date >= '2012-08-30' AND
a.Start_date <= '2012-09-04'
Replace occurances of 2012-08-30 and 2012-09-04 with your input start and end dates respectively.
This will account for start and end dates being in the same month as well as spanning across multiple months.
SQLFiddle Demo
You can use MySQL's BETWEEN ... AND ...
operator to find the date ranges in which the desired booking falls (remember to take one day off of the given checkout
date as, like you say, there is no night's stay), then group the results by room and take the
SUM() of price times number of nights (which can
be calculated using MySQL's LEAST() and
GREATEST() functions):
SELECT Room_id,
SUM(Price * (1 + DATEDIFF(
LEAST(End_date, '2012-09-04' - INTERVAL 1 DAY),
GREATEST(Start_date, '2012-08-30')
))) AS Total
FROM mytable
WHERE Room_type = 'luxury' AND (
'2012-09-04' - INTERVAL 1 DAY
BETWEEN Start_date AND End_date
OR '2012-08-30' BETWEEN Start_date AND End_date
)
GROUP BY Room_id
See it on sqlfidde.
try this:
set #Hotel_id :=13;
set #Room_id :=3;
set #Start_date :='2012-08-30' ;
set #End_date :='2012-09-04';
select sum(b.TotalPrice-b.deductions) as total_cost from
( select a.Price,a.StartDate,a.EndDate,price*(DATEDIFF(a.EndDate,a.StartDate)+1) as TotalPrice
,case when a.EndDate=#End_date then a.Price else 0 end as deductions
from
(select price,case when #Start_date>=Start_date then #Start_date else Start_date end as StartDate
,case when #End_date<=End_date then #End_date else End_date end as EndDate
from h_booking h1
where Hotel_id=#Hotel_id
and Room_id=#Room_id
and (#Start_date between Start_date and End_date or #End_date between Start_date and End_date ))a )b
in my component I have created an agenda, where user can save their appointments.
Agenda table is quite simple: there is a title, description and start/end datetime fields.
When a user adds a new event, I'd wish to hint him with the first empty spot.
How can I achieve that?
Is that possible with a single/bunch queries, or I have to create a loop until I find the first empty spot?
For example this is my table:
| ID | Start date | End date |
| 1 | 2012-06-14 09:00:00 | 2012-06-14 09:32:00 |
| 2 | 2012-06-14 15:00:00 | 2012-06-14 15:45:00 |
| 3 | 2012-06-14 18:20:00 | 2012-06-14 18:55:00 |
The first free datetime should be 2012-06-14 09:33:00, how can I fetch this date?
Interesting challenge to be done in one query :) Took me a while but I came with solution. With these assumptions:
minimum appointment duration is 30 minutes
appointment starts and ends in one day
day starts at 9:00 and ends at 17:00
minimum interval between ending and starting times is 1 minute
There are 4 cases to take into consideration:
There is some free time in the morning
There is some free time during the day
First free slot is next day to the last appointment in db
You have all free slots from now on
So yo have to select minimum form these dates.
And the query:
SELECT
MIN(next_start_date) AS next_start_date
FROM
(
(
SELECT
DATE_FORMAT(t1.start_date,'%Y-%m-%d 09:00:00') AS next_start_date
FROM
agenda t1
WHERE
t1.start_date > NOW()
AND
TIME(t1.start_date) > '09:30:00'
AND
NOT EXISTS(
SELECT 1 FROM agenda t2 WHERE DATE(t2.start_date) = DATE(t1.start_date) AND TIME(t2.start_date) <= '09:30:00'
)
LIMIT 1
)
UNION
(
SELECT
t3.end_date + INTERVAL 1 MINUTE AS next_start_date
FROM
agenda t3
WHERE
t3.start_date > NOW()
AND
TIME(t3.start_date) >= '09:00:00'
AND
TIME(t3.end_date) < '16:30:00'
AND NOT EXISTS
(SELECT 1 FROM agenda t4 WHERE TIMESTAMPDIFF(MINUTE,t3.end_date,t4.start_date) BETWEEN 0 AND 30 )
ORDER BY
t3.start_date ASC
LIMIT 1
)
UNION
(
SELECT CONCAT(CAST(DATE((SELECT MAX(t5.start_date) + INTERVAL 1 DAY FROM agenda t5 WHERE t5.start_date > NOW())) AS CHAR), ' 09:00:00') AS next_start_date
)
UNION
(
SELECT
IF(
TIME(NOW()) < '09:00:00',
DATE_FORMAT(NOW(),'%Y-%m-%d 09:00:00'),
IF(
TIME(NOW()) < '16:30',
NOW(),
DATE_FORMAT(NOW() + INTERVAL 1 DAY,'%Y-%m-%d 09:00:00' )
)
) AS next_start_date
FROM
(SELECT 1) t6
WHERE NOT EXISTS(
SELECT 1 FROM agenda t7 WHERE t7.start_date > NOW()
)
LIMIT 1
)
) t
Of course it is not perfect - when there next free slot occurs in the next day, there's a chance that it is Saturday or other day off from work. Taking it into consideration, the best way will be to check if returned is valid work day, if not then repeat the query with NOW() replaced by datestring of next valid work day.
Im developing a php booking system based on timeslot for daily basis.
Ive set up 4 database tables!
Bookslot (which store all the ids - id_bookslot, id_user, id_timeslot)
Timeslot (store all the times on 15 minutes gap ex: 09:00, 09:15, 09:30, etc)
Therapist (store all therapist details)
User (store all the members detail)
ID_BOOKSLOT ID_USER ID_THERAPIST ID_TIMESLOT
1 10 1 1 (09:00)
2 11 2 1 (09:00)
3 12 3 2 (09:15)
4 15 3 1 (09:00)
Now, my issue is, it keep showing repeation for timeslot when i want echoing the data for example:
thera a thera b thera c
-------------------------------------------------
09:00 BOOKED available available
09:00 available BOOKED available
09:00 available available BOOKED
09:15 available BOOKED available
as you can see, 09:00 showing three times, and i want something like below
thera a thera b thera c
-------------------------------------------------
09:00 BOOKED BOOKED BOOKED
09:15 available BOOKED available
There might be something wrong with joining the table or else.
The code to join the table
$mysqli->query("SELECT * FROM bookslot RIGHT JOIN timeslot ON bookslot.id_timeslot = timeslot.id_timeslot LEFT JOIN therapist ON bookslot.id_therapist = therapist.id_therapist"
if anyone have the solution for this system, please help me out and i appriciate it much!
select
id_TimeSlot
, coalesce(Thera_A, 'available') as Thera_A
, coalesce(Thera_B, 'available') as Thera_B
, coalesce(Thera_C, 'available') as Thera_C
from
(
select
t.id_TimeSlot
, max(case b.id_Therapist when 1 then 'booked' else null end) as Thera_A
, max(case b.id_Therapist when 2 then 'booked' else null end) as Thera_B
, max(case b.id_Therapist when 3 then 'booked' else null end) as Thera_C
from TimeSlot as t
left join BookSlot as b on b.id_TimeSlot = t.id_TimeSlot
left join Therapist as p on p.id_Therapist = b.id_Therapist
group by t.id_TimeSlot
) as xx ;
Test:
create table TimeSLot (id_TimeSLot integer);
create table Therapist (id_Therapist integer);
create table BookSlot (id_Therapist integer, id_TimeSlot integer);
insert into Therapist (id_Therapist)
values (1), (2), (3);
insert into TimeSlot (id_TimeSlot)
values (1), (2), (3), (4), (5);
insert into BookSlot (id_Therapist,id_TimeSlot)
values (1,1), (1,5), (2,1), (2,4), (3,1);
returns
id_TimeSlot Thera_A Thera_B Thera_C
----------------------------------------------
1 booked booked booked
2 available available available
3 available available available
4 available booked available
5 booked available available
I guess you need to GROUP BY id_timeslot, and then check which therapists are booked (or not).
To avoid complicated queries, make table "appointments" (id, u_id, t_id, start, stop, day)...
You can then print appointments on particular day or timespan using BETWEEN start / stop and WHERE day = someday...