Connect by and level - hour interval - sql

I have the following data:
ID SESSION START_DATE END_DATE
1 A 01/01/2016 22:35 02/01/2016 02:35
1 B 02/01/2016 02:35 02/01/2016 04:45
2 A 01/01/2016 00:00 01/01/2016 02:00
2 B 01/01/2016 02:00 01/01/2016 03:30
And I need to return like this:
ID SESSION START_DATE END_DATE
1 A 01/01/2016 22:35 01/01/2016 22:59
1 A 01/01/2016 23:00 01/01/2016 23:59
1 A 02/01/2016 00:00 02/01/2016 00:59
1 A 02/01/2016 01:00 02/01/2016 01:59
1 A 02/01/2016 02:00 02/01/2016 02:35
1 B 02/01/2016 02:35 02/01/2016 02:59
1 B 02/01/2016 03:00 02/01/2016 03:59
1 B 02/01/2016 04:00 02/01/2016 04:45
2 A 01/01/2016 00:00 01/01/2016 00:59
2 A ...
any help?

You can try something like this:
SELECT DISTINCT session,
id,
st,
end
FROM ( SELECT DECODE(LEVEL, 1, start_date, TRUNC(start_date, 'HH') + (LEVEL - 1) / 24) st,
PRIOR id p_id,
id,
PRIOR session p_session,
session,
LEAST(end_date, TRUNC(start_date, 'HH') + (LEVEL) / 24 - 1 / 24 / 60) end
FROM tab
CONNECT BY LEVEL <= EXTRACT(HOUR FROM end_date - start_date) + 1)
WHERE ( p_id = id
AND p_session = session)
OR (p_id IS NULL)
I would like to avoid the usage of DISTINCT, but could not find a way, hope someone else could give a better solution

Here is a solution, you just have to play a little with hour extractions:
create table connect_by_hour_test(id number, sess varchar2(1), start_date date, end_date date);
insert into connect_by_hour_test (id, sess, start_date, end_date) values (1, 'A', to_date('01/01/2016 22:30', 'dd/mm/yyyy hh24:mi'), to_date('02/01/2016 02:35', 'dd/mm/yyyy hh24:mi'));
insert into connect_by_hour_test (id, sess, start_date, end_date) values (1, 'B', to_date('02/01/2016 02:35', 'dd/mm/yyyy hh24:mi'), to_date('02/01/2016 04:45', 'dd/mm/yyyy hh24:mi'));
insert into connect_by_hour_test (id, sess, start_date, end_date) values (2, 'A', to_date('01/01/2016 00:00', 'dd/mm/yyyy hh24:mi'), to_date('01/01/2016 02:00', 'dd/mm/yyyy hh24:mi'));
insert into connect_by_hour_test (id, sess, start_date, end_date) values (2, 'B', to_date('01/01/2016 02:00', 'dd/mm/yyyy hh24:mi'), to_date('01/01/2016 03:30', 'dd/mm/yyyy hh24:mi'));
commit;
with max_level as (
select id, sess, ceil((end_date - start_date) / (1/24)) as val from connect_by_hour_test ) -- get the max number of iterations for each (id, sess) pair
select distinct
id,
sess,
decode(level, 1, to_char(start_date, 'dd/mm/yyyy hh24:mi'), to_char(trunc(start_date, 'hh24') + 1/24 * level - 1/24, 'dd/mm/yyyy hh24:mi')) start_date, -- if first iteration in pair show the start_date, otherwise show the start date, plus the number of hours for the current iteration(first iteration - 1 hour, 2nd iteration - two hours, etc) minus one minute
decode(level, (select val from max_level ml where ml.id = src.id and ml.sess = src.sess), to_char(end_date, 'dd/mm/yyyy hh24:mi'), to_char(trunc(start_date, 'hh24') + 1/24 * level - 1/24/60, 'dd/mm/yyyy hh24:mi')) end_date -- if last iteration show end_date, otherwise show value from previous iteration plus one minute
from
connect_by_hour_test src
connect by
trunc(start_date, 'hh24') + 1/24 * (level - 1) <= end_date order by 1,2;
Edit: Corrections made after feedback, added step to be level - 1 and max_level calculated as ceil, instead of trunc, of end_date - start_date

