Oracle time table - Fill in missing times - oracle

I have a query that gives me renders per minute (when there is a render during the minute)
select count(*) as "Total Rendered", to_char(r.request_dt, 'YYYY-MM-DD HH24:MI') as "ByMinute" from form_render r where r.form_type_id = 49
and r.request_dt >= to_timestamp('09-16-2015 08:00', 'mm-dd-yyyy hh24:mi')
group by to_char(r.request_dt, 'YYYY-MM-DD HH24:MI')
order by to_char(r.request_dt, 'YYYY-MM-DD HH24:MI') desc
Total Rendered | ByMinute
19 2015-09-17 09:31
10 2015-09-17 09:30
1 2015-09-17 09:28
6 2015-09-17 09:27
18 2015-09-17 09:25
22 2015-09-17 09:24
12 2015-09-17 09:23
13 2015-09-17 09:21
22 2015-09-17 09:20
However I want a row for all times (by minute) even when there were none rendered (add rows for times when no renders occurred...09:22,09:26, 09:29)
So something like this
Total Rendered | ByMinute
19 2015-09-17 09:31
10 2015-09-17 09:30
0 2015-09-17 09:29
1 2015-09-17 09:28
6 2015-09-17 09:27
0 2015-09-17 09:26
18 2015-09-17 09:25
22 2015-09-17 09:24
12 2015-09-17 09:23
0 2015-09-17 09:22
13 2015-09-17 09:21
22 2015-09-17 09:20
Any help is greatly appreciated
Sean
===========================================================================
Sept 20 attempt.....
===========================================================================
Thank you very much for taking the time to put this together. SQL is definitely not my strength as I am sure you can tell!
So I have patched together the time table and my query but am getting an error (ORA01843: not a valid month.)
When I run my query by itself
select count(*)as "Total_Rendered" , to_char(r.request_dt, 'MM/DD/YYYY HH24:MI')as "ByMinute" from form_render r where r.form_type_id = 49
and r.request_dt >= to_timestamp('09/17/2015 09:11', 'mm/dd/yyyy hh24:mi') and r.request_dt <= to_timestamp('09/17/2015 09:18', 'mm/dd/yyyy hh24:mi')
group by to_char(r.request_dt, 'MM/DD/YYYY HH24:MI')
order by 2 desc
I get the following
TOTAL_RENDERED | ByMinute
---------------------------
4 | 09/17/2015 09:18
16 | 09/17/2015 09:17
4 | 09/17/2015 09:16
2 | 09/17/2015 09:11
When I run the minute_table query
WITH min_date AS /* replace start and stop timestamp here */
(SELECT to_date('09/17/2015 09:11', 'MM/DD/YYYY hh24:mi') minute FROM dual
),
max_date AS
(SELECT to_date('09/17/2015 09:18', 'MM/DD/YYYY hh24:mi') minute FROM dual
),
minute_table AS
(SELECT
(SELECT minute FROM min_date
) + (rownum -1)/(24*60) AS by_minute
FROM dual
CONNECT BY level <=
(SELECT (24*60) *(
(SELECT minute FROM max_date
) -
(SELECT minute FROM min_date
))+1
FROM dual
)
)
select * from minute_table
I get the following....
9/17/2015 9:11:00 AM
9/17/2015 9:12:00 AM
9/17/2015 9:13:00 AM
9/17/2015 9:14:00 AM
9/17/2015 9:15:00 AM
9/17/2015 9:16:00 AM
9/17/2015 9:17:00 AM
9/17/2015 9:18:00 AM
so far so good.
When I combine the two queries to get this query
WITH min_date AS /* replace start and stop timestamp here */
(SELECT to_date('09/17/2015 09:11', 'MM/DD/YYYY hh24:mi:ss') minute FROM dual
),
max_date AS
(SELECT to_date('09/17/2015 09:18', 'MM/DD/YYYY hh24:mi:ss') minute FROM dual
),
minute_table AS
(SELECT
(SELECT minute FROM min_date
) + (rownum -1)/(24*60) AS by_minute
FROM dual
CONNECT BY level <=
(SELECT (24*60) *(
(SELECT minute FROM max_date
) -
(SELECT minute FROM min_date
))+1
FROM dual
)
),
tbl AS
(
select count(*)as "Total_Rendered" , to_char(r.request_dt, 'MM/DD/YYYY HH24:MI')as "ByMinute" from form_render r where r.form_type_id = 49
and r.request_dt >= to_timestamp('09/17/2015 09:11', 'mm/dd/yyyy hh24:mi') and r.request_dt <= to_timestamp('09/17/2015 09:18', 'mm/dd/yyyy hh24:mi')
group by to_char(r.request_dt, 'MM/DD/YYYY HH24:MI')
order by 2 desc
)
SELECT minute_table.by_minute ,
NVL(tbl."Total_Rendered",'0') AS total_rendered
FROM minute_table
left OUTER JOIN tbl
ON tbl."ByMinute" = minute_table.by_minute
order by 1 desc
;
I get an error ORA01843: not a valid month.
I am not entirely certain why the error is occuring but I am confident it has to do with the format differences between the columns I am joining on?
The format of the dates in the time table vs my table are likely the cause but I am not certain.
Time Table has 9/17/2015 9:11:00 AM and
my query has 09/17/2015 09:11 (notice missing leading zero on Month, seconds and AM)
Any help is appreciated.
Thanks again for your time and expertise....
Sean

