SELECT query over an interval of 1 day and use analytic functions LEAD() - oracle

I have a table which contains timestamps (called triggers) per business day (CTRL_DT). I need to process data for a given business date by selecting rows between previous days trigger timestamp and current days trigger. By using LEAD(), I was able to get below output. This only works if there are triggers every day.
Let's say, one trigger is missed. How do I rewrite the same query without much complexity but get the expected output.
Input Data: (if you observe, ctrl_dt 2023-02-16 is missed).
CAPTURE_DT
CTRL_DT
INST
2023-02-17 19:21:30.612814
2023-02-18
AAA
2023-02-16 19:18:16.045126
2023-02-17
AAA
2023-02-14 18:58:40.927273
2023-02-15
AAA
2023-02-13 21:43:38.832417
2023-02-14
AAA
2023-02-12 18:30:40.595363
2023-02-13
AAA
Expected Output:
STARTTIME
ENDTIME
BS_DATE
2023-02-16 19:18:16.045126
2023-02-17 19:21:30.612814
2023-02-17
2023-02-16 19:18:16.045126
2023-02-16
2023-02-14 18:58:40.927273
2023-02-15
2023-02-13 21:43:38.832417
2023-02-14 18:58:40.927273
2023-02-14
2023-02-12 18:30:40.595363
2023-02-13 21:43:38.832417
2023-02-13
2023-02-12 18:30:40.595363
2023-02-12
However, I'm getting
STARTTIME
ENDTIME
BS_DATE
2023-02-16 19:18:16.045126
2023-02-17 19:21:30.612814
2023-02-17
2023-02-14 18:58:40.927273
2023-02-16 19:18:16.045126
2023-02-16
2023-02-13 21:43:38.832417
2023-02-14 18:58:40.927273
2023-02-14
2023-02-12 18:30:40.595363
2023-02-13 21:43:38.832417
2023-02-13
2023-02-12 18:30:40.595363
2023-02-12
Query Used:
WITH
EVENT_TRIGGER
AS
(SELECT '2023-02-17 19:21:30.612814' CAPTURE_DT, '2023-02-18' CTRL_DT, 'AAA' INST FROM DUAL
UNION ALL
SELECT '2023-02-16 19:18:16.045126' CAPTURE_DT, '2023-02-17' CTRL_DT, 'AAA' INST FROM DUAL
UNION ALL
SELECT '2023-02-14 18:58:40.927273' CAPTURE_DT, '2023-02-15' CTRL_DT, 'AAA' INST FROM DUAL
UNION ALL
SELECT '2023-02-13 21:43:38.832417' CAPTURE_DT, '2023-02-14' CTRL_DT, 'AAA' INST FROM DUAL
UNION ALL
SELECT '2023-02-12 18:30:40.595363' CAPTURE_DT, '2023-02-13' CTRL_DT, 'AAA' INST FROM DUAL)
SELECT LEAD (CAPTURE_DT) OVER (PARTITION BY INST ORDER BY CTRL_DT DESC) AS STARTTIME,
CAPTURE_DT AS ENDTIME,
TO_DATE (CTRL_DT, 'YYYY-MM-DD') - 1 AS BS_DATE
FROM EVENT_TRIGGER
WHERE INST = 'AAA';

