条件超前/滞后功能PostgreSQL?

时间:2017-01-22 22:40:05

标签: sql postgresql greatest-n-per-group window-functions

我有一张这样的表:

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()函数,但它没有用。

我如何解决我的问题?

2 个答案:

答案 0 :(得分:8)

测试设置:

CREATE TEMP TABLE t (name text, activity text, time time);
INSERT INTO t values
 ('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');

您的定义:

  来自B组的

活动总是在A组活动之后进行。

..逻辑上暗示在一个或多个A活动之后,每个用户有0或1个B活动。按顺序进行的活动不得超过1个。

您可以使用单个窗口函数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;

如果没有添加CASE分支,则SQL NULL表达式默认为ELSE,所以我保持简短。

还假设time定义为NOT NULL。否则,您可能想要添加NULLS LAST。为什么呢?

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

条件窗函数?

实际上可能。您可以将聚合FILTER子句与窗口函数的OVER子句组合在一起。的然而

  1. FILTER子句本身只能用于当前行的值。

  2. 更重要的是,FILTER并未针对Postgres 9.6中的lead()lag()等纯窗口函数实现 - 仅适用于aggregate functions

  3. 如果您尝试:

    lead(activity) FILTER (WHERE activity LIKE 'A%') OVER () AS activity
    

    Postgres会告诉你:

    FILTER is not implemented for non-aggregate window functions
    

    关于FILTER

    性能

    (对于 少数 用户,每个用户 少数 行,几乎任何即使没有索引,查询也很快。)

    对于 许多 用户以及每个用户的 少数 行,上面的第一个查询应该是最快的。请参阅上面有关索引和效果的linked answer

    对于每个用户的 许多 行,有(可能很多)更快的技术,具体取决于您的设置的其他详细信息:

答案 1 :(得分:0)

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