Slightly different solution without dependency on 12c and with "parameters" for start and stop minutes.
WITH min_date AS /* replace start and stop timestamp here */
(SELECT to_date('2015-09-17 09:28','yyyy-mm-dd hh24:mi') minute FROM dual
),
max_date AS
(SELECT to_date('2015-09-17 09:31','yyyy-mm-dd hh24:mi') minute FROM dual
),
minute_table AS
(SELECT
(SELECT minute FROM min_date
) + (rownum -1)/(24*60) AS by_minute
FROM dual
CONNECT BY level <=
(SELECT (24*60) *(
(SELECT minute FROM max_date
) -
(SELECT minute FROM min_date
))+1
FROM dual
)
),
tbl AS
(SELECT 19 Total_Rendered,
to_date('2015-09-17 09:31','YYYY-MM-DD HH24:MI') By_Minute
FROM dual
UNION ALL
SELECT 1,to_date('2015-09-17 09:30','YYYY-MM-DD HH24:MI') FROM dual
UNION ALL
SELECT 19,to_date('2015-09-17 09:28','YYYY-MM-DD HH24:MI') FROM dual
)
SELECT minute_table.by_minute ,
NVL(tbl.total_rendered,'0') AS total_rendered
FROM minute_table
LEFT OUTER JOIN tbl
ON tbl.By_Minute = minute_table.by_minute
;

So this is working in Oracle 12c. But you have to change it for your use.
with tbl(Total_Rendered, ByMinute) as (
select 19,to_date('2015-09-17 09:31','YYYY-MM-DD HH24:MI') from dual union all
select 1,to_date('2015-09-17 09:30','YYYY-MM-DD HH24:MI') from dual union all
select 19,to_date('2015-09-17 09:28','YYYY-MM-DD HH24:MI') from dual )
select nvl(tbl.total_rendered,'0') as total_rendered,by_minute from
(select to_date('2015-09-17 09:28','YYYY-MM-DD HH24:MI') + (1/24/60) * column_value as by_minute from
TABLE( CAST( MULTISET( SELECT LEVEL FROM DUAL
CONNECT BY LEVEL <= 3 ) AS SYS.ODCINUMBERLIST
) )) time_range
left outer join
tbl
on tbl.ByMinute = time_range.by_minute
Output
TOTAL_RENDERED BY_MINUTE
0 17-SEP-2015 09:29:00
1 17-SEP-2015 09:30:00
19 17-SEP-2015 09:31:00
What I did is first I am using a temporary table tbl which will have data as your input. Then I generated timestamp from '17-SEP-2015 09:29:00' - '17-SEP-2015 09:31:00'to generate a time_range
Now I did a left outer join from this time_range to tbl which has missing data. For the rows not in tbl, I am printing 0.
You can use this query and edit it to get the time_range and join with your data to get this output.

Related

I have one requirement where I have to show the records between specific date and time every day of one week

