oracle group by date with specific time - oracle

some will say "another question from that guy" but here is my Problem. all this works as designed:
with tab1 as (
select to_timestamp( '04.02.15 14:25:21.503000000' ) as dt from dual union all
select to_timestamp( '04.02.15 14:25:25.154000000' ) as dt from dual union all
select to_timestamp( '09.02.15 22:20:36.861000000' ) as dt from dual union all
select to_timestamp( '09.02.15 22:20:36.883000000' ) as dt from dual union all
select to_timestamp( '10.02.15 04:19:13.839000000' ) as dt from dual union all
select to_timestamp( '10.02.15 04:13:18.142000000' ) as dt from dual union all
select to_timestamp( '10.02.15 12:43:18.171000000' ) as dt from dual union all
select to_timestamp( '11.02.15 04:30:53.654000000' ) as dt from dual union all
select to_timestamp( '11.02.15 22:00:38.951000000' ) as dt from dual union all
select to_timestamp( '11.02.15 22:00:42.014000000' ) as dt from dual union all
select to_timestamp( '16.02.15 08:50:43.967000000' ) as dt from dual union all
select to_timestamp( '16.02.15 16:35:41.387000000' ) as dt from dual union all
select to_timestamp( '16.02.15 16:35:42.835000000' ) as dt from dual union all
select to_timestamp( '17.02.15 04:21:08.542000000' ) as dt from dual union all
select to_timestamp( '17.02.15 04:21:08.912000000' ) as dt from dual union all
select to_timestamp( '17.02.15 04:06:09.818000000' ) as dt from dual union all
select to_timestamp( '17.02.15 04:40:39.411000000' ) as dt from dual union all
select to_timestamp( '18.02.15 04:41:08.218000000' ) as dt from dual union all
select to_timestamp( '18.02.15 03:20:40.609000000' ) as dt from dual union all
select to_timestamp( '18.02.15 01:20:40.712000000' ) as dt from dual union all
select to_timestamp( '20.02.15 06:55:42.185000000' ) as dt from dual union all
select to_timestamp( '20.02.15 12:55:42.364000000' ) as dt from dual union all
select to_timestamp( '20.02.15 12:55:42.518000000' ) as dt from dual union all
select to_timestamp( '20.02.15 12:55:43.874000000' ) as dt from dual union all
select to_timestamp( '20.02.15 14:16:05.080000000' ) as dt from dual union all
select to_timestamp( '20.02.15 18:14:17.630000000' ) as dt from dual union all
select to_timestamp( '22.02.15 21:25:40.683000000' ) as dt from dual union all
select to_timestamp( '22.02.15 21:25:42.046000000' ) as dt from dual union all
select to_timestamp( '23.02.15 12:43:27.246000000' ) as dt from dual
order by dt
),
tab2 as(
select trunc(dt) as leaddate, dt,
case
when dt between (to_timestamp(trunc(dt)) + interval '04:30' hour to minute) and (to_timestamp(trunc(dt)) + interval '28:29' hour to minute) then (dt)
else (dt) - interval '04:30' hour to minute
end as newBaseTime
from tab1
)
select trunc(newBaseTime),
sum(case when ( dt <= to_timestamp(trunc( trunc(dt)),'dd.MM.yy') + interval '17:30' hour to minute) then 1 else 0 end) as beforeTS,
sum(case when ( dt > to_timestamp(trunc( trunc(dt)),'dd.MM.yy') + interval '17:30' hour to minute) then 1 else 0 end) as afterTS
from tab2
group by trunc(newBaseTime)
order by trunc(newBaseTime)
the idea is to Group by days with a "new time base" and check if Dates are before or after a defined daytime. due to contracts days in our company lasts from 4.30a.m. this day to 4.30. next day. my solution above works (with little data), but i guess there is an easier way to get result. any idea?

Not sure exactly what you are trying to do, but you seem stuck on this question... perhaps this is the solution you are looking for?
select dt, trunc(dt - interval '270' minute) as leaddate from tab1
This will preserve the timestamp (showing perhaps "today's" date) but if the time is before 4:30 am, the leaddate will be "yesterday's" date.
If this is NOT what you were looking for, please try to clarify your question.

