条件超前/滞后函数PostgreSQL?

z31licg0  于 2023-08-04  发布在  PostgreSQL
关注(0)|答案(2)|浏览(127)

我有一张这样的table:

Name   activity  time

user1  A1        12:00
user1  E3        12:01
user1  A2        12:02
user2  A1        10:05
user2  A2        10:06
user2  A3        10:07
user2  M6        10:07
user2  B1        10:08
user3  A1        14:15
user3  B2        14:20
user3  D1        14:25
user3  D2        14:30

字符串
现在,我需要一个这样的结果:

Name   activity  next_activity

user1  A2        NULL
user2  A3        B1
user3  A1        B2


我想为每个用户检查A组的最后一个活动以及B组接下来发生的活动类型(B组的活动总是在A组的活动之后发生)。其他类型的活动对我来说不感兴趣。我尝试使用lead()函数,但它不起作用。
我如何解决我的问题?

ercv8c1e

ercv8c1e1#

您的定义:
B组的活动总是在A组的活动之后进行。
..逻辑上意味着在1个或多个A活动之后,每个用户有0个或1个B活动。序列中的活动不得超过B。
你可以使用一个窗口函数DISTINCT ONCASE,这应该是每个用户 * 几 * 行的最快方法(也见下文):

SELECT name
     , CASE WHEN a2 LIKE 'B%' THEN a1 ELSE a2 END AS activity
     , CASE WHEN a2 LIKE 'B%' THEN a2 END AS next_activity
FROM  (
   SELECT DISTINCT ON (name)
          name
        , lead(activity) OVER (PARTITION BY name ORDER BY time DESC) AS a1
        , activity AS a2
   FROM   t
   WHERE (activity LIKE 'A%' OR activity LIKE 'B%')
   ORDER  BY name, time DESC
   ) sub;

字符串

如果没有添加ELSE分支,则SQL CASE表达式默认为NULL,因此我保持简短。
假设time定义为NOT NULL。否则,您可能需要添加NULLS LAST。为什么?为什么?

  • 按列ASC排序,但首先是NULL值?

(activity LIKE 'A%' OR activity LIKE 'B%')activity ~ '^[AB]'更详细,但在旧版本的Postgres中通常更快。关于模式匹配:

条件窗口函数?

这实际上是可能的。您可以将聚合FILTER子句与窗口函数的OVER子句结合使用。然而

  1. FILTER子句本身只能处理当前行中的值。
    1.更重要的是,FILTER并不是为像lead()lag()(最高Postgres 13)这样的纯正版函数实现的-只为aggregate functions实现。
    如果您尝试:
lead(activity) FILTER (WHERE activity LIKE 'A%') OVER () AS activity


Postgres会告诉你:

FILTER is not implemented for non-aggregate window functions


关于FILTER

  • 使用其他(不同)筛选器聚合列
  • 在窗口函数的FILTER子句中引用当前行

性能

对于***few***用户,每个用户有***few***行,即使没有索引,几乎任何查询都是快速的。
对于***多***个用户和每个用户***少***行,上面的第一个查询应该是最快的。请参阅:

  • 是否选择每个GROUP BY组中的第一行?

对于每个用户 * 许多 * 行,有(可能 * 很多 *)更快的技术,这取决于您的设置细节。请参阅:

  • 优化GROUP BY查询以检索每个用户的最新行
s71maibg

s71maibg2#

select      distinct on(name) name,activity,next_activity

from       (select name,activity,time
                  ,lead(activity) over (partition by name order by time) as next_activity

            from   t

            where  left(activity,1) in ('A','B')
            ) t

where       left(activity,1) = 'A'

order by    name,time desc

字符串

相关问题