I have one requirement where I have to show the records between specific date and time every day in one week duration.
in one week duration( 2019-04-01 till 2019-04-06) ,for instance record of 2019-04-01 at 19 PM till 8 Am of 2019-04-02 ,and record of 2019-04-02 at 19 PM till 08 AM of 2019-04-03 and ...
would you please help me!
Use recursive query to create proper periods then join with your data or do it simpler with condition like here:
select callbegin, callerno
from table4
where callerno in ('7032','750')
and callbegin between timestamp '2019-04-01 19:00:00'
and timestamp '2019-04-06 08:00:00'
and ('19' <= to_char(callbegin, 'hh24') or to_char(callbegin, 'hh24') < '08');
demo
Here's how I understood the question.
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi';
Session altered.
SQL> break on period;
SQL> with
2 data (id, datum) as
3 (select 1, to_date('01.04.2019 15:30', 'dd.mm.yyyy hh24:mi') from dual union all
4 select 2, to_date('01.04.2019 20:00', 'dd.mm.yyyy hh24:mi') from dual union all -- 1st
5 select 3, to_date('02.04.2019 01:15', 'dd.mm.yyyy hh24:mi') from dual union all -- 1st perios
6 select 4, to_date('02.04.2019 11:00', 'dd.mm.yyyy hh24:mi') from dual union all
7 select 5, to_date('02.04.2019 23:15', 'dd.mm.yyyy hh24:mi') from dual union all -- 2nd period
8 select 6, to_date('03.04.2019 00:10', 'dd.mm.yyyy hh24:mi') from dual union all -- 2nd
9 select 7, to_date('04.04.2019 22:20', 'dd.mm.yyyy hh24:mi') from dual -- 3rd period
10 ),
11 test as
12 (select date '2019-04-01' dstart,
13 date '2019-04-06' dend
14 from dual
15 ),
16 inter as
17 (select dstart + level - 1 datum
18 from test
19 connect by level <= dend - dstart + 1
20 ),
21 from_to as
22 (select datum + 19/24 date_from,
23 lead(datum) over (order by datum) + 8/24 date_to
24 from inter
25 )
26 select f.date_From ||' - '|| f.date_to period,
27 d.id,
28 d.datum
29 from data d join from_to f on 1 = 1
30 where d.datum between f.date_from and f.date_to
31 order by f.date_From, d.id;
PERIOD ID DATUM
----------------------------------- ---------- ----------------
01.04.2019 19:00 - 02.04.2019 08:00 2 01.04.2019 20:00
3 02.04.2019 01:15
02.04.2019 19:00 - 03.04.2019 08:00 5 02.04.2019 23:15
6 03.04.2019 00:10
04.04.2019 19:00 - 05.04.2019 08:00 7 04.04.2019 22:20
SQL>
This is how to filter data by days and time by one week:
With date_list as (
Select
to_date(to_char( (sysdate - level), 'yyyymmdd') || '19', 'yyyymmddhh24') begin_time,
to_date(to_char( ((sysdate - level)+1), 'yyyymmdd') || '08', 'yyyymmddhh24') end_time
From dual connect by level <= 7
)
Select begin_time, your_table.*
From
your_table t1,
date_list t2
Where
t1.your_date between t2.begin_time and t2.end_time;

Returning times between other times