Related

oracle how to generate a list of period from a list of date

i would like to write a query to transform a list of date
list of date
15/02/2021
12/04/2021
28/07/2021
31/08/2021
to a list of period
start
end
15/02/2021
11/04/2021
12/04/2021
27/07/2021
28/07/2021
31/08/2021
Is it possible to do it in a oracle query ? Thanks for your help
Variant 1:
select *
from (
select
dt as dt_start,
case when lead(dt,2)over(order by dt) is not null
then lead(dt)over(order by dt)-1
else lead(dt)over(order by dt)
end as dt_end
from t
)
where dt_end is not null;
Full test with test data:
alter session set nls_date_format='dd/mm/yyyy';
with t(dt) as (
select to_date('15/02/2021','dd/mm/yyyy') from dual union all
select to_date('12/04/2021','dd/mm/yyyy') from dual union all
select to_date('28/07/2021','dd/mm/yyyy') from dual union all
select to_date('31/08/2021','dd/mm/yyyy') from dual
)
select *
from (
select
dt as dt_start,
case when lead(dt,2)over(order by dt) is not null
then lead(dt)over(order by dt)-1
else lead(dt)over(order by dt)
end as dt_end
from t
)
where dt_end is not null;
DT_START DT_END
---------- ----------
15/02/2021 11/04/2021
12/04/2021 27/07/2021
28/07/2021 31/08/2021
Variant 2: match_recognize (Oracle 12+):
select
dt_start,dt_end
from t
match_recognize(
order by dt
MEASURES
prev(dt) as dt_start,
nvl2(next(dt),e.dt-1,e.dt) as dt_end
all rows per match
PATTERN (e+)
DEFINE
e AS dt > prev(dt)
);
Full example with test data:
with t(dt) as (
select to_date('15/02/2021','dd/mm/yyyy') from dual union all
select to_date('12/04/2021','dd/mm/yyyy') from dual union all
select to_date('28/07/2021','dd/mm/yyyy') from dual union all
select to_date('31/08/2021','dd/mm/yyyy') from dual
)
select
dt_start,dt_end
from t
match_recognize(
order by dt
MEASURES
prev(dt) as dt_start,
nvl2(next(dt),e.dt-1,e.dt) as dt_end
all rows per match
PATTERN (e+)
DEFINE
e AS dt > prev(dt)
);
DT_START DT_END
---------- ----------
15/02/2021 11/04/2021
12/04/2021 27/07/2021
28/07/2021 31/08/2021
DBFiddle: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=a9f9e256762c87e67ce3783f51f8d7c8
That's exactly what I'm looking for !
thanks !
I've just changed few things because I'm in Oracle 10g probably
with t as (
select to_date('15/02/2021','dd/mm/yyyy') dt from dual union all
select to_date('12/04/2021','dd/mm/yyyy') from dual union all
select to_date('28/07/2021','dd/mm/yyyy') from dual union all
select to_date('31/08/2021','dd/mm/yyyy') from dual
)
select *
from (
select
dt as dt_start,
case when lead(dt,2)over(order by dt) is not null
then lead(dt)over(order by dt)-1
else lead(dt)over(order by dt)
end as dt_end
from t
)
where dt_end is not null;

Oracle: Is it possible to filter out duplicates only when they appear in succession?