select level,
NR_ATENDIMENTO,
CD_SETOR_ATENDIMENTO,
greatest(DT_ENTRADA_UNIDADE, trunc(DT_ENTRADA_UNIDADE+(level-1)/24, 'hh24')) DT_ENTRADA_UNIDADE,
to_char(trunc(DT_ENTRADA_UNIDADE, 'hh24') + 1/24 * level - 1/24/60, 'dd/mm/yyyy hh24:mi:ss') DT_SAIDA_UNIDADE
from
(
SELECT
CD_SETOR_ATENDIMENTO,
NR_SEQ_INTERNO,
DT_ENTRADA_UNIDADE,
nvl(DT_SAIDA_UNIDADE,sysdate) DT_SAIDA_UNIDADE,
NR_ATENDIMENTO
FROM ATEND_PACIENTE_UNIDADE
WHERE OBTER_TIPO_UNIDADE_ATEND(NR_ATENDIMENTO, NR_SEQ_INTERNO, IE_PASSAGEM_SETOR) NOT IN ('S')
AND nr_atendimento = 831838
ORDER BY NR_ATENDIMENTO,
CD_SETOR_ATENDIMENTO,
DT_ENTRADA_UNIDADE DESC
)
WHERE 1=1
CONNECT BY ROWID=PRIOR ROWID
and trunc(DT_ENTRADA_UNIDADE, 'hh24') + 1/24 * (level - 1) <= nvl(DT_SAIDA_UNIDADE,sysdate)
AND PRIOR SYS_GUID() IS NOT NULL
order by 2,3,4;

Related

finding the time periods an issuer has not called the service from a given time period

