比较不同记录之间的两个日期

时间:2019-01-17 08:42:22

标签: sql database oracle

我要在Oracle DB上构建的SQL查询有问题。 我需要找出在某些记录之前先创建一些记录的情况。 听起来很简单,但是我遇到了问题,我不知道为什么。

有一个包含用户详细事件的表,称为“ USER_EVENTS”。 我们发现了一个与激活用户之前删除用户有关的错误。 我想让所有拥有此bug的用户,所以如果我看表,我会看到这样的东西:

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

尝试了各种查询,我似乎无法匹配这两个查询,我知道这很愚蠢,我深表歉意。

看着上面的表格,我想获取在ACTIVATED事件之前发生DELETED事件的那些包裹的USERID。 从图像中,我应该得到的回报是“ 5302”,它在2019年5月1日删除,但在2019年1月1日激活。

谢谢!

P.S。 -请不要因命名约定或如何避免此错误等而陷入设计问题,以上只是一般示例。

8 个答案:

答案 0 :(得分:3)

您可以使用COUNT分析函数,该函数不需要您对表执行自联接。

Oracle设置

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;

查询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';

结果

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

查询2

如果只希望受影响的USER_ID,则可以使用GROUP BYHAVING

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 )

结果

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

db <>提琴here

答案 1 :(得分:1)

对于一次性练习(并假设基表不是太大),一个简单的解决方案是子查询。选择所有删除的用户,然后找到事件日期较晚的任何匹配的激活记录:

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
/

如其他答案所示,有许多方法可以编写以上内容。另一个简单的解决方案是自连接:

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
/

答案 2 :(得分:1)

您可以通过使用联接和子查询尝试以下操作

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

答案 3 :(得分:1)

只需在激活前寻找删除内容即可

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

答案 4 :(得分:1)

您可以在由user_id连接的两行行和日期之间使用内部连接

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' 

答案 5 :(得分:0)

我无法确定这是否是黑客,只是为了好玩:

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

答案 6 :(得分:0)

为什么不使用强大的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

答案 7 :(得分:0)

感谢大家的帮助!你们太棒了!! 我从这篇文章的第一个答案中得到了第二个查询:

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 )

像魅力一样工作,谢谢@MTO