This is my query to get all possible dates between two dates based on days.
select A.presentationID,
A.PRESENTATIONDAY,
TO_CHAR(A.PRESENTATIONDATESTART+delta,'DD-MM-YYYY','NLS_CALENDAR=GREGORIAN') LIST_DATE
from
PRESENTATION A,
(
select level-1 as delta
from dual
connect by level-1 <= (
select max(PRESENTATIONDATEEND- PRESENTATIONDATESTART) from PRESENTATION
)
)
where A.PRESENTATIONDATESTART+delta <= A.PRESENTATIONDATEEND
and
a.presentationday = trim(to_char(A.PRESENTATIONDATESTART+delta, 'Day'))
order by 1,2,3;
The values are retrieved from presentation table which consist of presentationday, presentationdatestart and presentationdateend.
Result from this query is :
622 Monday 02-05-2016 12:00:00
622 Monday 09-05-2016 12:00:00
622 Monday 16-05-2016 12:00:00
622 Monday 23-05-2016 12:00:00
622 Monday 30-05-2016 12:00:00
623 Tuesday 03-05-2016 12:00:00
623 Tuesday 10-05-2016 12:00:00
623 Tuesday 17-05-2016 12:00:00
623 Tuesday 24-05-2016 12:00:00
623 Tuesday 31-05-2016 12:00:00
624 Wednesday 04-05-2016 12:00:00
624 Wednesday 11-05-2016 12:00:00
624 Wednesday 18-05-2016 12:00:00
624 Wednesday 25-05-2016 12:00:00
624 Wednesday 01-06-2016 12:00:00
625 Thursday 05-05-2016 12:00:00
625 Thursday 12-05-2016 12:00:00
625 Thursday 19-05-2016 12:00:00
625 Thursday 26-05-2016 12:00:00
625 Thursday 02-06-2016 12:00:00
How can I arrange these value into something like this:
622 Monday 02-05-2016
623 Tuesday 03-05-2016
624 Wednesday 04-05-2016
625 Thursday 05-05-2016
622 Monday 09-05-2016
623 Tuesday 10-05-2016
624 Wednesday 11-05-2016
625 Thursday 12-05-2016
622 Monday 16-05-2016
....
625 Thursday 02-06-2016
I think you're just after this:
select a.presentationid,
a.presentationday,
to_char (a.presentationdatestart + delta, 'DD-MM-YYYY', 'NLS_CALENDAR=GREGORIAN') list_date
from presentation a,
(select level - 1 as delta
from dual
connect by level - 1 <= (select max (presentationdateend - presentationdatestart)
from presentation))
where a.presentationdatestart + delta <= a.presentationdateend
and a.presentationday = to_char(a.presentationdatestart + delta, 'fmDay')
order by a.presentationdatestart + delta,
a.presentationid;
N.B. note how I've removed your trim() and replaced it with fm in the format mask.
P.S. You could rewrite your query to remove the join condition (and extra call to the presentation table) by doing it like so:
with presentation as (select 622 presentationid, 'Monday' presentationday, to_date('01/05/2016', 'dd/mm/yyyy') presentationdatestart, to_date('31/05/2016', 'dd/mm/yyyy') presentationdateend from dual union all
select 623 presentationid, 'Tuesday' presentationday, to_date('01/05/2016', 'dd/mm/yyyy') presentationdatestart, to_date('31/05/2016', 'dd/mm/yyyy') presentationdateend from dual union all
select 624 presentationid, 'Wednesday' presentationday, to_date('01/05/2016', 'dd/mm/yyyy') presentationdatestart, to_date('07/06/2016', 'dd/mm/yyyy') presentationdateend from dual union all
select 625 presentationid, 'Thursday' presentationday, to_date('01/05/2016', 'dd/mm/yyyy') presentationdatestart, to_date('07/06/2016', 'dd/mm/yyyy') presentationdateend from dual)
-- end of mimicking your presentation table with data in it. You wouldn't need this subquery as you have the table; see SQL below.
select presentationid,
presentationday,
to_char(next_day(presentationdatestart -1, presentationday) + 7*(level - 1), 'DD-MM-YYYY') list_date
from presentation
connect by prior presentationid = presentationid
and prior sys_guid() is not null
and next_day(presentationdatestart -1, presentationday) + 7*(level - 1) <= presentationdateend
order by next_day(presentationdatestart -1, presentationday) + 7*(level - 1),
presentationid;
PRESENTATIONID PRESENTATIONDAY LIST_DATE
-------------- --------------- ----------
622 Monday 02-05-2016
623 Tuesday 03-05-2016
624 Wednesday 04-05-2016
625 Thursday 05-05-2016
622 Monday 09-05-2016
623 Tuesday 10-05-2016
624 Wednesday 11-05-2016
625 Thursday 12-05-2016
622 Monday 16-05-2016
623 Tuesday 17-05-2016
624 Wednesday 18-05-2016
625 Thursday 19-05-2016
622 Monday 23-05-2016
623 Tuesday 24-05-2016
624 Wednesday 25-05-2016
625 Thursday 26-05-2016
622 Monday 30-05-2016
623 Tuesday 31-05-2016
624 Wednesday 01-06-2016
625 Thursday 02-06-2016
Use this:
TO_CHAR(A.PRESENTATIONDATESTART+delta,'DD-MM-YYYY','NLS_CALENDAR=GREGORIAN' ) LIST_DATE
instead of
A.PRESENTATIONDATESTART+delta LIST_DATE
this formats your date
updated answer for your new challenge:
select * from (
select A.presentationID,
A.PRESENTATIONDAY,
TO_CHAR(A.PRESENTATIONDATESTART+delta,'DD-MM-YYYY','NLS_CALENDAR=GREGORIAN') LIST_DATE,
row_number() over (partition by presentationID,PRESENTATIONDATESTART+delta
order by presentationID,PRESENTATIONDATESTART+delta) r
from
PRESENTATION A,
(
select level-1 as delta
from dual
connect by level-1 <= (
select max(PRESENTATIONDATEEND- PRESENTATIONDATESTART) from PRESENTATION
)
)
where A.PRESENTATIONDATESTART+delta <= A.PRESENTATIONDATEEND
and
a.presentationday = trim(to_char(A.PRESENTATIONDATESTART+delta, 'Day'))
)
order by r
Related
I have a table with normal DATE field in it:
5/9/2022 2:32:02 AM
5/9/2022 12:33:02 PM
5/9/2022 10:34:02 AM
5/9/2022 10:20:02 AM
5/9/2022 6:54:02 AM
5/9/2022 4:28:02 AM
5/9/2022 5:08:02 AM
I need a query that I can run that will give me the record that is the closest to the top of the hour I am asking for. So like, which is closest to 5:00am, or 7:00am, or 12:00pm.
Thanks!
A query like the one below can be used to show each record that is closest to the "top of the hour". In your sample data, none of the records overlap to the nearest "top of the hour", so all the records will be shown. If you add additional dates that are closer together to the top of the hour, then only the the dates closest to the "top of the hour" will be shown.
Query
WITH
sample_dates (dt)
AS
(SELECT TO_DATE ('5/9/2022 2:32:02 AM', 'MM/DD/YYYY HH:MI:SS PM') FROM DUAL
UNION ALL
SELECT TO_DATE ('5/9/2022 12:33:02 PM', 'MM/DD/YYYY HH:MI:SS PM') FROM DUAL
UNION ALL
SELECT TO_DATE ('5/9/2022 10:34:02 AM', 'MM/DD/YYYY HH:MI:SS PM') FROM DUAL
UNION ALL
SELECT TO_DATE ('5/9/2022 10:20:02 AM', 'MM/DD/YYYY HH:MI:SS PM') FROM DUAL
UNION ALL
SELECT TO_DATE ('5/9/2022 6:54:02 AM', 'MM/DD/YYYY HH:MI:SS PM') FROM DUAL
UNION ALL
SELECT TO_DATE ('5/9/2022 4:28:02 AM', 'MM/DD/YYYY HH:MI:SS PM') FROM DUAL
UNION ALL
SELECT TO_DATE ('5/9/2022 5:08:02 AM', 'MM/DD/YYYY HH:MI:SS PM') FROM DUAL)
SELECT TO_CHAR (dt, 'MM/DD/YYYY HH:MI:SS PM') AS dt,
TO_CHAR (nearest_hour, 'MM/DD/YYYY HH:MI:SS PM') AS nearest_hour
FROM (SELECT dt,
nearest_hour,
ROW_NUMBER () OVER (PARTITION BY nearest_hour ORDER BY time_from_hour) AS time_rank
FROM (SELECT dt,
CASE
WHEN dt - TRUNC (dt, 'HH') <
ABS ((TRUNC (dt, 'HH') + INTERVAL '1' HOUR) - dt)
THEN
TRUNC (dt, 'HH')
ELSE
TRUNC (dt, 'HH') + INTERVAL '1' HOUR
END AS nearest_hour,
LEAST (dt - TRUNC (dt, 'HH'),
ABS ((TRUNC (dt, 'HH') + INTERVAL '1' HOUR) - dt)) AS time_from_hour
FROM sample_dates))
WHERE time_rank = 1
ORDER BY 1;
Result
DT NEAREST_HOUR
_________________________ _________________________
05/09/2022 02:32:02 AM 05/09/2022 03:00:00 AM
05/09/2022 04:28:02 AM 05/09/2022 04:00:00 AM
05/09/2022 05:08:02 AM 05/09/2022 05:00:00 AM
05/09/2022 06:54:02 AM 05/09/2022 07:00:00 AM
05/09/2022 10:20:02 AM 05/09/2022 10:00:00 AM
05/09/2022 10:34:02 AM 05/09/2022 11:00:00 AM
05/09/2022 12:33:02 PM 05/09/2022 01:00:00 PM
To get the closest row to a particular hour on any day then, from Oracle 12, you can use:
SELECT *
FROM table_name
ORDER BY ABS(
TRUNC(yourDate) + INTERVAL '07:00:00' HOUR TO SECOND
- yourDate
)
FETCH FIRST ROW ONLY;
Which, for the sample data:
CREATE TABLE table_name (id, yourDate) AS
SELECT 1, DATE '2022-05-09' + INTERVAL '02:32:02' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, DATE '2022-05-09' + INTERVAL '12:33:02' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, DATE '2022-05-09' + INTERVAL '10:34:02' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 4, DATE '2022-05-09' + INTERVAL '10:20:02' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 5, DATE '2022-05-09' + INTERVAL '06:54:02' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 6, DATE '2022-05-09' + INTERVAL '04:28:02' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 7, DATE '2022-05-09' + INTERVAL '05:08:02' HOUR TO SECOND FROM DUAL;
Outputs:
ID
YOURDATE
5
2022-05-09 06:54:02
If you want the closest hour to each of your rows then:
SELECT t.*,
TRUNC(yourDate, 'HH') + ROUND((yourDate - TRUNC(yourDate, 'HH'))*24)/24
AS nearest_hour
FROM table_name t
Outputs:
ID
YOURDATE
NEAREST_HOUR
1
2022-05-09 02:32:02
2022-05-09 03:00:00
2
2022-05-09 12:33:02
2022-05-09 13:00:00
3
2022-05-09 10:34:02
2022-05-09 11:00:00
4
2022-05-09 10:20:02
2022-05-09 10:00:00
5
2022-05-09 06:54:02
2022-05-09 07:00:00
6
2022-05-09 04:28:02
2022-05-09 04:00:00
7
2022-05-09 05:08:02
2022-05-09 05:00:00
If, for each closest hour, you want the single closest row then:
SELECT *
FROM (
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY nearest_hour ORDER BY ABS(nearest_hour - yourDate))
AS rn
FROM (
SELECT t.*,
TRUNC(yourDate, 'HH') + ROUND((yourDate - TRUNC(yourDate, 'HH'))*24)/24
AS nearest_hour
FROM table_name t
) t
)
WHERE rn = 1
Outputs:
ID
YOURDATE
NEAREST_HOUR
RN
1
2022-05-09 02:32:02
2022-05-09 03:00:00
1
6
2022-05-09 04:28:02
2022-05-09 04:00:00
1
7
2022-05-09 05:08:02
2022-05-09 05:00:00
1
5
2022-05-09 06:54:02
2022-05-09 07:00:00
1
4
2022-05-09 10:20:02
2022-05-09 10:00:00
1
3
2022-05-09 10:34:02
2022-05-09 11:00:00
1
2
2022-05-09 12:33:02
2022-05-09 13:00:00
1
db<>fiddle here
I've a table with employees and their birth date, in a column in a format string.
I cannot modify the table, so I created a view to get their birth date in a real date format (TO_DATE).
Now, I would like to get the list of the employees having theirs birthday in the last 15 days and the employees who'll have theirs birthday in the next 15 days.
So, just based with the Day and the month.
I successfully get for exemple all employees bornt in April with "Extract", but, I'm sure you've already understand, when I'll run the query the 25 April, I'd like the futures birthday in May.
How could I get that (oracle 12c)
Thank you 🙂
Using the hiredate column in table scott.emp for testing:
select empno, ename, hiredate
from scott.emp
where add_months(trunc(hiredate),
12 * round(months_between(sysdate, hiredate) / 12))
between trunc(sysdate) - 15 and trunc(sysdate) + 15
;
EMPNO ENAME HIREDATE
---------- ---------- ----------
7566 JONES 04/02/1981
7698 BLAKE 05/01/1981
7788 SCOTT 04/19/1987
This will produce the wrong result in the following situation: if someone's birthday is Feb. 28 in a non-leap year, their birthday in a leap year (calculated with the ADD_MONTHS function in the query) will be considered to be Feb. 29. So, they will be excluded if running the query on, say, Feb. 13 2024 (even though they should be included), and they will be included if running the query on March 14 (even though they should be excluded). If you can live with this - those people will be recognized in the wrong window, once every four years - then this may be all you need. Otherwise that situation will require further tweaking.
For people born on Feb. 29 (in a leap year, obviously), their birthday in a non-leap-year is considered to be Feb. 28. With this convention, the query will always work correctly for them. Whether this convention is appropriate in your locale, only your business users can tell you. (Local laws and regulations may matter, too - depending on what you are using this for.)
You can use ddd format model:
DDD - Day of year (1-366).
For example:
SQL> with v(dt) as (
2 select date'2020-01-01'+level-1 from dual connect by date'2020-01-01'+level-1<date'2021-01-01'
3 )
4 select *
5 from v
6 where
7 not abs(
8 to_number(to_char(date'&dt','ddd'))
9 -to_number(to_char(dt ,'ddd'))
10 ) between 15 and 350;
Enter value for dt: 2022-01-03
DT
-------------------
2020-01-01 00:00:00
2020-01-02 00:00:00
2020-01-03 00:00:00
2020-01-04 00:00:00
2020-01-05 00:00:00
2020-01-06 00:00:00
2020-01-07 00:00:00
2020-01-08 00:00:00
2020-01-09 00:00:00
2020-01-10 00:00:00
2020-01-11 00:00:00
2020-01-12 00:00:00
2020-01-13 00:00:00
2020-01-14 00:00:00
2020-01-15 00:00:00
2020-01-16 00:00:00
2020-01-17 00:00:00
2020-12-19 00:00:00
2020-12-20 00:00:00
2020-12-21 00:00:00
2020-12-22 00:00:00
2020-12-23 00:00:00
2020-12-24 00:00:00
2020-12-25 00:00:00
2020-12-26 00:00:00
2020-12-27 00:00:00
2020-12-28 00:00:00
2020-12-29 00:00:00
2020-12-30 00:00:00
2020-12-31 00:00:00
30 rows selected.
NB: This example doesn't analyze leap years.
Similar to mathguy's answer, but translating the current date back to the birth year (rather than translating the birth year forwards):
SELECT *
FROM employees
WHERE birth_date BETWEEN ADD_MONTHS(
TRUNC(SYSDATE),
ROUND(MONTHS_BETWEEN(birth_date, SYSDATE)/12)*12
) - INTERVAL '15' DAY
AND ADD_MONTHS(
TRUNC(SYSDATE),
ROUND(MONTHS_BETWEEN(birth_date, SYSDATE)/12)*12
) + INTERVAL '15' DAY;
Then, for the sample data:
CREATE TABLE employees (name, birth_date) AS
SELECT 'Alice', DATE '2020-02-28' FROM DUAL UNION ALL
SELECT 'Betty', DATE '2020-02-29' FROM DUAL UNION ALL
SELECT 'Carol', DATE '2021-02-28' FROM DUAL UNION ALL
SELECT 'Debra', DATE '2022-04-28' FROM DUAL UNION ALL
SELECT 'Emily', DATE '2021-03-30' FROM DUAL UNION ALL
SELECT 'Fiona', DATE '2021-03-31' FROM DUAL;
If today's date is 2022-04-16 then the output is:
NAME
BIRTH_DATE
Debra
28-APR-22
If today's date is 2022-03-15 then the output is:
NAME
BIRTH_DATE
Betty
29-FEB-20
Carol
28-FEB-21
Emily
30-MAR-21
And would get values from 28th February - 30th March in a non-leap-year and from 29th February - 30th March in a leap year.
db<>fiddle here
This is presentation table:
ID PRESENTATIONDAY PRESENTATIONSTART PRESENTATIONEND PRESENTATIONSTARTDATE PRESENTATIONENDDATE
622 Monday 12:00:00 02:00:00 01-05-2016 04-06-2016
623 Tuesday 12:00:00 02:00:00 01-05-2016 04-06-2016
624 Wednesday 08:00:00 10:00:00 01-05-2016 04-06-2016
625 Thursday 10:00:00 12:00:00 01-05-2016 04-06-2016
I would like to insert availabledate in schedule table. This is my current query :
insert into SCHEDULE (studentID,studentName,projectTitle,supervisorID,
supervisorName,examinerID,examinerName,exavailableID,
availableday,availablestart,availableend,
availabledate) //PROBLEM STARTS HERE
values (?,?,?,?,?,?,?,?,?,?,?,?));
The value availabledate are retrieved based on the exavailableID
. For example, if exavailableID = 2, the availableday = Monday, availablestart= 12pm, availableend = 2pm.
The dates will only be chosen only between PRESENTATIONSTARTDATE to PRESENTATIONENDDATE from presentation table.
In presentation table, it will match PRESENTATIONDAY, PRESENTATIONDATESTART and PRESENTATIONDATEEND with availableday, availablestart and availableend to get a list of all possible dates.
This is the query to get list of all possible dates based on particular days:
select
A.PRESENTATIONID,
A.PRESENTATIONDAY,
A.PRESENTATIONDATESTART+delta LIST_DATE
from
PRESENTATION A,
(
select level-1 as delta
from dual
connect by level-1 <= (
select max(PRESENTATIONDATEEND- PRESENTATIONDATESTART) from PRESENTATION
)
)
where A.PRESENTATIONDATESTART+delta <= A.PRESENTATIONDATEEND
and
a.presentationday = trim(to_char(A.PRESENTATIONDATESTART+delta, 'Day'))
order by 1,2,3;
This query result is:
622 Monday 02-05-2016 12:00:00
...
622 Monday 30-05-2016 12:00:00
623 Tuesday 03-05-2016 12:00:00
...
623 Tuesday 31-05-2016 12:00:00
624 Wednesday 04-05-2016 12:00:00
...
624 Wednesday 01-06-2016 12:00:00
625 Thursday 05-05-2016 12:00:00
...
625 Thursday 02-06-2016 12:00:00
It will automatically assign dates from the SELECT query to be inserted in schedule table. However, each date can be used only 4 times. Once it reached 4 times, it will proceed to next date. For example, if Monday, '02-05-2016' to '09-05-2016'
How can I corporate these two queries (INSERT and SELECT) to have a result like this:
StudentName projectTitle SupervisorID ExaminerID availableday availablestart availableend availabledate
abc Hello 1024 1001 MONDAY 12.00pm 2.00pm 02-05-2016
def Hi 1024 1001 MONDAY 12.00pm 2.00pm 02-05-2016
ghi Hey 1002 1004 MONDAY 12.00pm 2.00pm 02-05-2016
xxx hhh 1020 1011 MONDAY 12.00pm 2.00pm 02-05-2016
jkl hhh 1027 1010 MONDAY 12.00pm 2.00pm 09-05-2016
try ttt 1001 1011 MONDAY 12.00pm 2.00pm 09-05-2016
654 bbb 1007 1012 MONDAY 12.00pm 2.00pm 09-05-2016
gyg 888 1027 1051 MONDAY 12.00pm 2.00pm 09-05-2016
yyi 333 1004 1022 TUESDAY 12.00pm 2.00pm 03-05-2016
fff 111 1027 1041 TUESDAY ..
ggg 222 1032 1007 TUESDAY .. .. .. ..
hhh 444 1007 1001 TUESDAY 12.00pm 2.00pm 03-05-2016
and so on :)
In short, I would like to use the list of dates from presentation table based on the day, start time and end time to insertion query where each date will only used 4 times. Thank you!
I am not sure this kind of syntax works with oracle (and have no good way to check), but changing the select part of insert like this may or may not work.
select
A.PRESENTATIONID,
A.PRESENTATIONDAY,
A.PRESENTATIONDATESTART+delta LIST_DATE
from
PRESENTATION A,
(
select level-1 as delta
from dual
connect by level-1 <= (
select max(PRESENTATIONDATEEND - PRESENTATIONDATESTART) from PRESENTATION
)
),
--MIGHT NEED ADDITIONAL LOGIC FOR THE EXAVAILABLEID COMPARISON
(SELECT count(S.*) as counter FROM SCHEDULE S WHERE S.EXAVAILABLEID=A.ID) C
where A.PRESENTATIONDATESTART+delta <= A.PRESENTATIONDATEEND
and
a.presentationday = trim(to_char(A.PRESENTATIONDATESTART+delta, 'Day'))
and
C.counter<4
order by 1,2,3;
EDIT: Changed the operator. Had >= before. Placed teh WHERE check at the right place. Deleted aliases.
EDIT2: changed the syntax to where the counter select statement is a part of the from clause.
I have a scenario where in I have to aggregate data for a dynamic 24 hour period.
For eg: If a user selects the FROM date as Jan 05 2016 8:00 AM and TO date as Jan 10 2016 2:00 AM data in the output should be aggregated from Jan 05 2016 8:00 AM to Jan 06 2016 7:59 AM as 1 day (Jan 05 2016).
Jan 5 2016 - Jan 5 2016 8:00 AM to Jan 6 2016 7:59 AM
Jan 6 2016 - Jan 6 2016 8:00 AM to Jan 7 2016 7:59 AM
Jan 7 2016 - Jan 7 2016 8:00 AM to Jan 8 2016 7:59 AM
Jan 8 2016 - Jan 8 2016 8:00 AM to Jan 9 2016 7:59 AM
Jan 9 2016 - Jan 9 2016 8:00 AM to Jan 10 2016 2:00 AM
To achieve this, I subtracted 8 hours from the date column in the fact table and joined it to the Date Dimension. The query looks like this:
SELECT D.DAY_FMT,SUM(F.MEASURE) from FACT F
INNER JOIN DATES D ON
to_number(to_char((F.DATESTIME - 0.3333333),'YYYYMMDD')) = D.DATEID
WHERE F.DATESTIME between to_timestamp ('05-Jan-16 08.00.00.000000000 AM')
and to_timestamp ('10-Jan-16 02.00.00.000000000 AM')
GROUP BY D.DAY_FMT
Note 1: If the From Time is 06:00 AM then we would be subtracting 0.25 (days) instead of 0.3333333 (days)
Note 2: The Fact table has billions of rows.
Is there any way to improve the performance of the above query?
In Oracle the date and the time are stored together. You don't need to join on equality, and you don't need to wrap the date within any functions. (And why timestamps?) Having all the computations (if any are even needed) on the "right hand side" of conditions means the computations are done just once, the same for every row, instead of separately for each row.
select f.day_fmt, sum(f.measure) as some_col_name
from fact f inner join dates d
on f.datestime >= to_date('05-Jan-16 08:00:00 AM', 'dd-Mon-yy hh:mi:ss AM')
and f.datestime < to_date('10-Jan-16 02:00:00 AM', 'dd-Mon-yy hh:mi:ss AM')
group by day_fmt;
Edit: Based on further clarification from OP - suppose the data is in table "fact" - with columns day_fmt, measure, and datestime. The assignment is to aggregate (sum) measure, grouped by day_fmt and also grouped by 24-hour intervals, starting from a date-time chosen by the user and ending with a date-time chosen by the user. Solution below.
with user_input (sd, ed) as (
select to_date('05-Jan-16 08:00:00 AM', 'dd-Mon-yy hh:mi:ss AM'),
to_date('10-Jan-16 02:00:00 AM', 'dd-Mon-yy hh:mi:ss AM') from dual
),
prep (dt) as (
select (select sd from user_input) + level - 1 from dual
connect by level < (select ed - sd from user_input) + 1
union
select ed from user_input
),
dates (from_date, to_date) as (
select dt, lead(dt) over (order by dt) from prep
)
select f.day_fmt, d.from_datetime, d.to_datetime, sum(f.measure) as some_column_name
from fact f inner join dates d
on f.datestime >= d.from_datetime and f.datestime < d.to_datetime
where to_datetime is not null
group by f.day_fmt, d.from_datetime, f.to_datetime
order by f.day_fmt, d.from_datetime;
By not using function calls wrapped around f.datestime, you can take advantage of an index defined on this column of the "fact" table (an index you already have or one you can create now, to help speed up your queries).
I have presentation table consists of:
ID DAY START END STARTDATE ENDDATE
622 Monday 12:00:00 02:00:00 01-05-2016 04-06-2016
623 Tuesday 12:00:00 02:00:00 01-05-2016 04-06-2016
624 Wednesday 08:00:00 10:00:00 01-05-2016 04-06-2016
625 Thursday 10:00:00 12:00:00 01-05-2016 04-06-2016
I would like to list down all possible dateS from STARTDATE to ENDDATE. I have tried a query which is working except that it will list ALL POSSIBLE dates for every IDs. I may just need from one ID because all rows are having the same dates basically.
Query:
select
A.PRESENTATIONID,
A.PRESENTATIONDATESTART+delta dt
from
PRESENTATION A,
(
select level-1 as delta
from dual
connect by level-1 <= (
select max(PRESENTATIONDATEEND- PRESENTATIONDATESTART) from PRESENTATION
)
)
where A.PRESENTATIONDATESTART+delta <= A.PRESENTATIONDATEEND
order by 1,2;
The result:
622 01-05-2016 12:00:00 //startdate
.....
622 03-06-2016 12:00:00
622 04-06-2016 12:00:00 //enddate
.....
625 04-06-2016 12:00:00
The result is very tedious!
I would like to list down ONLY possible dates where the day is stored in my presentation table. For example, in presentation table the days are MONDAY,TUESDAY,WEDNESDAY and THURSDAY. Possible outcome will be:
MONDAY
02-05-2016 12:00:00
09-05-2016 12:00:00
16-05-2016 12:00:00
23-05-2016 12:00:00
30-05-2016 12:00:00
TUESDAY
03-05-2016 12:00:00
10-05-2016 12:00:00
17-05-2016 12:00:00
24-05-2016 12:00:00
31-05-2016 12:00:00
.... until THURSDAY
On the way to explain, in this problem I would like to EXCLUDE dates for FRIDAY, SATURDAY and SUNDAY. Is it possible?
UPDATE
New query:
select
A.PRESENTATIONID,
A.PRESENTATIONDAY,
A.PRESENTATIONDATESTART+delta dt
from
PRESENTATION A,
(
select level-1 as delta
from dual
connect by level-1 <= (
select max(PRESENTATIONDATEEND- PRESENTATIONDATESTART) from PRESENTATION
)
)
where A.PRESENTATIONDATESTART+delta <= A.PRESENTATIONDATEEND
and
a.presentationday = trim(to_char(A.PRESENTATIONDATESTART+delta, 'Day'))
order by 1,2,3;
Result:
622 Monday 02-05-2016 12:00:00
622 Monday 09-05-2016 12:00:00
622 Monday 16-05-2016 12:00:00
622 Monday 23-05-2016 12:00:00
622 Monday 30-05-2016 12:00:00
623 Tuesday 03-05-2016 12:00:00
623 Tuesday 10-05-2016 12:00:00
623 Tuesday 17-05-2016 12:00:00
623 Tuesday 24-05-2016 12:00:00
623 Tuesday 31-05-2016 12:00:00
624 Wednesday 04-05-2016 12:00:00
624 Wednesday 11-05-2016 12:00:00
624 Wednesday 18-05-2016 12:00:00
624 Wednesday 25-05-2016 12:00:00
624 Wednesday 01-06-2016 12:00:00
625 Thursday 05-05-2016 12:00:00
625 Thursday 12-05-2016 12:00:00
625 Thursday 19-05-2016 12:00:00
625 Thursday 26-05-2016 12:00:00
625 Thursday 02-06-2016 12:00:00
You may check the calendar to see if the days and dates are match :D Thank you again #dcieslak!
Add to your query:
and
a.day = trim(to_char(A.PRESENTATIONDATESTART+delta, 'Day'))
It will remove days that are not equal to your DAY column.
The trim is important for 'Day' because we want to get rid of leading and trailing spaces.