Oracle select data by contiguous date blocks - sql

I am using an Oracle database and I have a table that has 2 columns with data like so:
HASH | DATE
-----------------
abcd | 2017-11-01
abcd | 2017-11-02
abcd | 2017-11-03
wxyz | 2017-11-04
wxyz | 2017-11-05
abcd | 2017-11-06
wxyz | 2017-11-07
abcd | 2017-11-08
abcd | 2017-11-09
lmno | 2017-11-10
lmno | 2017-11-11
I want to know the windows of time that each hash is seen. So like
hash | start | end
------------------------------
abcd | 2017-11-01 | 2017-11-03
wxyz | 2017-11-04 | 2017-11-05
abcd | 2017-11-06 | 2017-11-06
wxyz | 2017-11-07 | 2017-11-07
abcd | 2017-11-08 | 2017-11-09
lmno | 2017-11-10 | 2017-11-11
What I have so far is basically this:
SELECT HASH, MIN(DATE) ST, MAX(DATE) ED
FROM HASH_TABLE
GROUP BY HASH
ORDER BY 3 DESC
And this almost works but it will give me like "abcd" as a start of 2017-11-01 and an end of 2017-11-09 which "hides" the fact that it switched in the middle.
Is there some way to group these results by contiguous date/time "blocks"?

It looks like "gaps and islands" problem:
WITH cte("hash","date") AS (
SELECT 'abcd', DATE'2017-11-01' FROM dual UNION ALL
SELECT 'abcd', DATE'2017-11-02' FROM dual UNION ALL
SELECT 'abcd', DATE'2017-11-03' FROM dual UNION ALL
SELECT 'wxyz', DATE'2017-11-04' FROM dual UNION ALL
SELECT 'wxyz', DATE'2017-11-05' FROM dual UNION ALL
SELECT 'abcd', DATE'2017-11-06' FROM dual UNION ALL
SELECT 'wxyz', DATE'2017-11-07' FROM dual UNION ALL
SELECT 'abcd', DATE'2017-11-08' FROM dual UNION ALL
SELECT 'abcd', DATE'2017-11-09' FROM dual UNION ALL
SELECT 'lmno', DATE'2017-11-10' FROM dual UNION ALL
SELECT 'lmno', DATE'2017-11-11' FROM dual
)
select "hash"
,min("date") as startdate
,max("date") as enddate
from (
select "date","hash"
, row_number() over (order by "date")
- row_number() over (partition by "hash" order by "date") as grp
from cte
) A
group by "hash", grp
ORDER BY startdate;
DBFiddle Demo

desc hash_table
hash varchar2(4)
date_ date
select hash, min(date_) start_, max(date_) end_
from
(
select h.hash, h.date_, row_number() over (partition by hash order by date_) rn
from hash_table h
)
group by hash, date_ - rn
order by 2;

Related

Can Not Group By on DATE from Timestamp