I have a table that contains the time periods when an issuer calls the service. this table can have overlapping and non overlapping time periods:
with mht_issuer_revoked_call (issuerid, startdate, enddate) as (values
(4, to_date('25-11-2022', 'dd-mm-yyyy'), to_date('25-11-2022 12:00:00', 'dd-mm-yyyy hh24:mi:ss'),
(4, to_date('25-11-2022 12:00:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('26-11-2022', 'dd-mm-yyyy'),
(40, to_date('25-11-2022', 'dd-mm-yyyy'), to_date('25-11-2022 06:00:00', 'dd-mm-yyyy hh24:mi:ss'),
(40, to_date('25-11-2022 06:00:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('25-11-2022 12:00:00', 'dd-mm-yyyy hh24:mi:ss'),
(40, to_date('25-11-2022 11:30:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('25-11-2022 18:00:00', 'dd-mm-yyyy hh24:mi:ss'),
(40, to_date('25-11-2022 18:30:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('25-11-2022 19:30:00', 'dd-mm-yyyy hh24:mi:ss'),
(50, to_date('25-11-2022', 'dd-mm-yyyy'), to_date('25-11-2022 12:00:00', 'dd-mm-yyyy hh24:mi:ss'),
(50, to_date('25-11-2022 11:00:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('26-11-2022 01:30:00', 'dd-mm-yyyy hh24:mi:ss'),
(40, to_date('25-11-2022 19:31:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('26-11-2022', 'dd-mm-yyyy'),
(50, to_date('25-11-2022 23:10:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('25-11-2022 23:30:00', 'dd-mm-yyyy hh24:mi:ss'),
(50, to_date('25-11-2022 23:30:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('25-11-2022 23:45:00', 'dd-mm-yyyy hh24:mi:ss'),
(50, to_date('25-11-2022 23:50:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('25-11-2022 23:55:00', 'dd-mm-yyyy hh24:mi:ss')
)
i managed to merge the time periods and new time periods dont have any overlapping with each other. my output is as follows:
with issuer_calls_merged (issuerid, start_date_time, end_date_time) as (values
(4 ,'11/25/2022' , '11/26/2022'),
(40 ,'11/25/2022', '11/25/2022 6:00:00 PM'),
(40 ,'11/25/2022 6:30:00 PM', '11/25/2022 7:30:00 PM'),
(40 ,'11/25/2022 7:31:00 PM', '11/26/2022' ),
(50 ,'11/25/2022', '11/26/2022 1:30:00 AM')
)
i am trying to write a procedure that gets FromDate and EndDate as input parameters and for each issuer calculates how many minutes are not covered according to retrieved FromDate and EndDate Parameters.
for example i will give these parameters:
FromDate := '11/20/2022'
EndDate := '11/28/2022'
then according to inserted time periods in issuer_calls table, for issuerid 40 i expect this output:
| issuerid | start_date_time(uncovered) | end_date_time(uncovered) | uncovered_time_minutes
| 40 | 11/20/2022 | 11/25/2022 | 7200
| 40 | 11/25/2022 6:00:00 PM | 11/25/2022 6:30:00 PM | 30
| 40 | 11/25/2022 7:30:00 PM | 11/25/2022 7:31:00 PM | 1
| 40 | 11/26/2022 | 11/28/2022 | 2880
i tried to do the job with procedure bellow:
create or replace procedure GAP(out_res out sys_refcursor,
in_FromDate mht_issuer_revoked_call.startdate%type,
in_EndDate mht_issuer_revoked_call.enddate%type
) AS
BEGIN
**-- i tried to compare the given time period(FromDate-EndDate) with previous merged time periods and calculate the gaps and then union with previous gap**
open out_res for
select ut.issuerid,
ut.startdate,
ut.enddate,
ut.initialgap as gap
from
(
with minStartDate as
(
select r.issuerid,
min(r.startdate) as min_StartDate
from mht_issuer_revoked_call r
group by r.issuerid
)
select m.issuerid,
in_FromDate as StartDate,
case
when m.min_StartDate >= in_EndDate then in_EndDate
else m.min_StartDate
end as EndDate,
case
when m.min_StartDate >= in_EndDate then (in_EndDate - in_FromDate + 1)*24*60
else (min_StartDate - in_FromDate + 1)*24*60
end as initialgap
from minStartDate m
union all
**--- bellow part merges the time periods and calculate the gaps between them**
SELECT issuerid,
end_date_time,
next_row_start,
(next_row_start - end_date_time)*24*60 as gap
from
(
SELECT issuerid,
start_date_time,
end_date_time,
case
when lead(start_date_time) over(partition by issuerid order by start_date_time) is null then end_date_time
else lead(start_date_time) over(partition by issuerid order by start_date_time)
end as next_row_start
FROM (
SELECT issuerid,
LAG( dt ) OVER ( PARTITION BY issuerid ORDER BY dt ) AS start_date_time,
dt AS end_date_time,
start_end
FROM (
SELECT issuerid,
dt,
CASE SUM( value ) OVER ( PARTITION BY issuerid ORDER BY dt ASC, value DESC, ROWNUM ) * value
WHEN 1 THEN 'start'
WHEN 0 THEN 'end'
END AS start_end
FROM mht_issuer_revoked_call
UNPIVOT ( dt FOR value IN ( startdate AS 1, enddate AS -1 ) )
)
WHERE start_end IS NOT NULL
)
WHERE start_end = 'end'
)
where (next_row_start - end_date_time) > 0
group by issuerid,next_row_start,end_date_time
) ut
order by ut.issuerid, ut.StartDate;
END gap;
but at the end i couldn't achieve the explained result above
You can get your result using just SQL and process it later. In this answer your FromDate And EndDate (P_FROM, P_UNTILL) are set to those from your question. You can define them as parameters or bind variables so you could change them. Comments are in the code.
WITH
tbl AS -- sample data
(
Select 4 "ID", To_Date('25.11.2022 00:00:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 12:00:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 4 "ID", To_Date('25.11.2022 12:00:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('26.11.2022 00:00:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 40 "ID", To_Date('25.11.2022 00:00:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 06:00:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 40 "ID", To_Date('25.11.2022 06:00:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 12:00:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 40 "ID", To_Date('25.11.2022 11:30:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 18:00:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 40 "ID", To_Date('25.11.2022 18:30:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 19:30:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 40 "ID", To_Date('25.11.2022 19:31:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('26.11.2022 00:00:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 50 "ID", To_Date('25.11.2022 00:00:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 12:00:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 50 "ID", To_Date('25.11.2022 11:00:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('26.11.2022 01:30:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 50 "ID", To_Date('25.11.2022 23:10:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 23:30:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 50 "ID", To_Date('25.11.2022 23:30:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 23:45:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 50 "ID", To_Date('25.11.2022 23:50:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 23:55:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual
),
day_tbl AS -- create CTE to prepare your data
( Select ID,
ROW_NUMBER() OVER(Partition By ID Order By START_DATE) "RN", -- ordering ID events
START_DATE "START_DATE", To_Char(START_DATE, 'hh24:mi:ss') "START_TIME", -- just showing the time part of START_DATE
END_DATE "END_DATE", To_Char(END_DATE, 'hh24:mi:ss') "END_TIME", -- just showing the time part of END_DATE
--
To_Date('20.11.2022', 'dd.mm.yyyy') "P_FROM", -- column with P_FROM - you could define it as bind variable
To_Date('28.11.2022', 'dd.mm.yyyy') "P_UNTILL", -- column with P_FROM - you could define it as bind variable
( END_DATE - START_DATE ) * 24 * 60 "MINS" -- first calculation used for first and last row
From
(Select *
From ( -- for each ID create starting and ending row and union them with your data
Select ID "ID", START_DATE "START_DATE", END_DATE "END_DATE" From tbl Union ALL
Select ID, To_Date('20.11.2022', 'dd.mm.yyyy'), Min(START_DATE) From tbl GROUP BY ID Union All -- row with P_FROM as START_DATE - you could define it as bind variable
Select ID, Max(END_DATE), To_Date('28.11.2022', 'dd.mm.yyyy') From tbl GROUP BY ID -- row with P_UNTILL as END_DATE - you could define it as bind variable
)
Order By ID, START_DATE
)
)
SELECT
* -- you can select just the columns you need (not all of them like here)
FROM
( Select
ID, RN, START_DATE, START_TIME, END_DATE, END_TIME, P_FROM, P_UNTILL,
CASE WHEN RN = 1 Or RN = Max(RN) OVER(Partition By ID) THEN MINS -- first and last row already calculated
-- else --> second calculation for rows that are not first nor last
ELSE Round(( START_DATE - FIRST_VALUE(END_DATE) OVER(Partition By ID, TRUNC(START_DATE) Order By START_DATE Rows Between 1 Preceding And Current Row) ) * 24 * 60, 0)
END "MINS"
From
day_tbl
)
WHERE
MINS > 0 -- if you want just ID=40 here you can filter it
--
/* R e s u l t :
ID RN START_DATE START_TIME END_DATE END_TIME P_FROM P_UNTILL MINS
---------- ---------- ---------- ---------- --------- -------- --------- --------- ----------
4 1 20-NOV-22 00:00:00 25-NOV-22 00:00:00 20-NOV-22 28-NOV-22 7200
4 4 26-NOV-22 00:00:00 28-NOV-22 00:00:00 20-NOV-22 28-NOV-22 2880
40 1 20-NOV-22 00:00:00 25-NOV-22 00:00:00 20-NOV-22 28-NOV-22 7200
40 5 25-NOV-22 18:30:00 25-NOV-22 19:30:00 20-NOV-22 28-NOV-22 30
40 6 25-NOV-22 19:31:00 26-NOV-22 00:00:00 20-NOV-22 28-NOV-22 1
40 7 26-NOV-22 00:00:00 28-NOV-22 00:00:00 20-NOV-22 28-NOV-22 2880
50 1 20-NOV-22 00:00:00 25-NOV-22 00:00:00 20-NOV-22 28-NOV-22 7200
50 6 25-NOV-22 23:50:00 25-NOV-22 23:55:00 20-NOV-22 28-NOV-22 5
50 7 26-NOV-22 01:30:00 28-NOV-22 00:00:00 20-NOV-22 28-NOV-22 2790
*/
i think i could finally finish the job.
mht_issuer_revoked_call: this is the table whenever an issuer calls the service, issuerid, startdate and enddate are submitted.
MHT_ISSUER_MERGED_CALLS: the requests of issuers are merged and stored in this table.
MHT_ISSUER_UNIONED_CALLS: merged calls and input parameters are unioned and stored in this table.
create or replace procedure GAP(out_res out sys_refcursor,
in_FromDate mht_issuer_revoked_call.startdate%type,
in_EndDate mht_issuer_revoked_call.enddate%type
) as
BEGIN
delete from MHT_ISSUER_MERGED_CALLS;
commit;
insert into MHT_ISSUER_MERGED_CALLS (
select issuerid,
start_date_time as start_date,
end_date_time as end_date
FROM (
SELECT issuerid,
LAG(dt) OVER(PARTITION BY issuerid ORDER BY dt) AS start_date_time,
dt AS end_date_time,
start_end
FROM (
SELECT issuerid,
dt,
CASE SUM(value) OVER(PARTITION BY issuerid ORDER BY dt ASC, value DESC, ROWNUM) * value
WHEN 1 THEN 'start'
WHEN 0 THEN 'end'
END AS start_end
FROM mht_issuer_revoked_call UNPIVOT(dt FOR value IN(startdate AS 1, enddate AS - 1))
)
WHERE start_end IS NOT NULL
)
WHERE start_end = 'end');
commit;
delete from MHT_ISSUER_UNIONED_CALLS;
commit;
insert into MHT_ISSUER_UNIONED_CALLS
(
Select ISSUERID,
ROW_NUMBER() OVER(Partition By ISSUERID Order By START_DATE) "RN",
START_DATE "START_DATE",
To_Char(START_DATE, 'hh24:mi:ss') "START_TIME",
END_DATE "END_DATE",
To_Char(END_DATE, 'hh24:mi:ss') "END_TIME",
in_FromDate "P_FROM",
in_EndDate "P_UNTILL",
(END_DATE - START_DATE) * 24 * 60 "MINS"
From (
Select *
From (
Select issuerid "ISSUERID",
STARTDATE "START_DATE",
ENDDATE "END_DATE"
From MHT_ISSUER_MERGED_CALLS
Union ALL
Select issuerid,
case
when in_FromDate >= Min(STARTDATE) then Min(STARTDATE)
else in_FromDate
end,
case
when in_EndDate <= Min(STARTDATE) then in_EndDate
else Min(STARTDATE)
end
From MHT_ISSUER_MERGED_CALLS
GROUP BY issuerid
Union All
Select issuerid,
case
when Max(ENDDATE) <= in_FromDate then in_FromDate
else Max(ENDDATE)
end,
case
when in_EndDate <= Max(ENDDATE) then Max(ENDDATE)
else in_EndDate
end
From MHT_ISSUER_MERGED_CALLS
GROUP BY issuerid)
Order By ISSUERID, START_DATE ASC, END_DATE ASC
)
);
COMMIT;
open out_res for
select ISSUERID,
START_DATE AS START_DATE,
SELECTED_END_DATE AS END_DATE,
MINS AS GAP_MINUTES
from
(
Select ISSUERID,
RN,
START_DATE,
trunc(start_date),
START_TIME,
END_DATE,
END_TIME,
P_FROM,
P_UNTILL,
FIRST_VALUE(END_DATE) OVER(Partition By ISSUERID, TRUNC(START_DATE) Order By START_DATE Rows Between 1 Preceding And Current Row) as Selected_End_Date,
CASE
WHEN RN = 1 Or RN = Max(RN) OVER(Partition By ISSUERID) THEN MINS
ELSE
Round((START_DATE - FIRST_VALUE(END_DATE)OVER(Partition By ISSUERID, TRUNC(START_DATE) Order By START_DATE
Rows Between 1 Preceding And Current Row)) * 24 * 60,0)
END "MINS"
From MHT_ISSUER_UNIONED_CALLS
)
where mins > 0
and START_DATE >= in_FromDate and END_DATE <= in_EndDate;
END gap;

Oracle SQL Query to fetch non-working hours in a given month

Need your help to frame a Oracle SQL query to capture non-working hours in a given month for the list of trips.
Values passed will be start date and end date
E.g.:
Feb has totally 696 hrs
Start Date End Date
S2 - 02-Feb-16 14:00 - 06-Feb-16 20:00 - 102hrs
S1 - 01-Feb-16 04:00 - 02-Feb-16 10:00 - 30hrs
Total Worked hrs - 132 hrs
The query result expected is 564 hrs
Attempted Query:
SELECT (a)
FROM (
SELECT COUNT( (SELECT DISTINCT 'O'
FROM TRIP s,
TRUCK p
WHERE s.TRUCKID = p.TRUCKID
AND p.NOTES2='ABC'
AND p.TRUCKID='553'
AND ( to_date(d,'DD-Mon-YYYY HH24')
NOT BETWEEN to_date(s.STIME,'DD-Mon-YYYY HH24')
AND to_date(s.ETIME,'DD-Mon-YYYY HH24')
)
) ) a
FROM (
SELECT (TRUNC(to_date(sysdate,'DD-MON-YYYY'),'MM') + level - 1) d
FROM dual
CONNECT BY level <= TO_CHAR(LAST_DAY(to_date(sysdate,'DD-MON-YYYY')),'DD')
)
)
WITH Trip_Dates ( Start_Date, End_Date ) AS (
SELECT TO_DATE( '2016-02-02 14:00:00', 'YYYY-MM-DD HH24:MI:SS' ), TO_DATE( '2016-02-06 20:00:00', 'YYYY-MM-DD HH24:MI:SS' ) FROM DUAL UNION ALL
SELECT TO_DATE( '2016-02-01 04:00:00', 'YYYY-MM-DD HH24:MI:SS' ), TO_DATE( '2016-02-02 10:00:00', 'YYYY-MM-DD HH24:MI:SS' ) FROM DUAL
)
SELECT ( ADD_MONTHS( TRUNC( SYSDATE, 'MM' ), 1 ) - TRUNC( SYSDATE, 'MM' )
- SUM( End_Date - Start_Date ) ) * 24 AS Unworked_Hours
FROM Trip_Dates;
Outputs:
UNWORKED_HOURS
--------------
564

Credit for sales report filing time

I have two date_time fields. The first is the date_time of the sale and the second is the date_time the sales report is filed.
In order for the salesperson to obtain credit, the sales report has to be filed by midnight on the date after the sale was made.
I'm currently calculating the difference, and know that anything over 24 hours could be out of a qualifying time period. Also, there are times when the sales report is filed prior to the date_time of the sale.
I searched through previous answers and couldn't find anything similar.
You need something like:
select to_char(report_time, 'mm/dd/yyyy hh24:mi:ss') report_time,
to_char(sales_time, 'mm/dd/yyyy hh24:mi:ss') sales_time,
round((report_time - sales_time) * 24, 6) diff_in_hours,
case when report_time < trunc(sales_time) + 2 then 'Y' else 'N' end decision
from sales
Function trunc() cuts hours, minutes and seconds from sales_time. For instance for date 01/15/2015 11:59:00 it returns 01/15/2015 00:00:00.
Next we add 2 days to this result. Report_date has to be lower then this value. So:
report_time < trunc(sales_time) + 2
Test with sample data:
with sales as (select
to_date('01/15/2015 11:59:00', 'mm/dd/yyyy hh24:mi:ss') report_time,
to_date('01/15/2015 11:45:00', 'mm/dd/yyyy hh24:mi:ss') sales_time from dual
union all select
to_date('01/16/2015 23:59:00', 'mm/dd/yyyy hh24:mi:ss'),
to_date('01/15/2015 12:45:00', 'mm/dd/yyyy hh24:mi:ss') from dual
union all select
to_date('01/17/2015 00:00:01', 'mm/dd/yyyy hh24:mi:ss'),
to_date('01/15/2015 23:59:00', 'mm/dd/yyyy hh24:mi:ss') from dual)
select to_char(report_time, 'mm/dd/yyyy hh24:mi:ss') report_time,
to_char(sales_time, 'mm/dd/yyyy hh24:mi:ss') sales_time,
round((report_time - sales_time) * 24, 6) diff_in_hours,
case when report_time < trunc(sales_time) + 2 then 'Y' else 'N' end decision
from sales
Output:
REPORT_TIME SALES_TIME DIFF_IN_HOURS DECISION
------------------- ------------------- ------------- --------
01/15/2015 11:59:00 01/15/2015 11:45:00 0,233333 Y
01/16/2015 23:59:00 01/15/2015 12:45:00 35,233333 Y
01/17/2015 00:00:01 01/15/2015 23:59:00 24,016944 N

Count records per hour within a time span

I have a table with a userID, a startDate and an endDate.
I would like to count hour by hour the number of userID concerned.
For example, the user '4242' with startDate = '21/05/2014 01:15:00' and with endDate = '21/05/2014 05:22:00' should be counted once from 01 to 02, once from 02 to 03, once from 03 to 04, ...
It would give a result like that:
DATE AND TIME COUNT
-------------------------------------
20140930 18-19 198
20140930 19-20 220
20140930 20-21 236
20140930 21-22 257
20140930 22-23 257
20140930 23-00 257
20141001 00-01 259
20141001 01-02 259
20141001 02-03 258
20141001 03-04 259
20141001 04-05 258
20141001 05-06 258
How would you do that ?
Well, I tried a lot of things. Here's my latest attempt. If the code is too messy, don't even bother reading it, just tell me how you would handle this problem ;) Thanks !
WITH timespan AS (
SELECT lpad(rownum - 1,2,'00') ||'-'|| lpad(mod(rownum,24),2,'00') AS hours
FROM dual
connect BY level <= 24
),
UserID_min_max AS (
SELECT USERS.UserID,
min(USERS.date_startUT) AS min_date,
max(USERS.date_end) AS max_date,
code_etat
FROM USERS
WHERE (
(USERS.date_startUT >= to_date('01/10/2014 00:00:00','dd/MM/YYYY HH24:mi:ss')
AND USERS.date_end <= to_date('08/10/2014 23:59:00','dd/MM/YYYY HH24:mi:ss'))
OR ( USERS.date_startUT <= to_date('01/10/2014 00:00:00','dd/MM/YYYY HH24:mi:ss')
AND USERS.date_end >= to_date('01/10/2014 00:00:00','dd/MM/YYYY HH24:mi:ss')
AND USERS.date_end <= to_date('08/10/2014 23:59:00','dd/MM/YYYY HH24:mi:ss'))
OR (USERS.date_startUT BETWEEN to_date('01/10/2014 00:00:00','dd/MM/YYYY HH24:mi:ss') AND to_date('08/10/2014 23:59:00','dd/MM/YYYY HH24:mi:ss')))
GROUP BY USERS.UserID, code_etat
),
hours_list AS (
SELECT UserID, min_date, max_date, code_etat
, to_char(min_date + row_number() over (partition BY UserID ORDER BY 1)-1,'yyyymmdd') AS days
, to_char(min_date,'yyyymmdd') AS date_start
, to_char(min_date, 'hh24') || '-' || lpad(to_number(to_char(min_date, 'hh24')) + 1, 2, '00') AS timespan_date_start
, to_char(max_date,'yyyymmdd') AS date_end
, to_char(max_date, 'hh24') || '-' || lpad(to_number(to_char(max_date, 'hh24')) + 1, 2, '00') AS timespan_date_end
FROM UserID_min_max cmm
connect BY level <= trunc(max_date) - trunc(min_date)+1
AND PRIOR UserID = UserID
AND prior sys_guid() IS NOT NULL
),
all_timespan_hours_list AS (
SELECT lj.*, t.*, lj.days ||' '|| t.hours AS days_hours
FROM hours_list lj
JOIN timespan t
ON lj.days || t.hours >= lj.date_start || lj.timespan_date_start
AND lj.days || t.hours <= lj.date_end || lj.timespan_date_end
)
SELECT DISTINCT days_hours, COUNT(*)
FROM (
SELECT *
FROM all_timespan_hours_list ttlj
WHERE CODE_ETAT IN ('SOH','SOL')
)
GROUP BY days_hours
ORDER BY days_hours;
Here's how I would do something similar:
with dt_tab as (select trunc(:p_start_date, 'hh') + (level - 1)/24 hr
from dual
connect by level <= (trunc(:p_end_date, 'hh') - trunc(:p_start_date, 'hh'))*24 + 1),
sample_data as (select 4242 usr, to_date('21/05/2015 01:15:00', 'dd/mm/yyyy hh24:mi:ss') start_date, to_date('21/05/2015 05:22:00', 'dd/mm/yyyy hh24:mi:ss') end_date from dual union all
select 4243 usr, to_date('20/05/2015 18:32:42', 'dd/mm/yyyy hh24:mi:ss') start_date, to_date('21/05/2015 01:36:56', 'dd/mm/yyyy hh24:mi:ss') end_date from dual union all
select 4244 usr, to_date('21/05/2015 07:00:00', 'dd/mm/yyyy hh24:mi:ss') start_date, null end_date from dual)
select to_char(dt.hr, 'dd/mm/yyyy hh24-')||to_char(dt.hr + 1/24, 'hh24') date_and_time,
count(sd.usr) cnt
from dt_tab dt
left outer join sample_data sd on (dt.hr < nvl(sd.end_date, :p_end_date) and dt.hr >= sd.start_date)
group by to_char(dt.hr, 'dd/mm/yyyy hh24-')||to_char(dt.hr + 1/24, 'hh24')
order by date_and_time;
:p_start_date := 20/05/2015 08:00:00
:p_end_date := 21/05/2015 08:00:00
DATE_AND_TIME CNT
---------------- ---
20/05/2015 08-09 0
20/05/2015 09-10 0
20/05/2015 10-11 0
20/05/2015 11-12 0
20/05/2015 12-13 0
20/05/2015 13-14 0
20/05/2015 14-15 0
20/05/2015 15-16 0
20/05/2015 16-17 0
20/05/2015 17-18 0
20/05/2015 18-19 0
20/05/2015 19-20 1
20/05/2015 20-21 1
20/05/2015 21-22 1
20/05/2015 22-23 1
20/05/2015 23-00 1
21/05/2015 00-01 1
21/05/2015 01-02 1
21/05/2015 02-03 1
21/05/2015 03-04 1
21/05/2015 04-05 1
21/05/2015 05-06 1
21/05/2015 06-07 0
21/05/2015 07-08 1
21/05/2015 08-09 0
(depending on how your time period start and end dates are configured, you might want to change from using bind variables - eg. use the min/max dates in your table, etc)
The above works when I run it in Toad. For something that works in SQL*Plus, or when you run it as a script (e.g. in Toad), the below should work:
variable p_start_date varchar2(20)
variable p_end_date varchar2(20)
exec :p_start_date := '20/05/2015 08:00:00';
exec :p_end_date := '21/05/2015 08:00:00';
with dt_tab as (select trunc(to_date(:p_start_date, 'dd/mm/yyyy hh24:mi:ss'), 'hh') + (level - 1)/24 hr
from dual
connect by level <= (trunc(to_date(:p_end_date, 'dd/mm/yyyy hh24:mi:ss'), 'hh') - trunc(to_date(:p_start_date, 'dd/mm/yyyy hh24:mi:ss'), 'hh'))*24 + 1),
sample_data as (select 4242 usr, to_date('21/05/2015 01:15:00', 'dd/mm/yyyy hh24:mi:ss') start_date, to_date('21/05/2015 05:22:00', 'dd/mm/yyyy hh24:mi:ss') end_date from dual union all
select 4243 usr, to_date('20/05/2015 18:32:42', 'dd/mm/yyyy hh24:mi:ss') start_date, to_date('21/05/2015 01:36:56', 'dd/mm/yyyy hh24:mi:ss') end_date from dual union all
select 4244 usr, to_date('21/05/2015 07:00:00', 'dd/mm/yyyy hh24:mi:ss') start_date, null end_date from dual)
select to_char(dt.hr, 'dd/mm/yyyy hh24-')||to_char(dt.hr + 1/24, 'hh24') date_and_time,
count(sd.usr) cnt
from dt_tab dt
left outer join sample_data sd on (dt.hr < nvl(sd.end_date, to_date(:p_end_date, 'dd/mm/yyyy hh24:mi:ss')) and dt.hr >= sd.start_date)
group by to_char(dt.hr, 'dd/mm/yyyy hh24-')||to_char(dt.hr + 1/24, 'hh24')
order by date_and_time;
Try to use the function TRUNC(date,[fmt]) like this:
select trunc(some_date, 'HH24')
from some_table
group by trunc(some_date, 'HH24');
Try this:
SELECT
DATE_FORMAT(your_datetime_column, '%Y%m%d %H') AS `hourly`,
COUNT(*) AS `count`
FROM your_table
GROUP BY DATE_FORMAT(your_datetime_column, '%Y%m%d %H')

Selecting window of entries

Consider the following (simplyfied) table:
ID NUMBER
PROD_NO VARCHAR2(10)
START_TIME DATE
What I want to do is selecting a 'window' of rows of size n around a given START_TIME.
Example:
ID PROD_NO START_TIME
...
42 1234567 2012-02-28 13:42:10
43 1234568 2012-02-28 13:47:53
44 1234569 2012-02-28 13:52:22
45 1234570 2012-02-28 13:59:01
46 1234571 2012-02-28 14:02:12
47 1234572 2012-02-28 14:05:19
...
Provided START_TIME = '2012-02-28 14:00:00' and window size n = 4 the resulting set of rows should be ID 44...47.
The entries cannot be assumed to be sorted by START_TIME. In case there are not enough entries available to match the specified window size, it may be cropped.
Since my SQL skills are pretty limited any help would be greatly appreciated.
Thanks in advance.
You can use analytic functions to help with this:
select WT.ID
from (select WT.ID
,max(
START_TIME)
over (order by START_TIME
rows between 2 preceding and 2 following)
as MAXST
,min(
START_TIME)
over (order by START_TIME
rows between 2 preceding and 2 following)
as MINST
from WT) WT
where MINST < to_date('2012-02-28 14:00:00', 'yyyy-mm-dd hh24:mi:ss')
and MAXST > to_date('2012-02-28 14:00:00', 'yyyy-mm-dd hh24:mi:ss')
This should work now:
SELECT *
FROM (SELECT id,
prod_no,
start_time,
ROWNUM rn,
datediff
FROM (SELECT id,
prod_no,
start_time,
start_time
- TO_DATE('01-JAN-2011 12:00:00',
'DD-MON-YYYY HH:MI:SS AM')
datediff
FROM table
WHERE start_time
- TO_DATE('01-JAN-2011 12:00:00',
'DD-MON-YYYY HH:MI:SS AM') > 0
ORDER BY datediff))
WHERE rn <= 2
UNION ALL
SELECT *
FROM (SELECT id,
prod_no,
start_time,
ROWNUM rn,
datediff
FROM (SELECT id,
prod_no,
start_time,
start_time
- TO_DATE('01-JAN-2011 12:00:00',
'DD-MON-YYYY HH:MI:SS AM')
datediff
FROM table
WHERE start_time
- TO_DATE('01-JAN-2011 12:00:00',
'DD-MON-YYYY HH:MI:SS AM') <= 0
ORDER BY datediff DESC))
WHERE rn <= 2