I have two tables AVAIL and AVAIL_TIMES. AVAIL contains avail_id, avail_date, open_flag. AVAIL_TIMES contains avail_times_id, Avail_id, Start_Time, End_time. All date and time fields are typed as DATE
If a date is flagged in the avail open_flag column it means that the facility is open for that date, but the times it is open is listed in avail_times. There can be multiple time ranges for a particular day.
I need to return a list of times it is not open for that day.
For Example (one day of many)
Open times for day:
Start_time: 08:00 End_time 10:00
Start_time: 12:00 End_time 14:00
Start_time: 15:00 End_time 17:00
I want it to return something like:
00:00 - 07:59
10:01 - 11:59
14:01 - 14:59
17:01 - 23:59
I think I would be able to work through this with a temporary table and some plsql logic, but ideally this would be a pure sql solution.
I am not exactly sure how you want to input the date of interest (I used a bind variable, passed in as a string - but that may not be the right way for you, perhaps you want to join to your other table, etc.) - or the exact output you want. In any case, the query below demonstrates the "core" of the code you need to achieve this kind of output from the inputs.
alter session set nls_date_format='mm/dd/yyyy hh24:mi';
with
avail_times ( start_time, end_time ) as (
select to_date('06/20/2017 08:00'), to_date('06/20/2017 10:00') from dual union all
select to_date('06/20/2017 12:00'), to_date('06/20/2017 14:00') from dual union all
select to_date('06/20/2017 15:00'), to_date('06/20/2017 17:00') from dual
)
select trunc(min(start_time)) as start_time, min(start_time) as end_time
from avail_times
where trunc(start_time) = to_date(:input_date, 'mm/dd/yyyy')
union all
select end_time,
lead(start_time, 1, trunc(start_time) + 1) over (order by start_time)
from avail_times
where trunc(end_time) = trunc(start_time)
order by start_time
;
START_TIME END_TIME
---------------- ----------------
06/20/2017 00:00 06/20/2017 08:00
06/20/2017 10:00 06/20/2017 12:00
06/20/2017 14:00 06/20/2017 15:00
06/20/2017 17:00 06/21/2017 00:00
Another Approach. Hope this helps.
SELECT ID,
START_TME,
END_TM,
DIFF_TM
FROM
--Not part of SQL just to simulate the table data
(WITH TMP AS
(SELECT 1 ID,
TO_DATE('06/27/2017 00:00','mm/dd/yyyy hh24:mi') START_TME,
TO_DATE('06/27/2017 08:00','mm/dd/yyyy hh24:mi') END_TM
FROM DUAL
UNION ALL
SELECT 1 ID,
TO_DATE('06/27/2017 10:00','mm/dd/yyyy hh24:mi') START_TME,
TO_DATE('06/27/2017 15:00','mm/dd/yyyy hh24:mi') END_TM
FROM DUAL
UNION ALL
SELECT 1 ID,
TO_DATE('06/27/2017 16:00','mm/dd/yyyy hh24:mi') START_TME,
TO_DATE('06/27/2017 17:00','mm/dd/yyyy hh24:mi') END_TM
FROM DUAL
UNION ALL
SELECT 1 id,
to_date('06/27/2017 17:00','mm/dd/yyyy hh24:mi') start_tme,
TO_DATE('06/27/2017 18:00','mm/dd/yyyy hh24:mi') END_TM
FROM DUAL
)
--SQL start from here
SELECT TMP.*,
LEAD(START_TME) OVER(PARTITION BY ID ORDER BY 1 DESC) next_st_tm,
LEAD(END_TM) OVER(PARTITION BY ID ORDER BY 1 DESC) NEXT_EN_TM,
EXTRACT( HOUR FROM TO_TIMESTAMP(LEAD(START_TME) OVER(PARTITION BY ID ORDER BY 1 DESC),'MM/DD/YYYY HH24:MI'))- EXTRACT(HOUR FROM TO_TIMESTAMP(end_tm,'MM/DD/YYYY HH24:MI')) DIFF_TM
FROM TMP
ORDER BY 1 ,
2
)
WHERE DIFF_TM <> 0;

Oracle - getting row numbers for a group

I have the SQL below which Im using to get row numbers. What I want is to know how many rows are in a particular group. Is this possible?
Thanks
select
to_char(ndt.dt , 'yyyy'),
to_char(ndt.dt , 'MON'),
to_char(ndt.dt , 'dd'),
row_number() OVER (partition by to_char(ndt.dt , 'yyyy'), to_char(ndt.dt , 'MON') ORDER BY ndt.dt)
from (
(SELECT TRUNC (SYSDATE - ROWNUM + 44) dt
FROM DUAL CONNECT BY ROWNUM < 91
order by dt asc) ndt
);
but this gives me
|2016| APR |01| 1|
|2016| APR |02| 2|
...
|2016| APR|30| 30|
|2016|MAY| 01 |1|
|2016| MAY |
whereas what I really want is
|2016| APR |01| 30|
|2016| APR |02| 30|
...
|2016| APR|30| 30|
|2016|MAY| 01 |31|
|2016| MAY |31|
You can try with this, using count instead or row_number:
select to_char(d, 'yyyy'), to_char(d, 'mon'), to_char(d, 'dd'),
count(1) over ( partition by extract(month from d)) as count
from (
select to_date('01042016', 'ddmmyyyy') + level -1 as d
from dual
connect by level <= 61
)
order by 1, 2, 3
The logics behind your +44 is not that clear to me, so I used a simple starting date

Count the no of saturdays and sundays in date range - oracle [duplicate]

