检查连续多次经历相同状态的记录

Check records that have gone through the same status more than once in a row

我有状态历史记录 table,我需要知道哪个 id_user 按顺序经过相同的状态。

Table结构

create table user (
    id_user number,
    user_name number,
    status_name char(1),
    created_at timestamp,
    primary key (id_user)
);

create table user_status_hist (
    id_user_status_hist number,
    id_user number,
    status_name char(1),
    updated_at timestamp,
    primary key (id_user),
    constraint fk foreign key (id_user) references user(id_user)
);

假设在下面的示例中,对于用户 123,它已连续两次通过状态 B。 我怎样才能在我的 table 中找到所有这样的案例?

select id_user, status_name, updated_at
from user_status_history
where id_user = 123;

--------+-------------+------------+
id_user | status_name | updated_at |
--------+-------------+------------+
    123 |           A | 2020-11-01 |
--------+-------------+------------+
    123 |           B | 2020-11-02 |
--------+-------------+------------+
    123 |           B | 2020-11-05 |
--------+-------------+------------+

通过此查询,我发现我有一个用户多次通过相同状态的情况,但考虑到 updated_at 列,我看不出是否是连续的。

select count(*), idt_card
from user_status_hist
group by id_user, status_name
having count(*) > 1;

我怎样才能得到像下面这样的输出? (“计数”列是他依次经历这些状态的次数)

--------+-------------+------------+
id_user | status_name | count      |
--------+-------------+------------+
    123 |           A |          3 |
--------+-------------+------------+
    456 |           B |          2 |
--------+-------------+------------+
    789 |           B |          6 |
--------+-------------+------------+

您只需要在 select:

中包含您想要的列
select idt_card, status_name, count(*)
from user_status_hist
group by id_user, status_name
having count(*) > 1;

使用 LAG() 分析函数。由于您必须在比较中使用它,并且解析函数只能在 SELECT 子句中计算(在 之后 应用所有过滤器),您必须计算解析在子查询中运行并在外部查询中引用它。

select id_user, status_name, updated_at
from   ( 
         select id_user, status_name, updated_at, 
                lag(status_name) over (partition by id_user order by updated_at) 
                                                                  as prev_status
          from  user_status_hist
       )
where  status_name = prev_status
;

这将为您提供所有事件的完整详细信息。如果您随后想按 id_user 和 status_name 分组并计数,您已经知道该怎么做。 (你可以直接在上面显示的解决方案的外部查询中进行。)