You can generate a calendar and then use a PARTITIONed OUTER JOIN:
WITH EVENT_TRIGGER (CAPTURE_DT, CTRL_DT, INST ) AS (
SELECT TIMESTAMP '2023-02-17 19:21:30.612814', DATE '2023-02-18', 'AAA' FROM DUAL UNION ALL
SELECT TIMESTAMP '2023-02-16 19:18:16.045126', DATE '2023-02-17', 'AAA' FROM DUAL UNION ALL
SELECT TIMESTAMP '2023-02-14 18:58:40.927273', DATE '2023-02-15', 'AAA' FROM DUAL UNION ALL
SELECT TIMESTAMP '2023-02-13 21:43:38.832417', DATE '2023-02-14', 'AAA' FROM DUAL UNION ALL
SELECT TIMESTAMP '2023-02-12 18:30:40.595363', DATE '2023-02-13', 'AAA' FROM DUAL
),
calendar (day) AS (
SELECT min_dt + LEVEL - 1 AS day
FROM (
SELECT MIN(TRUNC(capture_dt)) AS min_dt,
MAX(TRUNC(capture_dt)) AS max_dt
FROM event_trigger
)
CONNECT BY min_dt + LEVEL - 1 <= max_dt
)
SELECT LAG(e.capture_dt) OVER (PARTITION BY e.inst ORDER BY c.day)
AS STARTTIME,
e.CAPTURE_DT AS ENDTIME,
c.day AS BS_DATE
FROM calendar c
LEFT OUTER JOIN EVENT_TRIGGER e
PARTITION BY (e.inst)
ON (
c.day <= e.capture_dt
AND e.capture_dt < c.day + 1
)
WHERE e.inst = 'AAA'
ORDER BY c.day DESC;
Which, outputs:
STARTTIME
ENDTIME
BS_DATE
2023-02-16 19:18:16.045126000
2023-02-17 19:21:30.612814000
2023-02-17 00:00:00
null
2023-02-16 19:18:16.045126000
2023-02-16 00:00:00
2023-02-14 18:58:40.927273000
null
2023-02-15 00:00:00
2023-02-13 21:43:38.832417000
2023-02-14 18:58:40.927273000
2023-02-14 00:00:00
2023-02-12 18:30:40.595363000
2023-02-13 21:43:38.832417000
2023-02-13 00:00:00
null
2023-02-12 18:30:40.595363000
2023-02-12 00:00:00
fiddle

Related

Closest date/time to top of hour

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

IF / CASE statement in Where in Oracle (OR)