This question already has answers here:
Number of fridays between two dates
(7 answers)
Closed 8 years ago.
I have two parameters(start_Date,end_Date) from table1
I'm trying to count no of saturdays and sundays in a date range
star_Date=8/20/2014 13:52
end_Date=8/28/2014 13:52
And result should be like this
Start_Date end_date No_of_leaves
8/20/2014 13:52 8/28/2014 13:52 2
Update Section
SELECT retouch_req_time,retouch_submit_time,(
SELECT Count(*) FROM (SELECT To_char(start_date + ( LEVEL - 1 ), 'fmday') dt
FROM (WITH t AS (SELECT To_date (retouch_req_time, 'MM/DD/YYYY HH24:MI') start_date, To_date (retouch_submit_time, 'MM/DD/YYYY HH24:MI') end_date FROM TT))
CONNECT BY LEVEL <= end_date - start_date + 1) WHERE dt IN ('friday','saturday')) as worked_hours
FROM TT
You can try using hierarchical queries
WITH t
AS (SELECT To_date ('8/20/2014 13:52', 'MM/DD/YYYY HH24:MI') start_date,
To_date ('8/28/2014 13:52', 'MM/DD/YYYY HH24:MI') end_date
FROM dual)
SELECT Count(*)
FROM (SELECT To_char(start_date + ( LEVEL - 1 ), 'fmday') dt
FROM t
CONNECT BY LEVEL <= end_date - start_date + 1)
WHERE dt IN ( 'friday', 'saturday' );
RESULT
------
2
* The dates are listed by expanding the range.
* The TO_CHAR function is used to obtain the weekday
* Count everthing which is a friday or saturday
If you want to find the day wise count, then you can try
SELECT To_char(dat, 'DY'),
Count(*)
FROM (SELECT To_date ('8/20/2014 13:52', 'MM/DD/YYYY HH24:MI')
+ num dat
FROM (SELECT LEVEL - 1 num
FROM dual
CONNECT BY LEVEL <= Abs(To_date ('8/20/2014 13:52',
'MM/DD/YYYY HH24:MI') -
To_date (
'8/28/2014 13:52'
,
'MM/DD/YYYY HH24:MI')) - 1
))
WHERE To_char(dat, 'DY') IN ( 'FRI', 'SAT' )
GROUP BY To_char(dat, 'DY');
RESULTS
TO_CHAR(DAT,'DY') COUNT(*)
----------------- --------
FRI 1
SAT 1
You can calculate the number of saturdays and sundays like this:
with t(d) as (
select sysdate + level from dual connect by rownum < 10
)
select count(case when trim(to_char(d, 'DAY')) in ('SATURDAY', 'SUNDAY') then 1 end) cnt from t
CNT
---
2
If you don't have a range of dates then:
with t(a, b) as (
select sysdate a, sysdate + 10 b from dual connect by rownum < 10
), t2(d) as (
select a + level - 1 from t connect by rownum <= b - a
)
select count(case when trim(to_char(d, 'DAY')) in ('SATURDAY', 'SUNDAY') then 1 end) cnt from t2
CNT
---
2

Oracle PLSQL truncate datetime to specific hours

I have an Oracle PLSQL code generating a list of datetime stamps and I would like to truncate them to the specific hours of 7am and 7pm rather than the beginning of the day.
For example:
01/03/2013 0700 becomes 01/03/2013 0700
01/03/2013 1235 becomes 01/03/2013 0700
01/03/2013 1932 becomes 01/03/2013 1900
02/03/2013 0612 becomes 01/03/2013 1900
My code is currently:
SELECT TRUNC(TRUNC(SYSDATE,'hh') + 1/24 - (ROWNUM) / 24, 'dd') as shift_date
FROM widsys.times
ORDER BY SYSDATE
Thanks
Without conditionals :)
Select your_date,
trunc(your_date - 7/24) + --the date
trunc(to_char(your_date - 7/24,'hh24')/12)/2 + --wich half of day
7/24 --shift the hour
from
your_table;
See a fiddle.
with data(time) as (
select to_date('2013-09-19 00:00:00', 'YYYY-MM-DD HH24:MI:SS') from dual union all
select to_date('2013-09-19 06:45:44', 'YYYY-MM-DD HH24:MI:SS') from dual union all
select to_date('2013-09-19 08:12:25', 'YYYY-MM-DD HH24:MI:SS') from dual union all
select to_date('2013-09-19 18:59:59', 'YYYY-MM-DD HH24:MI:SS') from dual union all
select to_date('2013-09-19 19:00:00', 'YYYY-MM-DD HH24:MI:SS') from dual union all
select to_date('2013-09-19 20:15:35', 'YYYY-MM-DD HH24:MI:SS') from dual union all
select to_date('2013-09-19 23:59:59', 'YYYY-MM-DD HH24:MI:SS') from dual
)
select d.time,
case
when to_number(to_char(d.time, 'HH24')) >= 19 then
trunc(d.time) + 19/24
when to_number(to_char(d.time, 'HH24')) >= 7 then
trunc(d.time) + 7/24
else
trunc(d.time - 1) + 19/24
end as shift_date
from data d
;
Are you looking for a query like this:
SELECT CASE
WHEN TO_CHAR(your_date, 'hh24') > 12
THEN TRUNC(your_date)+ 19/24
ELSE TRUNC(your_date)+ 7/24
END
FROM your_table;

Resources