Thank you in advance for your help.
I have a table that holds itinerary information for drivers. There will be times when the itinerary seems to have the same stop (but is several days apart). I'd like to be able to query the table and filter out any record where the address is the same AND the dates are consecutive.
Is this possible?
Thanks again,
josh
with tst as(
select timestamp '2020-08-01 00:00:00' dt, '123 street' loc from dual
union all
select timestamp '2020-08-01 00:00:00', '89 street' from dual
union all
select timestamp '2020-08-02 00:00:00', '456 airport' from dual
union all
select timestamp '2020-08-04 00:00:00', '456 airport' from dual
union all
select timestamp '2020-08-05 00:00:00', '67 street' from dual
union all
select timestamp '2020-08-06 00:00:00', '89 street' from dual
union all
select timestamp '2020-08-07 00:00:00', '123 street' from dual
)
select dt, loc
from (
select dt, loc, nvl(lag(loc) over(order by dt), 'FIRST_ROW') prev_loc
from tst
) where loc <> prev_loc;
fiddle
Another approach would be to use Tabibitosan method which assign consecutive rows a group number and then count number of rows per group.(found in asktom website).
with test_data as(
select date'2020-08-01' dt, '123 street' loc from dual
union all
select date '2020-08-01', '89 street' from dual
union all
select date '2020-08-02', '456 airport' from dual
union all
select date '2020-08-04', '456 airport' from dual
union all
select date '2020-08-05', '67 street' from dual
union all
select date '2020-08-06', '89 street' from dual
union all
select date '2020-08-07', '123 street' from dual
)
select max(dt),loc
from
(
select t.*
,row_number() over (order by dt) -
row_number() over (partition by loc order by dt) grp
from test_data t
)
group by grp,loc
having count(*) > 1;
Another approach using match_recognize available from 12c onwards.patter used {1,} says repeated one or more times
more to learn match_recognize here
with test_data as(
select date'2020-08-01' dt, '123 street' loc from dual
union all
select date '2020-08-01', '89 street' from dual
union all
select date '2020-08-02', '456 airport' from dual
union all
select date '2020-08-04', '456 airport' from dual
union all
select date '2020-08-05', '67 street' from dual
union all
select date '2020-08-06', '89 street' from dual
union all
select date '2020-08-07', '123 street' from dual
)
select *
from test_data
match_recognize (
order by dt
all rows per match
pattern (equal{1,})
define
equal as loc = prev(loc)
);
Playground: Dbfiddle

calculate the running total over the column contain date difference in HH:MI:SS format in oracle

I have to find the running total over the column interval.
SELECT
( ( EXTRACT(DAY FROM intrvl) * 24 ) + ( EXTRACT(HOUR FROM intrvl) ) ) ||':'||
EXTRACT(MINUTE FROM intrvl) ||':'||
EXTRACT(SECOND FROM intrvl) ||':'|| as interval
FROM
(
SELECT
( to_timestamp(TO_CHAR(date_column_name,'dd-mon-rrrr hh:mi:ss') ) ) - ( to_timestamp(TO_CHAR(date_column_name,'dd-mon-rrrr hh:mi:ss') ) ) intrvl
FROM
dual
);
currrently Interval column of table has below data:
Interval(HH:mi:ss)
0:4:23
696:1:36
696:4:51
8760:1:18
The best I can come up with is this. Note that the interval data type does not take a format model for displaying - you can't force an interval of 25 hours to be displayed as 25:00:00 (although you can use that to INPUT an interval). Instead, it will be shown as 01 01:00:00 (meaning, a day and an hour).
with
tbl (interv) as (
select interval '0:4:23' hour(9) to second from dual union all
select interval '696:1:36' hour(9) to second from dual union all
select interval '696:4:51' hour(9) to second from dual union all
select interval '8760:1:18' hour(9) to second from dual
)
select interval '1' day * sum(date '2000-01-01' + interv - date'2000-01-01')
as sum_interv
from tbl;
SUM_INTERV
--------------------
+423 00:12:08.000000
In your original attempt you were trying to get a STRING output. I am not sure that's wise, but if that's what you need you can do it like so:
with
tbl (interv) as (
select interval '0:4:23' hour(9) to second from dual union all
select interval '696:1:36' hour(9) to second from dual union all
select interval '696:4:51' hour(9) to second from dual union all
select interval '8760:1:18' hour(9) to second from dual
)
, prep (sum_interv) as (
select interval '1' day * sum(date '2000-01-01' + interv - date'2000-01-01')
from tbl
)
select to_char( extract(day from sum_interv) * 24
+ extract(hour from sum_interv), 'fm999999999' ) || ':' ||
to_char( extract(minute from sum_interv), 'fm00' ) || ':' ||
to_char( extract(second from sum_interv), 'fm00' ) as sum_interv
from prep
;
SUM_INTERV
------------------
10152:12:08

