Dmoy
Dmoy

Reputation: 57

compare two dates between different records

I have an issue with an SQL Query I'm trying to build on Oracle DB. I need to find out cases where some records have been created before other records. it sounds simple, but i'm having issues with it, i cant tell why.

there's a table that contains user detailed events, called "USER_EVENTS". we found a certain bug that involves deletion of users, before they were activated. I want to get all the Users that had this bug, so if i look at the table i'll see something like this:

TABLE USER_EVENTS

ID  EVENT_TYPE      EVENT_DATE  USER_ID
1   USER_DELETED    10/1/2019   5301
2   USER_ACTIVATED  9/1/2019    5301
3   USER_DELETED    5/1/2019    5302
4   USER_ACTIVATED  11/1/2019   5302
5   USER_DELETED    1/1/2019    5288
6   USER_DELETED    2/1/2019    5287
7   USER_CREATED    1/12/2018   5211
8   USER_NOTE       1/12/2018   5211

tried various queries i cant seem to match those two, i know its silly, i apologize.

looking at the table above, i want to get the USERIDs of those parcels that had the DELETED event before the ACTIVATED event. from the image, what i should get in return is "5302", which was deleted on the 5/1/2019 but activated on the 11/1/2019.

Thanks in advance!

P.S. - please dont get into a design issue with naming conventions or how to not have this bug etc', the above is just a general example.

Upvotes: 2

Views: 78

Answers (8)

Dmoy
Dmoy

Reputation: 57

thanks everyone for helping out! you guys are amazing!! i went with the 2nd Query from the first answer to this post:

SELECT USER_ID
FROM   USER_EVENTS
GROUP BY USER_ID
HAVING MIN( CASE EVENT_TYPE WHEN 'USER_DELETED' THEN EVENT_DATE END )
         < MIN( CASE EVENT_TYPE WHEN 'USER_ACTIVATED' THEN EVENT_DATE END )

works like a charm, thanks @MTO

Upvotes: 0

q4za4
q4za4

Reputation: 652

Why not use the mighty LEAD/LAG?

SELECT * FROM (
SELECT id,
       event_type,
       event_date,
       user_id,
       CASE
         WHEN event_type = 'USER_DELETED' THEN LEAD(EVENT_DATE) OVER (PARTITION BY useR_id ORDER BY event_type DESC) 
         ELSE NULL
       END  AS CREATED
FROM   user_events
WHERE event_type IN ('USER_ACTIVATED','USER_DELETED')
)
WHERE created > event_date

Upvotes: 0

William Robertson
William Robertson

Reputation: 16001

I can’t decide if this is a hack, but just for fun:

select user_id
from   user_events
group by user_id
having listagg(event_type, ',') within group (order by event_date) like '%DELETED,%ACTIVATED%';

Upvotes: 0

MT0
MT0

Reputation: 167982

You can use the COUNT analytic function which will not require you to perform a self-join on your table.

Oracle Setup:

CREATE TABLE USER_EVENTS ( ID, EVENT_TYPE, EVENT_DATE, USER_ID ) AS
SELECT 1, 'USER_DELETED',    DATE '2019-01-10', 5301 FROM DUAL UNION ALL
SELECT 2, 'USER_ACTIVATED',  DATE '2019-01-09', 5301 FROM DUAL UNION ALL
SELECT 3, 'USER_DELETED',    DATE '2019-01-05', 5302 FROM DUAL UNION ALL
SELECT 4, 'USER_ACTIVATED',  DATE '2019-01-11', 5302 FROM DUAL UNION ALL
SELECT 5, 'USER_DELETED',    DATE '2019-01-01', 5288 FROM DUAL UNION ALL
SELECT 6, 'USER_DELETED',    DATE '2019-01-02', 5287 FROM DUAL UNION ALL
SELECT 7, 'USER_CREATED',    DATE '2018-12-01', 5211 FROM DUAL UNION ALL
SELECT 8, 'USER_NOTE',       DATE '2018-12-01', 5211 FROM DUAL;

Query 1:

SELECT *
FROM   (
  SELECT u.*,
         COUNT( CASE event_type WHEN 'USER_ACTIVATED' THEN 1 END )
           OVER (
             PARTITION BY user_id
             ORDER BY event_date
             ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING
           ) AS num_activated
  FROM   USER_EVENTS u
)
WHERE  num_activated > 0
AND    event_type = 'USER_DELETED';

Results:

ID | EVENT_TYPE   | EVENT_DATE | USER_ID | NUM_ACTIVATED
-: | :----------- | :--------- | ------: | ------------:
 3 | USER_DELETED | 05-JAN-19  |    5302 |             1

Query 2:

If you just want the affected USER_IDs then you can use GROUP BY and HAVING:

SELECT USER_ID
FROM   USER_EVENTS
GROUP BY USER_ID
HAVING MIN( CASE EVENT_TYPE WHEN 'USER_DELETED' THEN EVENT_DATE END )
         < MIN( CASE EVENT_TYPE WHEN 'USER_ACTIVATED' THEN EVENT_DATE END )

Results:

| USER_ID |
| ------: |
|    5302 |

db<>fiddle here

Upvotes: 3

APC
APC

Reputation: 146239

For a one-off exercise (and assuming the base table is not too huge) a simple solution is a sub-query. Select all the deleted users then find any matching activated records with a later event date:

with del as ( 
 select user_id
        , event_date as date_deleted
 from user_events
 where event_type = 'USER_DELETED'
)
select del.user_id
      , del.date_deleted
      , act.event_date as date_activated
from del
     join user_events act
          on act.user_id = del.user_id
where act.event_type = 'USER_ACTIVATED'
and del.date_deleted < act.event_date
order by del.user_id
/

As the other answers show, there are many ways to write the above. Another simple solution is the self-join:

select del.user_id
      , del.date_deleted
      , act.event_date as date_activated
from user_events del
     join user_events act
          on act.user_id = del.user_id
where del.event_type = 'USER_DELETED'
and act.event_type = 'USER_ACTIVATED'
and del.date_deleted < act.event_date
order by del.user_id
/

Upvotes: 1

ScaisEdge
ScaisEdge

Reputation: 133370

You could use an inner join between the tow set of row joined by user_id and date

select  user_id, event_date 
from USER_EVENTS  U
inner join  
(
select  user_id, event_date 
from USER_EVENTS 
where EVENT_TYPE ='USER_DELETED'
) T ON T.event_date < U.event_date 
    AND T.user_id= U.user_id  

WHERE EVENT_TYPE ='USER_ACTIVATED' 

Upvotes: 1

dani herrera
dani herrera

Reputation: 51665

Just look for deletions before activations:

With activations as
( select * from t where EVENT_TYPE="USER_ACTIVATED"
),
deletions as
( select * from t where EVENT_TYPE="USER_DELETED"
),
select *
from deletions d
left outer join activations a
on d.USER_ID = a.USER_ID and
   d.EVENT_DATE < a.EVENT_DATE   --here

Upvotes: 1

Zaynul Abadin Tuhin
Zaynul Abadin Tuhin

Reputation: 31993

you can try like below by using join and subquery

select t1.USER_ID from 
(
select USER_ID,max(EVENT_DATE) as EVENT_DATE from 
USER_EVENTS where EVENT_TYPE='USER_ACTIVATED'
group by USER_ID
) t1 
join
select * from 
(
select USER_ID,max(EVENT_DATE) as EVENT_DATE from 
USER_EVENTS where EVENT_TYPE='USER_DELETED'
 group by USER_ID
) t2 on t1.=t2.USER_ID and t2.EVENT_DATE>t1.EVENT_DATE

Upvotes: 1

Related Questions