【问题标题】:Count equal, consecutive values in an ordered rowset在有序行集中计算相等的连续值
【发布时间】:2017-05-15 02:01:25
【问题描述】:

我有一个包含两列的表格,例如:

CREATE TABLE actions (
  action_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  "action" text NOT NULL
);

以及其中的以下数据:

        action_time         | action 
----------------------------+--------
 2016-12-30 14:12:33.353269 | a
 2016-12-30 14:12:38.536818 | b
 2016-12-30 14:12:43.305001 | a
 2016-12-30 14:12:49.432981 | a
 2016-12-30 14:12:53.536397 | b
 2016-12-30 14:12:57.449101 | b
 2016-12-30 14:13:01.592785 | a
 2016-12-30 14:13:06.192907 | b
 2016-12-30 14:13:11.249181 | b
 2016-12-30 14:13:13.690897 | b
(10 rows)

您可以假设 action_time 列中没有重复值。

我如何计算从上一个操作开始连续执行的相同操作的数量?

连续相同动作的数量没有限制,任何动作都可以是最后一个。此外,对不同动作的种类没有限制:我只使用了两个来简化示例数据。

对于这个示例数据,我希望结果为 3。这是因为最后一个操作是“b”,并且连续发生了 3 次。

我认为结合窗口函数和WITH RECURSIVE子句可以实现解决方案,但我不知道该怎么做。

【问题讨论】:

    标签: sql postgresql window-functions gaps-and-islands postgresql-9.6


    【解决方案1】:

    我为经典的间隙和孤岛解决方案添加了一些变化。
    请注意 ROW_NUMBER 函数如何使用降序 ORDER BY。

    select  count(*)
    
    from   (select  
    
                action
               ,row_number() over (                    order by action_time desc) as rn
               ,row_number() over (partition by action order by action_time desc) as rn_action
    
            from    mytab
            ) t
    
    group by action
            ,rn - rn_action
    
    having   min(rn) = 1
    

    【讨论】:

      【解决方案2】:

      想到这个:

      select count(*)
      from t cross join
           (select t2.action
            from t t2
            order by action_time desc
            limit 1
           ) last
      where t.action_time >= (select max(t2.action_time)
                              from t t2
                              where t2.action <> last.action
                             );
      

      这应该能够利用(action_time, action)上的索引。

      【讨论】:

      • 查询返回 4 但应该返回 3 :-(
      • 那么您将使用&gt; 而不是&gt;=
      【解决方案3】:

      应该这样做。

      SELECT COUNT(*)
      FROM actions
      WHERE action_time > (
      SELECT action_time
        FROM actions 
        WHERE action <> (SELECT action FROM actions ORDER BY action_time DESC LIMIT 1) 
      ORDER BY action_time DESC LIMIT 1);
      

      最里面的查询

      SELECT action FROM actions ORDER BY action_time DESC LIMIT 1
      

      确定最后一个动作。

      查询

      SELECT action_time
        FROM actions 
        WHERE action <> (SELECT action FROM actions ORDER BY action_time DESC LIMIT 1) 
      ORDER BY action_time DESC LIMIT 1
      

      查找具有不同操作的最后一行。

      最外面的查询查找该行之后的所有行。

      【讨论】:

      • 效果很好!非常感谢。你拯救了我的一天:-)
      【解决方案4】:

      改进的解决方案

      select  count(*)
      
      from   (select  
      
                  action
                 ,row_number() over (                    order by action_time desc) as rn
                 ,row_number() over (partition by action order by action_time desc) as rn_action
      
              from    mytab
              ) t
      
      where   rn = rn_action
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-04-21
        • 1970-01-01
        • 2022-01-18
        • 1970-01-01
        • 1970-01-01
        • 2018-11-17
        • 2015-06-20
        • 1970-01-01
        相关资源
        最近更新 更多