I want to search for a rows between two dates. In each row there is a column with date. I want to decrease this date by 1 and always display the results with that column decreased by 1 day.
For example - I'm searching between 2021-07-08 00:00:00 and 2021-07-08 23:59:59 so I want to search for columns with date 2021-07-09 but display them as 2021-07-08.
The problem is that I want to exclude from that searching holidays and weekend. So for example if I will search between 2021-07-09 00:00:00 and 2021-07-09 23:59:59 then I want to search for columns with with date 2021-07-12 and display them as 2021-07-09.
For holidays I have a list:
with BANKHOLIDAYSUK as(
select COLUMN_VALUE as HOLIDAYDATE
from table(sys.odcivarchar2list (
TO_DATE('30/08/2021', 'DD/MM/YYYY')
,TO_DATE('27/12/2021', 'DD/MM/YYYY')
,TO_DATE('28/12/2021', 'DD/MM/YYYY')
,TO_DATE('01/01/2022', 'DD/MM/YYYY')
,TO_DATE('03/01/2022', 'DD/MM/YYYY')
,TO_DATE('15/04/2022', 'DD/MM/YYYY')
,TO_DATE('18/04/2022', 'DD/MM/YYYY')
,TO_DATE('02/05/2022', 'DD/MM/YYYY')
,TO_DATE('02/06/2022', 'DD/MM/YYYY')
,TO_DATE('03/06/2022', 'DD/MM/YYYY')
,TO_DATE('29/08/2022', 'DD/MM/YYYY')
,TO_DATE('26/12/2022', 'DD/MM/YYYY')
,TO_DATE('27/12/2022', 'DD/MM/YYYY')
,TO_DATE('01/01/2023', 'DD/MM/YYYY')
,TO_DATE('02/01/2023', 'DD/MM/YYYY')
,TO_DATE('07/04/2023', 'DD/MM/YYYY')
,TO_DATE('10/04/2023', 'DD/MM/YYYY')
,TO_DATE('01/05/2023', 'DD/MM/YYYY')
,TO_DATE('29/05/2023', 'DD/MM/YYYY')
,TO_DATE('28/08/2023', 'DD/MM/YYYY')
,TO_DATE('25/12/2023', 'DD/MM/YYYY')
,TO_DATE('26/12/2023', 'DD/MM/YYYY')
,TO_DATE('09/07/2021', 'DD/MM/YYYY')))
)
How to check in where clause that we have :start or :end date into that list or weekend.
I've tried with:
where
to_date(to_char(from_tz( cast( (o.DUEDATEUTC - 1) as timestamp ), 'UTC' ) at time zone to_char(l.oracletimezone ), 'YYYY-MM-DD HH24:MI:SS'), 'YYYY-MM-DD HH24:MI:SS') between :startDate and :endDate
OR (
(SELECT * FROM BANKHOLIDAYSUK WHERE HOLIDAYDATE = TO_DATE(:startdate, 'DD/MM/YYYY')) is not null
and to_date(to_char(from_tz( cast( (o.DUEDATEUTC - 1) as timestamp ), 'UTC' ) at time zone to_char(l.oracletimezone ), 'YYYY-MM-DD HH24:MI:SS'), 'YYYY-MM-DD HH24:MI:SS') between :startDate and :endDate
)
OR
(
((SELECT to_char(:startDate, 'd') FROM DUAL) = 5)
and to_date(to_char(from_tz( cast( (o.DUEDATEUTC - 3) as timestamp ), 'UTC' ) at time zone to_char(l.oracletimezone ), 'YYYY-MM-DD HH24:MI:SS'), 'YYYY-MM-DD HH24:MI:SS') between :startDate and :endDate
)
But it seems like executing of the query goes forever...
l.oracletimezone is an column with timezone for different locations.
Of course I'm using decreasing also in select.
Without OR statements it works but as I said only between monday and thursday. If we select between friday date then we will get nothing cause there is no 'DUEDATE' until weekend days.
Is my logic is wrong here?
Example:
id
name
duedate
1
Electricity bill
2021-07-08
2
Water bill
2021-07-09
3
Rent bill
2021-07-12
Search between 2021-07-07 00:00:00 and 2021-07-07 23:59:59
Result:
id
name
duedate
1
Electricity bill
2021-07-07
Search between 2021-07-08 00:00:00 and 2021-07-08 23:59:59
Result:
id
name
duedate
1
Water bill
2021-07-08
Search between 2021-07-09 00:00:00 and 2021-07-09 23:59:59
Result:
id
name
duedate
1
Rent bill
2021-07-09
Search between 2021-07-07 00:00:00 and 2021-07-09 23:59:59
Result:
id
name
duedate
1
Electricity bill
2021-07-07
2
Water bill
2021-07-08
3
Rent bill
2021-07-09
You can create the function:
CREATE FUNCTION next_working_day(
day IN DATE
) RETURN DATE
IS
working_day DATE;
BEGIN
working_day := day + CASE TRUNC(day) - TRUNC(day, 'IW')
WHEN 5 THEN 2 -- Saturday
WHEN 6 THEN 1 -- Sunday
ELSE 0 -- Weekday
END;
WITH non_holiday_date ( day, skip ) AS (
SELECT working_day,
NVL2(
b.holidaydate,
CASE TRUNC(working_day) - TRUNC(working_day, 'IW')
WHEN 4 THEN 3 -- Friday
ELSE 1 -- Any other weekday
END,
0
)
FROM DUAL d
LEFT OUTER JOIN bankholidaysuk b
ON (TRUNC(working_day) = b.holidaydate)
UNION ALL
SELECT day + skip,
NVL2(
b.holidaydate,
CASE TRUNC(day) - TRUNC(day, 'IW')
WHEN 4 THEN 3 -- Friday
ELSE 1 -- Any other weekday
END,
0
)
FROM non_holiday_date n
LEFT OUTER JOIN bankholidaysuk b
ON (TRUNC(day) + skip = b.holidaydate)
WHERE n.skip > 0
)
SELECT day
INTO working_day
FROM non_holiday_date
WHERE skip = 0;
RETURN working_day;
END;
/
Then, if you have the sample data:
CREATE TABLE your_table (id, name, duedateutc) AS
SELECT 1, 'Electricity bill', DATE '2021-08-20' FROM DUAL UNION ALL
SELECT 2, 'Water bill', DATE '2021-08-23' FROM DUAL UNION ALL
SELECT 3, 'Rent bill', DATE '2021-08-31' FROM DUAL UNION ALL
SELECT 4, 'XYZ bill', DATE '2021-12-29' FROM DUAL;
CREATE TABLE BANKHOLIDAYSUK ( holidaydate ) as
SELECT DATE '2021-08-30' FROM DUAL UNION ALL
SELECT DATE '2021-12-27' FROM DUAL UNION ALL
SELECT DATE '2021-12-28' FROM DUAL;
Then:
SELECT *
FROM your_table o
WHERE o.duedateutc BETWEEN next_working_day( DATE '2021-08-19' + 1 )
AND next_working_day( DATE '2021-08-19' + INTERVAL '23:59:59' HOUR TO SECOND + 1 )
Gets the bill due on the next day and outputs:
ID
NAME
DUEDATEUTC
1
Electricity bill
2021-08-20 00:00:00
and:
SELECT *
FROM your_table o
WHERE o.duedateutc BETWEEN next_working_day( DATE '2021-08-20' + 1 )
AND next_working_day( DATE '2021-08-20' + INTERVAL '23:59:59' HOUR TO SECOND + 1 )
Skips the weekend and gets the bill on the next Monday and outputs:
ID
NAME
DUEDATEUTC
2
Water bill
2021-08-23 00:00:00
and:
SELECT *
FROM your_table o
WHERE o.duedateutc BETWEEN next_working_day( DATE '2021-08-27' + 1 )
AND next_working_day( DATE '2021-08-27' + INTERVAL '23:59:59' HOUR TO SECOND + 1 )
Skips the weekend and the Monday holiday and gets the bill on the next Tuesday and outputs:
ID
NAME
DUEDATEUTC
3
Rent bill
2021-08-31 00:00:00
and:
SELECT *
FROM your_table o
WHERE o.duedateutc BETWEEN next_working_day( DATE '2021-08-19' + 1 )
AND next_working_day( DATE '2021-08-27' + INTERVAL '23:59:59' HOUR TO SECOND + 1 )
Gets all the previous bills, outputting:
ID
NAME
DUEDATEUTC
1
Electricity bill
2021-08-20 00:00:00
2
Water bill
2021-08-23 00:00:00
3
Rent bill
2021-08-31 00:00:00
and:
SELECT *
FROM your_table o
WHERE o.duedateutc BETWEEN next_working_day( DATE '2021-12-24' + 1 )
AND next_working_day( DATE '2021-12-24' + INTERVAL '23:59:59' HOUR TO SECOND + 1 )
Skips the weekend and the 2-day Christmas holiday and gets the bill on the next Wednesday, outputting:
ID
NAME
DUEDATEUTC
4
XYZ bill
2021-12-29 00:00:00
db<>fiddle here

