通过窗口功能获得最频繁的值

时间:2020-01-25 08:04:47

标签: sql postgresql window-functions

我有一个看起来像这样的SQL表:

user_id role    date
1       1       2019-11-26 21:20:54.397+00
1       2       2019-11-27 22:46:28.923+00
2       1       2019-12-06 22:17:53.925+00
2       3       2019-12-13 00:12:28.006+00
3       1       2019-11-25 21:57:17.701+00
3       1       2019-12-06 20:48:28.314+00
3       1       2019-12-15 23:59:06.81+00
4       3       2019-12-04 15:26:10.639+00
4       3       2019-11-22 19:20:01.025+00
4       3       2019-11-25 12:38:53.169+00

我想根据过去的日期和使用来获得最频繁的职位。结果应如下所示:

user_id role    date                        most_frequent_role
1       1       2019-11-26 21:20:54.397+00  NULL
1       2       2019-11-27 22:46:28.923+00  1
2       1       2019-12-06 22:17:53.925+00  NULL
2       3       2019-12-13 00:12:28.006+00  1
3       1       2019-11-25 21:57:17.701+00  NULL
3       1       2019-12-06 20:48:28.314+00  1
3       1       2019-12-15 23:59:06.81+00   1
4       3       2019-12-04 15:26:10.639+00  NULL
4       3       2019-11-22 19:20:01.025+00  3
4       3       2019-11-25 12:38:53.169+00  3

3 个答案:

答案 0 :(得分:0)

以下查询将为您服务。

select test.user_id,test.role,test.role_date, 
case when test.role_date in 
(select min(role_date) from test group by user_id) then NULL 
else t.role end as MOST_FREQUENT_ROLE 
from 
(select user_id,min(role) as role from test group by user_id
)t 
join test on t.user_id=test.user_id
order by user_id,role_date

输出

USER_ID ROLE    ROLE_DATE   MOST_FREQUENT_ROLE
1         1     26-NOV-19    - 
1         2     27-NOV-19    1
2         1     06-DEC-19    - 
2         3     13-DEC-19    1
3         1     25-NOV-19    - 
3         1     06-DEC-19    1
3         1     15-DEC-19    1
4         3     22-NOV-19    - 
4         3     25-NOV-19    3
4         3     04-DEC-19    3   

答案 1 :(得分:0)

如果您确实想使用窗口功能,请尝试以下-

SELECT user_id
      ,role
      ,date
      ,CASE WHEN date = MIN(date) OVER(PARTITION BY user_id ORDER BY date)
                 THEN NULL
            ELSE MIN(role) OVER(PARTITION BY user_id) END MOST_FREQUENT_ROLE 
FROM YOUR_TABLE;

答案 2 :(得分:0)

从技术上讲,您要计算的是 mode (这是一个统计术语)。

Postgres具有内置的mode()功能。 las,它不能作为窗口功能正常工作,因此几乎没有帮助。

我建议使用横向连接:

select t.*, m.role
from t left join lateral 
     (select t2.role
      from t t2
      where t2.user_id = t.user_id and
            t2.date < t.date
      group by t2.role
      order by count(*) desc,
               max(date) desc  -- in the event of ties, use the most recent
      limit 1
     ) m
     on 1=1
order by user_id, date;

Here是db <>小提琴。请注意,我添加了一些行以给出运行模式更改位置的示例。

这并不是特别有效,但是在(user_id, date, role)上建立索引应该会有所帮助。

如果您只有少数几个角色,那么可能会有更有效的解决方案。如果是这种情况,并且性能存在问题,请提出一个 new 问题。