I need a query to calculate a hotels income for every Thursday in a month but the total SUM has to be calculated for all the period between last weeks Friday and the next weeks Thursday.
Week 1 Friday -> Thursday Total income
Week 2 Friday -> Thursday Total income
and so on..
To explain it as simple as possible, I use the table RESERVATIONS to track the check-in and check-out date, number of guests and room type which is joined with the ROOM_PRICES table to calculate the prices.. How to calculate the SUM is not the problem, but to get that span between days is.
RESERVATIONS (reservation_ID, reservation_date, room_number,guest_id, number_of_guests, room_type, check_out_date)
ROOM_PRICE (room_type, price)
Expected output are two columns, Period and Total Income.
Total Income is the sum of all payments made by guests who spent their time in the hotel AND left on a week in between Friday and Thursday no matter if they spent two, three weeks in the hotel
Period |Total Income
|-------------------------------------|
|05.May.2017-11.May.2017 | 20000|
|-------------------------------------|
|12.May.2017-18.May.2017 | 30000|
|-------------------------------------|
|19.May.2017-25.May.2017 | 12000|
And now the list continues..
Any suggestions?
Thank you
Oracle Setup:
CREATE TABLE ROOM_PRICES(
hotel_id INTEGER,
day DATE CHECK ( day = TRUNC( day ) ),
price NUMBER(8,2),
CONSTRAINT room_prices__hotel_id__day__pk PRIMARY KEY ( hotel_id, day )
);
CREATE TABLE RESERVATIONS(
hotel_id INTEGER,
check_in DATE CHECK ( check_in = TRUNC( check_in ) ),
check_out DATE CHECK ( check_out = TRUNC( check_out ) ),
customer_id INTEGER
);
Query:
Subtract 4 from the date (so Friday maps to Monday) and then truncate to the iso-week (rounding to the Monday at the start of the week) then add the 4 days back on and add another 6 days to get the Thursday:
SELECT TRUNC( day - 4, 'IW' ) + 10 AS Thursday_of_week,
r.hotel_id,
SUM( price ) AS
FROM room_prices p
INNER JOIN reservations r
ON ( r.hotel_id = p.hotel_id
AND p.day >= r.check_in
AND p.day < r.check_out )
GROUP BY
r.hotel_id,
TRUNC( day - 4, 'IW' ) + 10
Related
My Financial year starts from September.
I need to select Fiscal day number, Fiscal week number, Fiscal Month number, Fiscal Quarter number, Fiscal Quarter Name
I am getting date range from below query :
select date_sub(boms, interval 0 day) as start_date
from unnest(generate_date_array('2020-01-01','2050-12-31', interval 1 day)) boms
Example :
2020-01-01
2020-01-02
2020-01-03
.....
.....
2025-12-31
The output i need to get :
For Example for the date : 11-Feb-2020
Output:
FISCAL_DAY_OF_YEAR_NUMBER- 162
FISCAL_WEEK_OF_YEAR_NUMBER- 24
FISCAL_MONTH_NUMBER - 6
FISCAL_QUARTER_NUMBER - 2
FISCAL_QUARTER_NAME - Third
So I have the following table :
id end_date name number_of_days start_date
1 "2022-01-01" holiday1 1 "2022-01-01"
2 "2022-03-20" holiday2 1 "2022-03-20"
3 "2022-04-09" holiday3 1 "2022-04-09"
4 "2022-05-01" holiday4 1 "2022-05-01"
5 "2022-05-04" holiday5 3 "2022-05-02"
6 "2022-07-12" holiday6 9 "2022-07-20"
I want to check if a week falls in a holiday range.
So far I can select the holidays that overlap with my choosen week( week_start_date, week_end_date) , but i cant get the exact days in which the overlap happens.
this is the query i'm using, i want to add a mechanism to detect the DAYS OF THE WEEK IN WHICH THE OVERLAP HAPPENS
SELECT * FROM holidays
where daterange(CAST(start_date AS date), CAST(end_date as date), '[]') && daterange('2022-07-18', '2022-07-26','[]')
THE CURRENT QUERY RETURNS THE OVERLLAPPING HOLIDA, (id = 6), however i'm trying to get the exact DAYS OF THE WEEK in which the overlap happens ( in this case, it should be monday,tuesday , wednesday)
You can use the * operator with tsranges, generate a series of dates with the lower and upper dates and finally with to_char print the days of the week, e.g.
SELECT
id, name, start_date, end_date, array_agg(dow) AS days
FROM (
SELECT *,
trim(
to_char(
generate_series(lower(overlap), upper(overlap),'1 day'),
'Day')) AS dow
FROM holidays
CROSS JOIN LATERAL (SELECT tsrange(start_date,end_date) *
tsrange('2022-07-18', '2022-07-26')) t (overlap)
WHERE tsrange(start_date,end_date) && tsrange('2022-07-18', '2022-07-26')) j
GROUP BY id,name,start_date,end_date,number_of_days;
id | name | start_date | end_date | days
----+----------+------------+------------+----------------------------
6 | holiday6 | 2022-07-12 | 2022-07-20 | {Monday,Tuesday,Wednesday}
(1 row)
Demo: db<>fiddle
In BigQuery, given a table of date intervals, how can I find the overlap of their union with a single date interval of interest?
For example, given a table of date intervals (call this table A) as:
start_date end_date
2021-02-01 2021-05-01
2021-04-01 2021-07-01
2020-12-01 2021-03-01
2021-09-01 2021-12-01
And the single date interval of interest (call this table B) as:
start_date end_date
2021-01-01 2021-11-01
I would like to calculate the overlap between the intervals in A with the interval in B as 8 months.
When A's intervals are disjoint, I can solve this with the following:
SELECT
SUM(GREATEST(0, DATE_DIFF(LEAST(B.end_date, A.end_date),
GREATEST(B.start_date,A.start_date), MONTH)))
AS months_overlap
FROM
A, B
The problem comes in when the date intervals in A overlap with each other, as in the above example, in which case the above code double counts overlapping intervals in A i.e. it will return 10 months for the above example.
Any suggestions on how to calculate the overlap of these intervals without double counting? I thought about introducing Lags into the date diff function but I'm not coming right.
Consider below approach
select count(1) as months_overlap
from (
select distinct date_trunc(day, month) month
from tableA, unnest(generate_date_array(start_date, end_date - 1)) day
)
join (
select distinct date_trunc(day, month) month
from tableB, unnest(generate_date_array(start_date, end_date - 1)) day
)
using(month)
if applied to sample data in your question - output is
One approach is to expand the various intervals into months, join and count:
with b as (
select mon
from b cross join
unnest(generate_date_array(b.start_date, b.end_date, interval 1 month)) mon
),
a as (
select mon
from a cross join
unnest(generate_date_array(a.start_date, a.end_date, interval 1 month)) mon
)
select count(distinct mon)
from a join
b
using (mon);
I have a table like this
Id Valid_From Valid_To
9744 24/06/2019 07/07/2019
9745 12/08/2019 31/12/9999
I would like to split this table into multiple rows based on the week like this by joining to the date table
Id Valid_from Valid_To Month Week
9744 24/06/2019 07/07/2019 June 4
9744 24/06/2019 07/07/2019 July 1
9744 24/06/2019 07/07/2019 July 2
9745 12/08/2019 31/12/9999 August 2
9745 12/08/2019 31/12/9999 August 3
9745 12/08/2019 31/12/9999 August 4
In this case there will be 3 rows as the valid from and valid two falls between these 3 weeks for ID - 9744
For ID - 9745 the Valid_to date is infinity so we need to just take all the week in the current month from the valid_from date
I then just need to append the output with Month and the Week number
Can someone help me to write a query to have this output?
Thanks
You mention a "date" table. If you have one then you can use a join like this:
select distinct t.id, valid_from, t.valid_to, d.month, d.week
from yourtable t join
date d
on d.date >= t.valid_from and
d.date <= t.valid_to;
If I understand your question right, you need to list all month names and week numbers of these months' existing between valid_from and valid_to dates. I did it by following query:
SELECT
Q.ID,
Q.VALID_FROM,
Q.VALID_TO,
Q.MONTH_NAME,
WEEK_NUMBER
FROM
(
SELECT
CEIL((Q.DATES_BETWEEN_INTERVAL - FIRST_DAY_OF_MONTH + 1) / 7) WEEK_NUMBER,
TO_CHAR(Q.DATES_BETWEEN_INTERVAL, 'MONTH', 'NLS_DATE_LANGUAGE = American') MONTH_NAME,
Q.*
FROM
(
SELECT
LEVEL + S.VALID_FROM DATES_BETWEEN_INTERVAL,
TRUNC(LEVEL + S.VALID_FROM, 'MONTH') FIRST_DAY_OF_MONTH,
S.* FROM
(
SELECT T.*,
(CASE WHEN EXTRACT(YEAR FROM T.VALID_TO) = 9999 THEN LAST_DAY(T.VALID_FROM) ELSE T.VALID_TO END) - T.VALID_FROM DAYS_COUNT
FROM AAA_TABLE T
) S
CONNECT BY LEVEL <= S.DAYS_COUNT
) Q
) Q
GROUP BY
Q.ID,
Q.VALID_FROM,
Q.VALID_TO,
Q.MONTH_NAME,
WEEK_NUMBER
ORDER BY
Q.ID,
Q.VALID_FROM,
Q.VALID_TO,
Q.MONTH_NAME,
WEEK_NUMBER;
But there must be 5th week if the date greater than 28th day of month. Hope this will help you.
I have two tables below. I want to count the number of days, Monday-Friday only between Hire_dt and end of calendar month the hire date falls under.
TableA
Hire_DT Id
09/26/2018 1
TableCalendar:
Date WorkDay(M-F) EOM WorkDay
09/26/2018 Wednesday 9/30/2018 1
09/27/2018 Thursday 09/30/2018 1
09/28/2018 Friday 09/30/2018 1
09/29/2018 Saturday 09/30/2018 0
09/30/2018 Sunday 09/30/2018 0
Expected Results
Hire_dt WorkDaysEndMonth WorkDaysEndMonth --counting hire_dt
09/26/2018 2 3
Here is one way to do the calculation - WITHOUT using a calendar table. The only input data is what comes from your first table (ID and HIRE_DATE), which I included in a WITH clause (not part of the query that answers your question!). Everything else is calculated. I show how to compute the number of days INCLUDING the hire date; if you don't need that, subtract 1 at the end.
TRUNC(<date>, 'iw') is the Monday of the week of <date>. The query computes how many days are in the EOM week, between Monday and EOM, but no more than 5 (in case EOM may be a Saturday or Sunday). It does a similar calculation for HIRE_DATE, but it counts the days from Monday to HIRE_DATE excluding HIRE_DATE. The last part is adding 5 days for each full week between the Monday of HIRE_DATE and the Monday of EOM.
with
sample_data(id, hire_date) as (
select 1, to_date('09/26/2018', 'mm/dd/yyyy') from dual union all
select 2, to_date('07/10/2018', 'mm/dd/yyyy') from dual
)
select id, to_char(hire_date, 'Dy mm/dd/yyyy') as hire_date,
to_char(eom, 'Dy mm/dd/yyyy') as eom,
least(5, eom - eom_mon + 1) - least(5, hire_date - hire_mon)
+ (eom_mon - hire_mon) * 5 / 7 as workdays
from (
select id, hire_date, last_day(hire_date) as eom,
trunc(hire_date, 'iw') as hire_mon,
trunc(last_day(hire_date), 'iw') as eom_mon
from sample_data
)
;
ID HIRE_DATE EOM WORKDAYS
---------- ----------------------- ----------------------- ----------
1 Wed 09/26/2018 Sun 09/30/2018 3
2 Tue 07/10/2018 Tue 07/31/2018 16
You may use the following routine ( where last_day function is a great contributor ):
SQL> alter session set NLS_TERRITORY="AMERICA";
SQL> create table TableA( ID int, Hire_DT date );
SQL> insert into TableA values(1,date'2018-09-26');
SQL> select sum(case when mod(to_char(myDate,'D'),7) <= 1 then 0 else 1 end )
as "WorkDaysEndMonth"
from
(
select Hire_DT + level - 1 myDate
from TableA
where ID = 1
connect by level <= last_day(Hire_DT) - Hire_DT + 1
);
WorkDaysEndMonth
----------------
3
P.S. integer value comes from to_char(<date>,'D') depends on the NLS_TERRITORY setting. Here I used AMERICA for which Saturday is the 7th and Sunday is the 1st day, while for UNITED KINGDOM(or my country TURKEY) setting those are 6th and 7th respectively.
Rextester Demo