get particular day between a data range from giving table

I read many articles which introduce the using of connect by, but all of them get the particular days from a giving parameter(almost the begin date and end date)
What I want to know is how could I get split the rows from a certain table?
Example
Table T1
StartDate EndDate T_ID
2017-06-01 2017-06-15 01
2017-06-05 2017-06-06 02
The result I want is
TargetDate T_ID
2017-06-01 01
2017-06-02 01
2017-06-03 01
2017-06-04 01
2017-06-05 01
.
.
.
.
2017-06-15 01
2017-06-06 01
2017-06-06 02
I tried
SELECT T_ID, T1.StartDate+ LEVEL - 1 DD, LEVEL
FROM T1
WHERE T1.T_ID in = '01'
CONNECT BY LEVEL <= (TO_DATE(TRUNC(T1.EndDate)) - T1.StartDate + 1 ) ;
Waiting for your solution. Thanks.
Test Data:
CREATE TABLE t1 ( t_id, startdate, enddate ) AS
SELECT 1, DATE '2017-06-01', DATE '2017-06-15' FROM DUAL UNION ALL
SELECT 2, DATE '2017-06-05', DATE '2017-06-06' FROM DUAL;
Query:
SELECT T_ID,
COLUMN_VALUE AS dt,
ROW_NUMBER() OVER ( PARTITION BY t1.ROWID
ORDER BY Column_value ) AS lvl
FROM T1
CROSS JOIN
TABLE(
CAST(
MUTLTSET(
SELECT t1.Startdate + LEVEL - 1
FROM DUAL
CONNECT BY t1.Startdate + LEVEL - 1 <= t1.EndDate
) AS SYS.ODCIDATELIST
)
);
Output:
T_ID DT LVL
---- ---------- ---
1 2017-06-01 1
1 2017-06-02 2
1 2017-06-03 3
1 2017-06-04 4
1 2017-06-05 5
1 2017-06-06 6
1 2017-06-07 7
1 2017-06-08 8
1 2017-06-09 9
1 2017-06-10 10
1 2017-06-11 11
1 2017-06-12 12
1 2017-06-13 13
1 2017-06-14 14
1 2017-06-15 15
2 2017-06-05 1
2 2017-06-06 2
Here is the query in standard SQL (with a recursive cte) which also works in Oracle:
with all_dates(targetdate, t_id, enddate) as
(
select startdate as targetdate, t_id, enddate from t1
union all
select targetdate + 1, t_id, enddate from all_dates where targetdate < enddate
)
select targetdate, t_id
from all_dates
order by t_id, targetdate;
SELECT DISTINCT T_ID
, T1.StartDate+ LEVEL - 1 DD
, LEVEL
FROM T1
WHERE T1.T_ID IN( 1,2)
CONNECT BY LEVEL <= T1.EndDate - T1.StartDate + 1
But I'm not sure about performances (At moment I didn't find a way to limit without DISTINCT but using CONNECT BY clauses).
As an alternative you can use a CTE like this (you can remove RN column, I left it as a check):
with all_dates(targetdate, t_id, enddate, RN) as
(
select startdate as targetdate, t_id, enddate, 1 AS RN from t1
union all
select T1.startdate + all_dates.RN, T1.t_id, T1.enddate, all_dates.RN+1 AS RN
from t1
inner JOIN all_dates ON T1.startdate+all_dates.RN<=all_dates.enddate
AND T1.T_ID = all_dates.T_ID
)
select targetdate, t_id , RN
from all_dates
order by t_id, targetdate;
Sample data:
CREATE TABLE T1 (StartDate DATE, EndDate DATE, T_ID NUMBER(10,0));
INSERT INTO T1 VALUES ('20170601','20170615', 1);
INSERT INTO T1 VALUES ('20170605','20170606', 2);
INSERT INTO T1 VALUES ('20170701','20170703', 3);
Output:
20170601 1 1
20170602 1 2
20170603 1 3
20170604 1 4
20170605 1 5
20170606 1 6
20170607 1 7
20170608 1 8
20170609 1 9
20170610 1 10
20170611 1 11
20170612 1 12
20170613 1 13
20170614 1 14
20170615 1 15
20170605 2 1
20170606 2 2
20170701 3 1
20170702 3 2
20170703 3 3
If you're wanting to use connect by to achieve this, you will need to add a couple of additional clauses in order to get it to work with multiple rows:
WITH t1 AS (SELECT to_date('01/06/2017', 'dd/mm/yyyy') startdate, to_date('15/06/2017', 'dd/mm/yyyy') enddate, 1 t_id FROM dual UNION ALL
SELECT to_date('05/06/2017', 'dd/mm/yyyy') startdate, to_date('06/06/2017', 'dd/mm/yyyy') enddate, 2 t_id FROM dual)
SELECT t_id,
startdate + LEVEL -1 dd
FROM t1
CONNECT BY LEVEL <= enddate - startdate + 1
AND PRIOR t_id = t_id
AND PRIOR sys_guid() IS NOT NULL
ORDER BY t_id, dd;
T_ID DD
---------- -----------
1 01/06/2017
1 02/06/2017
1 03/06/2017
1 04/06/2017
1 05/06/2017
1 06/06/2017
1 07/06/2017
1 08/06/2017
1 09/06/2017
1 10/06/2017
1 11/06/2017
1 12/06/2017
1 13/06/2017
1 14/06/2017
1 15/06/2017
2 05/06/2017
2 06/06/2017

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 sql last cancel record

I have below data:
Order
order_id order_name order_date order_status
1 iphone 20130102 13:20:00 cancelled
1 blackberry 20130102 13:00:00 cancelled
1 ipad 20130102 13:00:00 cancelled
Person
person_id person_name order_id
1 harshini 1
I want to retrieve the below data when i query based on order_date between 20130102 13:00:00 to 2013 13:20:00.It means last cancel order.
person_name order_name order_date
harshini blackberry 20130102 13:00:00
just try with this...select p.person_name ,o.order_name,o.order_status from order_1 o,person p where orderdate=(select max(orderdate) from order_1)
select person_name, order_name, order_date
from(
select
o.order_id, o.order_name, o.order_date, o.order_status,
p.person_id, p.person_name, p.order_id,
row_number() over (/*partition by person_id*/ order by order_date desc) as rnk
from order o join person p on (o.order_id = p.order_id)
where o.order_status = 'canceled'
and o.order_date between
to_date('20130102 13:00:00','yyyymmdd hh24:mi:ss') and
to_date('20130102 13:00:00','yyyymmdd hh24:mi:ss')
)
where rnk = 1;
But see the comments, you have some issues in your design.

Resources