I'm working on a hotel booking module, and after a few days I'm stack.
Tables
+++++++++++++++++++++++++++++++
rooms bookings
======== =============
room_id b_id
avabile_rooms b_room_id
check_in
check_out
b_rooms
== Where ==
avabile_rooms - the number of rooms avabile
b_rooms = the number of rooms with this b_id booked
Values needed
++++++++++++++++++++++++++++++++
room_id and number_of_rooms_avabile foreach avabile room in interval A(check_in) - B(check_out)
Current Query
++++++++++++++++++++++++++++++++
SELECT r.* FROM rooms AS r WHERE r.room_id NOT IN
(
SELECT b.b_room_id FROM bookings AS b
WHERE (b.check_out >= ? AND b.check_in <= ?)
OR (b.check_out <= ? AND b.check_in >= ?)
)
Now I get the avabile rooms without taking into account the avabile_rooms/b_rooms
The Unkown Query (I thik It needs to be something like this)
++++++++++++++++++++++++++++++++
SELECT r.* FROM rooms AS r WHERE r.room_id IS IN
(
SELECT b.b_room_id FROM bookings AS b
OR b.b_id IS NULL
OR (b.check_out >= ? AND b.check_in <= ?)
OR (b.check_out <= ? AND b.check_in >= ?)
)
I'm not figured out how to get the room_id/number_of_rooms_avabile
P.S.: I did a deep search but did not find a solution that takes into account the number of rooms.
Thanks.
Here is how I would do it:
select rt.room_type_id, available = rt.available_rooms - isnull(sum(b.b_rooms), 0)
from room_types rt
left join bookings b
on rt.room_type_id = b.b_room_type_id
and b.check_in <= #end_date
and b.check_out >= #start_date
group by rt.room_type_id, rt.available_rooms
I renamed your rooms table to room_types as discussed in the comments, and renamed columns as appropriate.
This will take the number of available rooms and subtract all bookings that have any overlap with the selected date range, which I believe is what you want.
Depending on your business rules, you may want this instead:
where b.check_in < #end_date
and b.check_out > #start_date
Related
I have this query that I would like to run with different dates for all customers.
select
hey.*
, case
when (birthdate <= (CURRENT_DATE - 366)) then 365
else extract(days
from
(hey.time1))
end as days
from
(
select
a.salesforce_id
, max(a.birthdate) as birthdate
, max(c2.completed) as max_date
, min(c2.completed) as min_date
, count(c2.id) as file_count
from
accounts a
left join customers c
on
a.customer_id = c.id
left join files c2
on
c.id = c2.createdbycustomerid
where
1 = 1
and c2.completed is not null
and c2.completed > (CURRENT_DATE - 366)
group by
a.salesforce_id
) as hey
My goal is to replace "CURRENT_DATE" with dates from another table. The table is a sequence of dates like 2020-09-01, 2020-10-01
I've been googling a lot but can't get my head around it.
The following query gives me the MRR (monthly recurring revenue) for my customer:
with dims as (
select distinct subscription_id, country_name, product_name from revenue
where site_id = '18XLsHIVSJg' and subscription_id is not null
)
select to_date('2022-07-01') as occurred_date,
count(distinct srm.subscription_id) as subscriptions,
count(distinct srm.receiver_contact) as subscribers,
sum(srm.baseline_mrr) as mrr_srm
from subscription_revenue_mart srm
join dims d on d.subscription_id = srm.subscription_id
where srm.site_id = '18XLsHIVSJg'
-- MRR as of the day before ie June 30th
and to_date(srm.creation_date) < '2022-07-01'
-- Counting the subscriptions active after July 1st
and ((srm.subscription_status = 'SUBL.A') or
-- Counting the subscriptions canceled/deactivated after July 1st
(srm.subscription_status = 'SUBL.C' and (srm.deactivation_date >= '2022-07-01') or (srm.canceled_date >= '2022-07-01')) ) group by 1;
I get a total of $5922.15 but I need to add data from another table to capture upgrades/downgrades a customer makes on a product subscription. Using the same approach as above, I can query my "change" table thusly:
select subscription_id, sum(mrr_change_amount) mrr_change_amount,max(subscription_event_date) subscription_event_date from subscription_revenue_mart_change srmc
where site_id = '18XLsHIVSJg'
and to_date(srmc.creation_date) < '2022-07-01'
and ((srmc.subscription_status = 'SUBL.A')
or (srmc.subscription_status = 'SUBL.C' and (srmc.deactivation_date >= '2022-07-01') or (srmc.canceled_date >= '2022-07-01')))
group by 1;
I get a total of $3635.47
When I combine both queries into one, I get an inflated result:
with dims as (
select distinct subscription_id, country_name, product_name from revenue
where site_id = '18XLsHIVSJg' and subscription_id is not null
),
change as (
select subscription_id, sum(mrr_change_amount) mrr_change_amount,
-- there can be multiple changes per subscription
max(subscription_event_date) subscription_event_date from subscription_revenue_mart_change srmc
where site_id = '18XLsHIVSJg'
and to_date(srmc.creation_date) < '2022-07-01'
and ((srmc.subscription_status = 'SUBL.A')
or (srmc.subscription_status = 'SUBL.C' and (srmc.deactivation_date >= '2022-07-01') or (srmc.canceled_date >= '2022-07-01')))
group by 1
)
select to_date('2022-07-01') as occurred_date,
count(distinct srm.subscription_id) as subscriptions,
count(distinct srm.receiver_contact) as subscribers,
-- See comment RE: LEFT OUTER join
sum(coalesce(c.mrr_change_amount,srm.baseline_mrr)) as mrr
from subscription_revenue_mart srm
join dims d
on d.subscription_id = srm.subscription_id
-- LEFT OUTER join required for customers that never made a change
left outer join change c
on srm.subscription_id = c.subscription_id
where srm.site_id = '18XLsHIVSJg'
and to_date(srm.creation_date) < '2022-07-01'
and ((srm.subscription_status = 'SUBL.A')
or (srm.subscription_status = 'SUBL.C' and (srm.deactivation_date >= '2022-07-01') or (srm.canceled_date >= '2022-07-01'))) group by 1;
It should be $9557.62 ie (5922.15 + $3635.47) but the query outputs $16116.91, which is wrong.
I think the explode-implode syndrome may cause this.
I had designed my "change" CTE to prevent this by aggregating all the relevant fields but it's not working.
Can someone provide pointers on the best way to work around this issue?
It would help if you gave us sample data too, but I see a problem here:
sum(coalesce(c.mrr_change_amount,srm.baseline_mrr)) as mrr
Why COALESCE? That will give you one of the 2 numbers, but I guess what you want is:
sum(ifnull(c.mrr_change_amount, 0) + srm.baseline_mrr) as mrr
That's the best I can offer with what you've given us.
I have a table on booking orders
Bookings (order_no, user_id, booking_time,complete_time)
I try to write a query to return the order_no from all rows where customers made concurrent bookings (customer made a new booking before they completed the previous booking).
Explanation:
Customer X booked #000 at 1:15, and completed it at 1:25.
Customer X booked #001 at 1:20, and completed it at 1:25.
Customer X booked #002 at 5:30, and completed it at 6:00.
Customer Y booked #020 at 1:20, and completed it at 2:10.
Customer Y booked #021 at 6:55, and completed it at 7:16.
Only Customer X had a concurrent booking. The correct query would return order_no #000 and #001.
Output should be
000
001
I have tried using subquery in the criteria, but I still don’t get the logic
I need help with this, Please someone help me
If you want both bookings on separate rows, then one method is window functions:
select b.*
from (select b.*,
lag(booking_time) over (partition by user_id order by booking_time) as prev_booking_time,
lead(booking_time) over (partition by user_id order by booking_time) as next_booking_time,
lag(coalesce(complete_time, cancel_time) over (partition by user_id order by booking_time) as prev_end_time
from bookings b
) b
where (next_booking_time >= booking_time and
next_booking_time < coalesce(complete_time, cancel_time)
) or
(booking_time > prev_booking_time and
booking_time < prev_end_time
);
If you want the overlaps on one row, then you can do:
select b1.*, b2.*
from bookings b1 join
bookings b2
on b2.user_id = b1.user_id and
b2.booking_time >= b1.booking_time and
(b2.booking_time <= b1.complete_time) or
b2.booking_time <= b1.cancel_time
);
Note that for multiple overlaps on the same booking, this produces a row for each pair.
This is just the overlapping date range problem. You may solve this via a self join:
SELECT b1.*
FROM Bookings b1
INNER JOIN Bookings b2
ON b1.user_id = b2.user_id AND
b1.order_no <> b2.order_no
WHERE
b2.booking_time < b1.complete_time AND
b2.complete_time > b1.booking_time;
As the title states, I'm trying to grab all available hotel rooms when a user specifies a check in and check out date. I've made some progress, but I'm struggling to understand the logic behind this process.
Here's what I have:
SELECT r.FLOOR, r.ROOM
FROM BOOKING b, ROOMS r
WHERE TO_DATE('2015-03-28', 'YYYY-MM-DD')
BETWEEN TO_DATE(b.CHECKIN, 'YY-MM-DD') AND TO_DATE(b.CHECKOUT, 'YY-MM-DD')
AND r.ROOMID = b.ROOMID;
This simply returns back all taken rooms on the specified date. (2015-03-28)
How can I change this code to take in two dates, checkin an checkout, while also providing available rooms instead of taken rooms.
Any help is much appreciated!
You can use Oracle's wm_overlaps function, which finds overlapping time spans:
select *
from rooms
where roomid not in
(
select b.room_id
from booking b
where wm_overlaps (
wm_period(b.checkin, b.checkout),
wm_period(
to_date('2014-01-01', 'yyyy-mm-dd'),
to_date('2014-01-05', 'yyyy-mm-dd')
)
) = 1
)
In this query, the rooms have no bookings between the both given parameters.
Try to get list of all rooms and exclude from it booked set, for example
SELECT r.FLOOR, r.ROOM
FROM ROOMS r
EXCEPT
SELECT r.FLOOR, r.ROOM
FROM BOOKING b, ROOMS r
WHERE TO_DATE('2015-03-28', 'YYYY-MM-DD')
BETWEEN TO_DATE(b.CHECKIN, 'YY-MM-DD') AND TO_DATE(b.CHECKOUT, 'YY-MM-DD')
AND r.ROOMID = b.ROOMID;
This might be closer. Substitute the parameters (marked with #) as appropriate:
SELECT r.FLOOR, r.ROOM
FROM ROOMS r
WHERE r.ROOMID NOT IN (
-- exclude rooms where checkin or checkout overlaps with the desired dates
SELECT r.ROOMID
FROM BOOKING b
WHERE (
b.CHECKIN BETWEEN TO_DATE(#CHECKIN, 'YY-MM-DD') AND TO_DATE(#CHECKOUT, 'YY-MM-DD')
OR b.CHECKOUT BETWEEN TO_DATE(#CHECKIN, 'YY-MM-DD') AND TO_DATE(#CHECKOUT, 'YY-MM-DD')
)
The table structure:
StaffingRecords
PersonnelId int
GroupId int
StaffingStartDateTime datetime
StaffingEndDateTime datetime
How can I get a list of staffing records, given a date and a group id that employees belong to, where the count of present employees fell below a threshold, say, 3, at any minute of the day?
The way my brain works, I would call a stored proc repeatedly with each minute of the day, but of course this would be horribly inefficient:
SELECT COUNT(PersonnelId)
FROM DailyRosters
WHERE GroupId=#GroupId
AND StaffingStartTime <= #TimeParam
AND StaffingEndTime > #TimeParam
AND COUNT(GroupId) < 3
GROUP BY GroupId
HAVING COUNT(PersonnelId) < 3
Edit: If it helps to refine the question, employees may come and go throughout the day. Personnel may have a staffing record from 0800 - 0815, and another from 1000 - 1045, for example.
Here is a solution where I find all of the distinct start and end times, and then query to see how many other people are clocked in at the time. Everytime the answer is less than 4, you know you are understaffed at that time, and presumably until the NEXT start time.
with meaningfulDtms(meaningfulTime, timeType, group_id)
as
(
select distinct StaffingStartTime , 'start' as timeType, group_id
from DailyRosters
union
select distinct StaffingEndTime , 'end' as timeType, group_id
from DailyRosters
)
select COUNT(*), meaningfulDtms.group_id, meaningfulDtms.meaningfulTime
from DailyRosters dr
inner join meaningfulDtms on dr.group_id = meaningfulDtms.group_id
and (
(dr.StaffingStartTime < meaningfulDtms.meaningfulTime
and dr.StaffingEndTime >= meaningfulDtms.meaningfulTime
and meaningfulDtms.timeType = 'start')
OR
(dr.StaffingStartTime <= meaningfulDtms.meaningfulTime
and dr.StaffingEndTime > meaningfulDtms.meaningfulTime
and meaningfulDtms.timeType = 'end')
)
group by meaningfulDtms.group_id, meaningfulDtms.meaningfulTime
having COUNT(*) < 4
Create a table with all minutes in the day with dt at PK
It will have 1440 rows
this will not give you count of zero - no staff
select allMiuntes.dt, worktime.grpID, count(distinct(worktime.personID))
from allMinutes
join worktime
on allMiuntes.dt > worktime.start
and allMiuntes.dt < worktime.end
group by allMiuntes.dt, worktime.grpID
having count(distinct(worktime.personID)) < 3
for times with zero I think the best way is a master of grpID
but I am not sure about this one
select allMiuntes.dt, grpMaster.grpID, count(distinct(worktime.personID))
from grpMaster
cross join allMinutes
left join worktime
on allMiuntes.dt > worktime.start
and allMiuntes.dt < worktime.end
and worktime.grpID = grpMaster.grpID
group by allMiuntes.dt, grpMaster.grpID
having count(distinct(worktime.personID)) < 3