Determine start of data's most recent uninterrupted 'streak' by date

I have a dataset that looks something like:
asset_id,date_logged
1234,2018-02-01
1234,2018-02-02
1234,2018-02-03
1234,2018-02-04
1234,2018-02-05
1234,2018-02-06
1234,2018-02-07
1234,2018-02-08
1234,2018-02-09
1234,2018-02-10
9876,2018-02-01
9876,2018-02-02
9876,2018-02-03
9876,2018-02-07
9876,2018-02-08
9876,2018-02-09
9876,2018-02-10
For the purpose of this exercise, imagine today's date is 2018-02-10 (10 Feb 2018). For all the asset_ids in the table, I am trying to identify the start of the most recent unbroken streak for date_logged.
For asset_id = 1234, this would be 2018-02-01. The asset_id was logged all 10 days in an unbroken streak. For asset_id = 9876, this would be 2018-02-07. Because the asset_id was not logged on 2018-02-04, 2018-02-05, and 2018-02-06, the most recent unbroken streak starts on 2018-02-07.
So, my result set would hopefully look something like:
asset_id,Number_of_days_in_most_recent_logging_streak
1234,10
9876,4
Or, alternatively:
asset_id,Date_Begin_Most_Recent_Streak
1234,2018-02-01
9876,2018-02-07
I haven't been able to work out anything that gets me close -- my best effort so far is to get the number of days since the first log date and today, and the number of days the asset_id appears in the dataset, and compare these to identify situations where the streak is more recent than the first day they appear. For my real dataset this isn't particularly problematic, but it's an ugly solution and I would like to understand a better way of getting to the outcome.
Perhaps something like this. Break the query after each inline view in the WITH clause and SELECT * FROM the most recent inline view, to see what each step does.
with
inputs ( asset_id, date_logged ) as (
select 1234, to_date('2018-02-01', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-02', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-03', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-04', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-05', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-06', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-07', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-08', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-09', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-10', 'yyyy-mm-dd') from dual union all
select 9876, to_date('2018-02-01', 'yyyy-mm-dd') from dual union all
select 9876, to_date('2018-02-02', 'yyyy-mm-dd') from dual union all
select 9876, to_date('2018-02-03', 'yyyy-mm-dd') from dual union all
select 9876, to_date('2018-02-07', 'yyyy-mm-dd') from dual union all
select 9876, to_date('2018-02-08', 'yyyy-mm-dd') from dual union all
select 9876, to_date('2018-02-09', 'yyyy-mm-dd') from dual union all
select 9876, to_date('2018-02-10', 'yyyy-mm-dd') from dual
),
prep ( asset_id, date_logged, grp ) as (
select asset_id, date_logged,
date_logged - row_number()
over (partition by asset_id order by date_logged)
from inputs
),
agg ( asset_id, date_logged, cnt ) as (
select asset_id, min(date_logged), count(*)
from prep
group by asset_id, grp
)
select asset_id, max(date_logged) as date_start_recent_streak,
max(cnt) keep (dense_rank last order by date_logged) as cnt
from agg
group by asset_id
order by asset_id -- If needed
;
ASSET_ID DATE_START_RECENT_STREAK CNT
---------- ------------------------ ----------
1234 2018-02-01 10
9876 2018-02-07 4
you can try this,
with test (asset_id, date_logged) as
(select 1234, date '2018-02-01' from dual union all
select 1234, date '2018-02-02' from dual union all
select 1234, date '2018-02-03' from dual union all
select 1234, date '2018-02-04' from dual union all
select 1234, date '2018-02-05' from dual union all
select 1234, date '2018-02-06' from dual union all
select 1234, date '2018-02-07' from dual union all
select 1234, date '2018-02-08' from dual union all
select 1234, date '2018-02-09' from dual union all
select 1234, date '2018-02-10' from dual union all
select 9876, date '2018-02-01' from dual union all
select 9876, date '2018-02-02' from dual union all
select 9876, date '2018-02-03' from dual union all
select 9876, date '2018-02-07' from dual union all
select 9876, date '2018-02-08' from dual union all
select 9876, date '2018-02-09' from dual union all
select 9876, date '2018-02-10' from dual union all
select 9876, date '2018-02-11' from dual union all
select 9876, date '2018-02-12' from dual
)
SELECT asset_id, MIN(date_logged), COUNT(1)
FROM (SELECT asset_id, date_logged,
MAX(date_logged) OVER (PARTITION BY asset_id)+1 max_date_logged_plus_one,
DENSE_RANK() OVER (PARTITION BY asset_id ORDER BY date_logged desc) rown
FROM test
ORDER BY asset_id, date_logged desc)
WHERE max_date_logged_plus_one - date_logged = rown
GROUP BY asset_id;
ASSET_ID MIN(DATE_LOGGED) COUNT(1)
---------- ---------------- ----------
1234 01-FEB-18 10
9876 07-FEB-18 6
if below data is commented, output is
select 9876, date '2018-02-10' from dual union all
ASSET_ID MIN(DATE_LOGGED) COUNT(1)
---------- ---------------- ----------
1234 01-FEB-18 10
9876 11-FEB-18 2
Would this make any sense?
SQL> with test (asset_id, date_logged) as
2 (select 1234, date '2018-02-01' from dual union all
3 select 1234, date '2018-02-02' from dual union all
4 select 1234, date '2018-02-03' from dual union all
5 select 1234, date '2018-02-04' from dual union all
6 select 1234, date '2018-02-05' from dual union all
7 select 1234, date '2018-02-06' from dual union all
8 select 1234, date '2018-02-07' from dual union all
9 select 1234, date '2018-02-08' from dual union all
10 select 1234, date '2018-02-09' from dual union all
11 select 1234, date '2018-02-10' from dual union all
12 select 9876, date '2018-02-01' from dual union all
13 select 9876, date '2018-02-02' from dual union all
14 select 9876, date '2018-02-03' from dual union all
15 select 9876, date '2018-02-07' from dual union all
16 select 9876, date '2018-02-08' from dual union all
17 select 9876, date '2018-02-09' from dual union all
18 select 9876, date '2018-02-10' from dual
19 ),
20 inter as
21 -- difference between DATE_LOGGED and its previous DATE_LOGGED
22 (select asset_id,
23 date_logged,
24 date_logged - lag(date_logged) over (partition by asset_id order by date_logged) diff
25 from test
26 )
27 select i.asset_id, min(i.date_logged) date_logged
28 from inter i
29 where nvl(i.diff, 1) = (select max(i1.diff) from inter i1
30 where i1.asset_id = i.asset_id
31 )
32 group by i.asset_id
33 order by i.asset_id;
ASSET_ID DATE_LOGGE
---------- ----------
1234 2018-02-01
9876 2018-02-07
SQL>

oracle query to retrieve closest and second closest future date based on the current date

I want to retrieve the closest and second closest future date based on the current date.
example :
current-date=28-07-2017
dates to be retrieve
28-07-2017
29-07-2017
or followed top two dates which are closest to current date.
plzz help me out in writing this query in oracle
Try this:
select sysdate,sysdate + level "Dates" From DUAL connect by level <= 1 ;
You can do this by using the dense_rank analytic function, like so:
WITH sample_data AS (SELECT 1 ID, to_date('01/07/2017', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 2 ID, to_date('02/07/2017', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 3 ID, to_date('02/07/2017', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 4 ID, to_date('03/07/2017', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 5 ID, to_date('04/07/2017', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 6 ID, to_date('05/07/2017', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 7 ID, to_date('06/07/2017', 'dd/mm/yyyy') dt FROM dual)
SELECT ID, dt
FROM (SELECT ID,
dt,
dense_rank() OVER (ORDER BY dt) dr
FROM sample_data
WHERE dt >= to_date('01/07/2017', 'dd/mm/yyyy'))
WHERE dr <= 2;
ID DT
---------- -----------
1 01/07/2017
2 02/07/2017
3 02/07/2017

Resources