我要在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。 -请不要因命名约定或如何避免此错误等而陷入设计问题,以上只是一般示例。
答案 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 BY
和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 )
结果:
| 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