I am unable to group by on date from a timestamp column in below query:
CHG_TABLE
+----+--------+----------------+-----------------+-------+-----------+
| Key|Seq_Num | Start_Date | End_Date | Value |Record_Type|
+----+--------+----------------+-----------------+-------+-----------+
| 1 | 1 | 5/25/2019 2.05 | 12/31/9999 00.00| 800 | Insert |
| 1 | 1 | 5/25/2019 2.05 | 5/31/2019 11.12 | 800 | Update |
| 1 | 2 | 5/31/2019 11.12| 12/31/9999 00.00| 900 | Insert |
| 1 | 2 | 5/31/2019 11.12| 6/15/2019 12.05 | 900 | Update |
| 1 | 3 | 6/15/2019 12.05| 12/31/9999 00.00| 1000 | Insert |
| 1 | 3 | 6/15/2019 12.05| 6/25/2019 10.20 | 1000 | Update |
+---+---------+----------------+-----------------+-------+-----------+
RESULT:
+-----+------------------+----------------+-----------+----------+
| Key | Month_Start_Date | Month_End_Date |Begin_Value|End_Value |
+---- +------------------+----------------+-----------+----------+
| 1 | 6/1/2019 | 6/30/2019 | 1700 | 1000 |
| 1 | 7/1/2019 | 7/31/2019 | 1000 | 1000 |
+-----+------------------+----------------+-----------+----------+
Begin_Value : Sum(Value) for Max(Start_Date) < Month_Start_Date -> Should pick up latest date from last month
End_Value : Sum(Value) for Max(Start_Date) <= Month_End_Date -> Should pick up the latest date
SELECT k.key,
dd.month_start_date,
dd.month_end_date,
gendata.value first_value,
gendata.next_value last_value
FROM dim_date dd CROSS JOIN dim_person k
JOIN (SELECT ct.key,
dateadd('day',1,last_day(ct.start_date)) start_date ,
SUM(ct.value),
lead(SUM(ct.value)) OVER(ORDER BY ct.start_date) next_value
FROM (SELECT key,to_char(start_Date,'MM-YYYY') MMYYYY, max(start_Date) start_date
FROM CHG_TABLE
GROUP BY to_char(start_Date,'MM-YYYY'), key
) dt JOIN CHG_TABLE ct ON
dt.start_date = ct.start_date AND
dt.key = ct.key
group by ct.key, to_char(start_Date,'MM-YYYY')
) gendata ON
to_char(dd.month_end_date,'MM-YYYY') = to_char(to_char(start_Date,'MM-YYYY')) AND
k.key = gendata.key;
Error:
start_Date is not a valid group by expression
Related post:
Monthly Snapshot using Date Dimension
Hoping, I understood your question correctly.
You can check below query
WITH chg_table ( key, seq_num, start_date, end_date, value, record_type ) AS
(
SELECT 1,1,TO_DATE('5/25/2019 2.05','MM/DD/YYYY HH24.MI'),TO_DATE('12/31/9999 00.00','MM/DD/YYYY HH24.MI'), 800, 'Insert' FROM DUAL UNION ALL
SELECT 1,1,TO_DATE('5/25/2019 2.05','MM/DD/YYYY HH24.MI'),TO_DATE('5/31/2019 11.12','MM/DD/YYYY HH24.MI'), 800, 'Update' FROM DUAL UNION ALL
SELECT 1,2,TO_DATE('5/31/2019 11.12','MM/DD/YYYY HH24.MI'),TO_DATE('12/31/9999 00.00','MM/DD/YYYY HH24.MI'), 900, 'Insert' FROM DUAL UNION ALL
SELECT 1,2,TO_DATE('5/31/2019 11.12','MM/DD/YYYY HH24.MI'),TO_DATE('6/15/2019 12.05','MM/DD/YYYY HH24.MI'), 900, 'Update' FROM DUAL UNION ALL
SELECT 1,3,TO_DATE('6/15/2019 12.05','MM/DD/YYYY HH24.MI'),TO_DATE('12/31/9999 00.00','MM/DD/YYYY HH24.MI'), 1000, 'Insert' FROM DUAL UNION ALL
SELECT 1,3,TO_DATE('6/15/2019 12.05','MM/DD/YYYY HH24.MI'),TO_DATE('6/25/2019 10.20','MM/DD/YYYY HH24.MI'), 1000, 'Update' FROM DUAL
)
select key , new_start_date Month_Start_Date , new_end_date Month_End_Date , begin_value ,
nvl(lead(begin_value) over(order by new_start_date),begin_value) end_value
from
(
select key , new_start_date , new_end_date , sum(value) begin_value
from
(
select key, seq_num, start_date
, value, record_type ,
trunc(add_months(start_date,1),'month') new_start_date ,
trunc(add_months(start_date,2),'month')-1 new_end_date
from chg_table
where record_type = 'Insert'
)
group by key , new_start_date , new_end_date
)
order by new_start_date
;
Db Fiddle link: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=c77a71afa82769b48f424e1c0fa1c0b6
I am assuming that you are getting an "ORA-00979: not a GROUP BY expression" and this is due to your use of the TO_CHAR(timestamp_col,'DD-MM-YYYY') in the GROUP BY clause.
Adding the TO_CHAR(timestamp_col,'DD-MM-YYYY') to the select side of your statement should resolve this and provide the results you are expecting.
a, b, dateadd('day',1,last_day(timestamp_col)) start_date, TO_CHAR(timestamp_col,'DD-MM-YYYY'), ...```

How to aggregate count of rows having the same value in a sequence?

I have a query that returns data in the following sample:
SELECT timestamp, atm_id FROM TRANSACTIONS ORDER BY TIMESTAMP ASC;
Output
TIMESTAMP | ATM_ID |
--------------------
2010-01-01 | EP02 |
2010-01-01 | EP02 |
2010-01-28 | EP02 |
2010-02-07 | EP02 |
2010-02-09 | EP11 |
2010-03-19 | EP11 |
2010-03-19 | EP02 |
2010-04-03 | EP05 |
2010-04-30 | EP02 |
I know how to group by ATM_ID and put the count in-front of each
SELECT
ATM_ID,
COUNT(*) CNT
FROM
TRANSACTIONS
GROUP BY
ATM_ID;
Based on the sample data above, this will yield something like
ATM_ID | CNT
---------------
EP02 | 6
EP11 | 2
EP05 | 1
However, I am interested in grouping on a different level. If a certain ATM_ID is duplicated in consecutive rows, the number of rows having the same ATM_ID in sequence should be included in the output, even if the same ATM_ID appears later after a different ATM_ID
Desired Output
ATM_ID | CNT
---------------
EP02 | 4 --Four rows of ATM_ID EP02
EP11 | 2 --Followed by 2 rows of ATM_ID EP11
EP02 | 1 --Followed by 1 row of ATM_ID EP02
EP05 | 1 --Followed by 1 row of ATM_ID EP05
EP02 | 1 --Followed by 1 row of ATM_ID EP02
Ignore the comments on the right, these are just for clarifications, not part of the output.
Is that possible?
PS: The answer below by Syed Aladeen gives the output with the correct count, but with the wrong order. I create an SQL fiddle for convenience:
SQL Fiddle
Try this
select atm_id , count(*)
from (select TRANSACTIONS.*,
(row_number() over (order by id) -
row_number() over (partition by atm_id order by id)
) as grp
from TRANSACTIONS
) TRANSACTIONS
group by grp, atm_id order by max(id)
-- Oracle 12c+: pattern matching
with s(dt, atm_id) as (
select to_date('2010-01-01', 'yyyy-mm-dd'), 'EP02' from dual union all
select to_date('2010-01-01', 'yyyy-mm-dd'), 'EP02' from dual union all
select to_date('2010-01-28', 'yyyy-mm-dd'), 'EP02' from dual union all
select to_date('2010-02-07', 'yyyy-mm-dd'), 'EP02' from dual union all
select to_date('2010-02-09', 'yyyy-mm-dd'), 'EP11' from dual union all
select to_date('2010-03-19', 'yyyy-mm-dd'), 'EP11' from dual union all
select to_date('2010-03-19', 'yyyy-mm-dd'), 'EP02' from dual union all
select to_date('2010-04-03', 'yyyy-mm-dd'), 'EP05' from dual union all
select to_date('2010-04-30', 'yyyy-mm-dd'), 'EP02' from dual)
select *
from s
match_recognize (
order by dt
measures v.atm_id as atm_id,
count(v.atm_id) as cnt,
first(dt) as min_dt,
last (dt) as max_dt
pattern (v+)
define v as v.atm_id = first(atm_id)
);
ATM_ CNT MIN_DT MAX_DT
---- ---------- ------------------- -------------------
EP02 4 2010-01-01 00:00:00 2010-02-07 00:00:00
EP11 2 2010-02-09 00:00:00 2010-03-19 00:00:00
EP02 1 2010-03-19 00:00:00 2010-03-19 00:00:00
EP05 1 2010-04-03 00:00:00 2010-04-03 00:00:00
EP02 1 2010-04-30 00:00:00 2010-04-30 00:00:00
Elapsed: 00:00:00.01
-- Oracle 8i+: window sort + window buffer + group by [+ order by]
with s(dt, atm_id) as (
select to_date('2010-01-01', 'yyyy-mm-dd'), 'EP02' from dual union all
select to_date('2010-01-01', 'yyyy-mm-dd'), 'EP02' from dual union all
select to_date('2010-01-28', 'yyyy-mm-dd'), 'EP02' from dual union all
select to_date('2010-02-07', 'yyyy-mm-dd'), 'EP02' from dual union all
select to_date('2010-02-09', 'yyyy-mm-dd'), 'EP11' from dual union all
select to_date('2010-03-19', 'yyyy-mm-dd'), 'EP11' from dual union all
select to_date('2010-03-19', 'yyyy-mm-dd'), 'EP02' from dual union all
select to_date('2010-04-03', 'yyyy-mm-dd'), 'EP05' from dual union all
select to_date('2010-04-30', 'yyyy-mm-dd'), 'EP02' from dual)
select atm_id, count(*) cnt, min(dt) min_dt, max(dt) as max_dt
from
(select dt, atm_id, count(lg) over (order by dt) ct, lg
from
(select dt, atm_id, decode(atm_id, lag(atm_id) over (order by dt), null, 1) lg
from s
)
)
group by ct, atm_id
order by min_dt;
ATM_ CNT MIN_DT MAX_DT
---- ---------- ------------------- -------------------
EP02 4 2010-01-01 00:00:00 2010-02-07 00:00:00
EP11 1 2010-02-09 00:00:00 2010-02-09 00:00:00
EP02 1 2010-03-19 00:00:00 2010-03-19 00:00:00
EP11 1 2010-03-19 00:00:00 2010-03-19 00:00:00
EP05 1 2010-04-03 00:00:00 2010-04-03 00:00:00
EP02 1 2010-04-30 00:00:00 2010-04-30 00:00:00
6 rows selected.

Deriving continuous history from overlapping from/to periods, with gaps [duplicate]

I have a table with 200.000 rows in a SQL Server 2014 database looking like this:
CREATE TABLE DateRanges
(
Contract VARCHAR(8),
Sector VARCHAR(8),
StartDate DATE,
EndDate DATE
);
INSERT INTO DateRanges (Contract, Sector, StartDate, Enddate)
SELECT '111', '999', '01-01-2014', '03-31-2014'
union
SELECT '111', '999', '04-01-2014', '06-30-2014'
union
SELECT '111', '999', '07-01-2014', '09-30-2014'
union
SELECT '111', '999', '10-01-2014', '12-31-2014'
union
SELECT '111', '888', '08-01-2014', '08-31-2014'
union
SELECT '111', '777', '08-15-2014', '08-31-2014'
union
SELECT '222', '999', '01-01-2014', '03-31-2014'
union
SELECT '222', '999', '04-01-2014', '06-30-2014'
union
SELECT '222', '999', '07-01-2014', '09-30-2014'
union
SELECT '222', '999', '10-01-2014', '12-31-2014'
union
SELECT '222', '666', '11-01-2014', '11-30-2014'
UNION
SELECT '222', '555', '11-15-2014', '11-30-2014';
As you can see there can be multiple overlaps for each contract and what I would like to have is the result like this
Contract Sector StartDate EndDate
---------------------------------------------
111 999 01-01-2014 07-31-2014
111 888 08-01-2014 08-14-2014
111 777 08-15-2014 08-31-2014
111 999 09-01-2014 12-31-2014
222 999 01-01-2014 10-31-2014
222 666 11-01-2014 11-14-2014
222 555 11-15-2014 11-30-2014
222 999 12-01-2014 12-31-2014
I can not figure out how this can be done and the examples i have seen on this site quite do not fit my problem.
This answer makes use of a few different techniques. The first is a recursive-cte that creates a table with every relevant cal_date which then gets cross apply'd with unique Contract values to get every combination of both values. The second is window-functions such as lag and row_number to determine a variety of things detailed in the comments below. Lastly, and probably most importantly, gaps-and-islands to determine when one Contract/Sector combination ends and the next begins.
Answer:
--determine range of dates
declare #bgn_dt date = (select min(StartDate) from DateRanges)
, #end_dt date = (select max(EndDate) from DateRanges)
--use a recursive CTE to create a record for each day / Contract
; with dates as
(
select #bgn_dt as cal_date
union all
select dateadd(d, 1, a.cal_date) as cal_date
from dates as a
where a.cal_date < #end_dt
)
select d.cal_date
, c.Contract
into #contract_dates
from dates as d
cross apply (select distinct Contract from DateRanges) as c
option (maxrecursion 0)
--Final Select
select f.Contract
, f.Sector
, min(f.cal_date) as StartDate
, max(f.cal_date) as EndDate
from (
--Use the sum-over to obtain the Island Numbers
select dr.Contract
, dr.Sector
, dr.cal_date
, sum(dr.IslandBegin) over (partition by dr.Contract order by dr.cal_date asc) as IslandNbr
from (
--Determine if the record is the start of a new Island
select a.Contract
, a.Sector
, a.cal_date
, case when lag(a.Sector, 1, NULL) over (partition by a.Contract order by a.cal_date asc) = a.Sector then 0 else 1 end as IslandBegin
from (
--Determine which Contract/Date combinations are valid, and rank the Sectors that are in effect
select cd.cal_date
, dr.Contract
, dr.Sector
, dr.EndDate
, row_number() over (partition by dr.Contract, cd.cal_date order by dr.StartDate desc) as ConractSectorRnk
from #contract_dates as cd
left join DateRanges as dr on cd.Contract = dr.Contract
and cd.cal_date between dr.StartDate and dr.EndDate
) as a
where a.ConractSectorRnk = 1
and a.Contract is not null
) as dr
) as f
group by f.Contract
, f.Sector
, f.IslandNbr
order by f.Contract asc
, min(f.cal_date) asc
Output:
+----------+--------+------------+------------+
| Contract | Sector | StartDate | EndDate |
+----------+--------+------------+------------+
| 111 | 999 | 2014-01-01 | 2014-07-31 |
| 111 | 888 | 2014-08-01 | 2014-08-14 |
| 111 | 777 | 2014-08-15 | 2014-08-31 |
| 111 | 999 | 2014-09-01 | 2014-12-31 |
| 222 | 999 | 2014-01-01 | 2014-10-31 |
| 222 | 666 | 2014-11-01 | 2014-11-14 |
| 222 | 555 | 2014-11-15 | 2014-11-30 |
| 222 | 999 | 2014-12-01 | 2014-12-31 |
+----------+--------+------------+------------+

Oracle - Assigning the correct Date from a Set

I have a table A like below
REGID | PKG_DESC | EVENT_DATE | IS_CON | IS_REN
-----------------------------------------------------
1234 | cc | 27-MAR-14 | 0 | 0
1234 | cc | 27-JUN-14 | 1 | 0
1234 | GUI | 27-MAR-14 | 0 | 0
1234 | GUI | 27-JUN-14 | 1 | 0
1234 | GUI | 27-SEPT-14 | 0 | 1
1234 | GUI | 27-SEPT-15 | 0 | 1
1234 | REMOTE | 27-MAR-14 | 0 | 0
1234 | REMOTE | 27-JUN-14 | 1 | 0
1234 | REMOTE | 27-SEPT-14 | 0 | 1
2431 | cc | 27-MAR-14 | 0 | 0
2431 | cc | 27-JUN-14 | 1 | 0
I have a query like below
select a.reg_id, b.sess_start_dt,
case when TRUNC(A.EVENT_DATE) - B.SESS_START_DT BETWEEN 0-30 THEN 'DAYS 0_30'
WHEN TRUNC(A.EVENT_DATE) - B.SESS_START_DT BETWEEN 31-60 THEN 'DAYS 31-60'
from tab a inner join tab b on a.reg_id = b.reg_id and a.is_ren = 1
union
select a.reg_id, b.sess_start_dt,
case when TRUNC(A.EVENT_DATE) - B.SESS_START_DT BETWEEN 0-30 THEN 'DAYS 0_30'
WHEN TRUNC(A.EVENT_DATE) - B.SESS_START_DT BETWEEN 31-60 THEN 'DAYS 31-60'
from tab a inner join tab b on a.reg_id = b.reg_id and a.is_con = 1
Tab B contains all the usage for each reg_id there will be 100's of records.. Sample of few are
REGID | SESS_START_DT
1234 | 27-Jan-14
1234 | 20-MAR-12
1234 | 27-MAR-12
1234 | 01-sept-14
1234 | 07-sept-14
1234 | 29-JUL-14
1234 | 03-AUG-14
1234 | 27-MAR-13
1234 | 27-MAR-12
1234 | 27-MAR-12
1234 | 27-MAR-12
1234 | 27-MAR-12
1234 | 27-MAR-12
1234 | 27-MAR-12
2431 | 20-JUN-14
The Above query needs to be corrected in a way like,
1) If the REG_ID is having at least one is_ren = 1 then that subscription should be considered as renewal subscription and needs to get the 30 days and 60 days usage from table B from his is_ren = 1 event_date. (for REGID 1234 only is_ren query should execute)
2) If multiple IS_REN = 1 are existing for each REGID then the usage needs to be taken 30 days and 60 days from table B with the MIN(event_date). in this case the usage should be taken from 27-SEPT-14 instead of 27-SEPT-15
3) If there is no IS_REN = 1 and there is IS_CON = 1 then it's considered as conversion and usage should be taken before 60 days from the converted date (for REGID 2431, usage needs to get 60 days back from 27-JUN-14{this is my event_date in the query})
The O/P should be like
REGID | EVENT_DATE | DAYS 0_30 | DAYS 31-60 | CODE
1234 | 27-SEPT-14 | 2 | 2 | REN
2431 | 27-JUL-14 | 1 | 0 | CON
If my assumptions in my Comment are correct, this may be what you need. Notice the order by clause in row_number() - first the rows with is_ren = 1, then the rows with is_ren = 0 and is_con = 1, then all the other rows, and within each group order by event_date ascending. This way, the top row (rn = 1), which is the only one I use in the outer query, will have is_ren = 1 with the earliest possible date, or if no is_ren = 1 then the row with is_con = 1 and the earliest date, or else just the earliest date. (In the last case, the CODE will be null: this means there were no is_ren = 1 and no is_con = 1 for that regid.
Not sure why you have 27-JUL-14 in the output for regid = 2431, that should be 27-JUN-14. Also, there are no four-letter months in Oracle ("SEPT"). The output shows dates using my session parameters; if you need to format the dates, use to_date(event_date, .....) with the desired date format model. Also, since the data you provided is just dates (with no time-of-day component), I didn't truncate anything; you may need to, if your real data has time-of-day components.
with
table_a ( regid, pkg_desc, event_date, is_con, is_ren ) as (
select 1234, 'cc' , to_date ('27-MAR-14', 'dd-MON-rr'), 0, 0 from dual union all
select 1234, 'cc' , to_date ('27-JUN-14', 'dd-MON-rr'), 1, 0 from dual union all
select 1234, 'GUI' , to_date ('27-MAR-14', 'dd-MON-rr'), 0, 0 from dual union all
select 1234, 'GUI' , to_date ('27-JUN-14', 'dd-MON-rr'), 1, 0 from dual union all
select 1234, 'GUI' , to_date ('27-SEP-14', 'dd-MON-rr'), 0, 1 from dual union all
select 1234, 'GUI' , to_date ('27-SEP-15', 'dd-MON-rr'), 0, 1 from dual union all
select 1234, 'REMOTE', to_date ('27-MAR-14', 'dd-MON-rr'), 0, 0 from dual union all
select 1234, 'REMOTE', to_date ('27-JUN-14', 'dd-MON-rr'), 1, 0 from dual union all
select 1234, 'REMOTE', to_date ('27-SEP-14', 'dd-MON-rr'), 0, 1 from dual union all
select 2431, 'cc' , to_date ('27-MAR-14', 'dd-MON-rr'), 0, 0 from dual union all
select 2431, 'cc' , to_date ('27-JUN-14', 'dd-MON-rr'), 1, 0 from dual
),
table_b ( regid, sess_start_dt ) as (
select 1234, to_date ('27-JAN-14', 'dd-MON-rr') from dual union all
select 1234, to_date ('20-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('01-SEP-14', 'dd-MON-rr') from dual union all
select 1234, to_date ('07-SEP-14', 'dd-MON-rr') from dual union all
select 1234, to_date ('29-JUL-14', 'dd-MON-rr') from dual union all
select 1234, to_date ('03-AUG-14', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-13', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 2431, to_date ('20-JUN-14', 'dd-MON-rr') from dual
),
prep ( regid, event_date, code, rn ) as (
select regid, event_date,
case when is_ren = 1 then 'REN' when is_con = 1 then 'CON' else null end,
row_number() over (partition by regid
order by case when is_ren = 1 then 0
when is_con = 1 then 1 else 2 end,
event_date)
from table_a
)
select p.regid, p.event_date,
count(case when b.sess_start_dt between p.event_date - 30 and p.event_date
then 1 end) as days_0_30,
count(case when b.sess_start_dt between p.event_date - 60 and p.event_date - 31
then 1 end) as days_31_60,
p.code
from prep p inner join table_b b on p.regid = b.regid
where rn = 1
group by p.regid, p.event_date, p.code
;
Output:
REGID EVENT_DATE DAYS_0_30 DAYS_31_60 COD
---------- ------------------- ---------- ---------- ---
1234 2014-09-27 00:00:00 2 2 REN
2431 2014-06-27 00:00:00 1 0 CON

How can I select only newest entries?

I have table like this:
IST | FILEDATE | DATE | ...
1 | 2013-2014 | 27.03.2015 10:20:47 | ...
2 | 2013-2014 | 27.03.2015 10:20:47 | ...
3 | 2013-2014 | 27.03.2015 10:20:47 | ...
1 | 2013-2014 | 28.03.2015 11:20:47 | ...
2 | 2013-2014 | 28.03.2015 11:20:47 | ...
3 | 2013-2014 | 28.03.2015 11:20:47 | ...
1 | 2014-2015 | 29.03.2015 12:20:47 | ...
2 | 2014-2015 | 29.03.2015 12:20:47 | ...
3 | 2014-2015 | 29.03.2015 12:20:47 | ...
...
I need to select newest(with date value) entry of all IST, like this:
IST | FILEDATE | DATE | ...
1 | 2014-2015 | 29.03.2015 11:20:47 | ...
2 | 2014-2015 | 29.03.2015 11:20:47 | ...
3 | 2014-2015 | 29.03.2015 11:20:47 | ...
I tried order by and rownum=1, but its working for just single IST.
How can I do that? Thank you.
That's a typical scenario where analytical functions (aka windowing functions) are really helpful:
with v_data(ist, filedate, entry_date) as (
select 1, '2013-2014', to_date('27.03.2015 10:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 2, '2013-2014', to_date('27.03.2015 10:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 3, '2013-2014', to_date('27.03.2015 10:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 1, '2013-2014', to_date('28.03.2015 11:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 2, '2013-2014', to_date('28.03.2015 11:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 3, '2013-2014', to_date('28.03.2015 11:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 1, '2014-2015', to_date('29.03.2015 12:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 2, '2014-2015', to_date('29.03.2015 12:20:47','DD.MM.YYYY hh24:mi:ss') from dual union all
select 3, '2014-2015', to_date('29.03.2015 12:20:47','DD.MM.YYYY hh24:mi:ss') from dual)
select * from (
select
v1.*,
row_number() over (partition by ist order by entry_date desc) as rn
from v_data v1
)
where rn=1
This solution
computes an ordering per group using the ROW_NUMBER analytical function
removes everything but the newest entry per group with WHERE rn = 1
You can first group the result:
select ist, max(date) date
from table
group
by ist
Then you can combine that result with a select to get all matching lines:
select master.*
from table master
join
( select ist, max(date) date
from table
group
by ist
) filter
on master.ist = filter.ist
and master.date = filter.date
Use NOT EXISTS to find ist's that have no newer row in table:
select *
from tablename t1
where not exists (select 1 from tablename t2
where t2.ist = t1.ist
and t2.date > t1.date)*