如何根据事件日志表在一天结束时获取每个状态的用户总数?

Posted

技术标签:

【中文标题】如何根据事件日志表在一天结束时获取每个状态的用户总数?【英文标题】:How to get total number of users in each status at End of Day based on event log table? 【发布时间】:2019-03-06 14:14:16 【问题描述】:

我有一个事件日志表,它记录了所有用户的状态变化,比如状态 A、状态 B 和状态 C。他们可以随时更改它。如何获取每个结束日(从事件日志表中最早的一天到最后一天)处于每种状态的用户数量的快照

如果有人能以优雅的方式向我展示如何通过 PostsgreSQL 进行操作,不胜感激。谢谢!

编辑:事件日志表捕获每个用户的一堆事件(其中一个是状态更改),log_id 记录了该特定用户的事件日志的顺序。

 user_id  |     log_time     | status | event_A | log_id |
----------------------------------------------------------
   456    | 2019-01-05 15:00 |   C    |         |   5    |
   123    | 2019-01-05 14:00 |   C    |         |   4    |
   123    | 2019-01-05 13:00 |        |   xxx   |   3    |
   456    | 2019-01-04 22:00 |   B    |         |   4    |
   456    | 2019-01-04 10:00 |   C    |   xxx   |   3    |
   987    | 2019-01-04 05:00 |   C    |         |   3    |
   123    | 2019-01-03 23:00 |   B    |         |   2    |
   987    | 2019-01-03 15:00 |        |   xxx   |   2    |
   456    | 2019-01-02 22:00 |   A    |   xxx   |   2    |
   123    | 2019-01-01 23:00 |   C    |         |   1    |
   456    | 2019-01-01 09:00 |   B    |   xxx   |   1    |
   987    | 2019-01-01 04:00 |   A    |         |   1    |

所以我想在一天结束时获取每个状态的用户总数:

   Date    | status A | status B | status C |
---------------------------------------------
2019-01-05 |     0    |     0    |     3    |
2019-01-04 |     0    |     2    |     1    |
2019-01-03 |     2    |     1    |     0    |
2019-01-02 |     2    |     0    |     1    |
2019-01-01 |     1    |     1    |     1    |

【问题讨论】:

假设 Status 是唯一可以更新的列,添加一个“更新触发器”,在某处将计数器标志增加 1 可能有助于解决这个问题。或者,如果您需要状态更改的人员列表,请在主表中添加一个标志列,在更新状态时更新其值,然后返回标志与默认值不同的人员列表 请提供一些样本数据,让您的问题更清楚。 刚刚添加了一个示例@TimBiegeleisen。干杯! 【参考方案1】:

这是一个安静的挑战:)。我试图分割子查询以获得良好的可读性。这可能不是一种非常有效的方式来做你想做的事,但它确实可以完成工作。

-- collect all days to make sure there are no missing days
WITH all_days_cte(dt) as (
    SELECT
        generate_series(
        (SELECT min(date_trunc('day', log_time)) from your_table),
        (SELECT max(date_trunc('day', log_time)) from your_table),
        '1 day'
        )::DATE
),
-- collect all useres
all_users_cte as (
    select distinct
        user_id
    from your_table
),
-- setup the table with infos needed, i.e. only the last status by day and user_id
infos_to_aggregate_cte as (
    select
        s.user_id,
        s.dt,
        s.status
    from (
        select
            user_id,
            date_trunc('day', log_time)::DATE as dt,
            status,
            row_number() over (partition by user_id, date_trunc('day', log_time) order by log_time desc) rn
        from your_table
        where status is not null
    ) s
-- only the last status of the day
    where s.rn = 1
),
-- now we still have a problem, we need to find the last status, if there was no change on a day
completed_infos_cte as (
    select
        u.user_id,
        d.dt,
        -- not very efficient, but found no other way (first_value(...) would be nice, but there is no simple way to exclude nulls
        (select
            status
        from infos_to_aggregate_cte i2 
        where i2.user_id = u.user_id 
             and i2.dt <= d.dt 
             and i2.status is not null 
        order by i2.dt desc 
        limit 1) status
    from all_days_cte d
    -- cross product for all dates and users (that is what we need for our aggregation)
    cross join all_users_cte u
    left outer join infos_to_aggregate_cte i on u.user_id = i.user_id
        and d.dt = i.dt
)
select
    c.dt,
    sum(case when status = 'A' then 1 else 0 end) status_a,
    sum(case when status = 'B' then 1 else 0 end) status_b,
    sum(case when status = 'C' then 1 else 0 end) status_c    
from completed_infos_cte c
group by c.dt
order by c.dt desc

【讨论】:

以上是关于如何根据事件日志表在一天结束时获取每个状态的用户总数?的主要内容,如果未能解决你的问题,请参考以下文章

如何配置 Tomcat JULI 日志记录以滚动日志文件?

如何从每 2 分钟存储的状态日志中确定事件的开始/结束时间

获取每个名称的总成本总和

在一天的开始和结束时选择并计算条目数

为每个员工选择一天中最早时间戳的完整记录[重复]

在事件日志中获取特定任务的开始结束时间