【问题标题】:Grouping based on sequence of rows基于行序列的分组
【发布时间】:2014-09-02 16:32:30
【问题描述】:

我有一个订单表,其中有一列表示是买入还是卖出,行通常按时间戳排序。我想做的是对连续买入的组加上他们的卖出进行操作。例如B B S B S B B S -> (B B S) (B S) (B B S)

例子:

order_action |      timestamp      
-------------+---------------------
buy          | 2013-10-03 13:03:02
buy          | 2013-10-08 13:03:02
sell         | 2013-10-10 15:58:02
buy          | 2013-11-01 09:30:02
buy          | 2013-11-01 14:03:02
sell         | 2013-11-07 10:34:02
buy          | 2013-12-03 15:46:02
sell         | 2013-12-09 16:00:03
buy          | 2013-12-11 13:02:02
sell         | 2013-12-18 15:59:03

最后我将运行一个聚合函数(这些组是这样我可以根据其卖出订单排除整个组),所以GROUP BY 或分区窗口似乎是正确的方法,但我无法弄清楚如何获得这个特定的分组。

【问题讨论】:

  • 如果您有任何尝试,请发布您的代码
  • 您可以添加您希望看到的输出示例吗?
  • 会不会每组买只有一个卖?
  • What I'd like to do is operate on groups of consecutive buys... operate 是什么?也就是说,你真正想要做什么?你这样做有什么问题?为未定义的问题提出解决方案很棘手。
  • 我之前问过一个类似的问题,它可能会有所帮助。 stackoverflow.com/questions/18889056/…

标签: sql postgresql aggregate-functions window-functions


【解决方案1】:

使用count() 作为窗口聚合函数,这可能非常简单

Postgres 9.4 或更高版本,聚合 FILTER

SELECT *, count(*) FILTER (WHERE order_action = 'sell')
                   OVER (ORDER BY ts DESC) AS grp
FROM   orders
ORDER  BY ts;

或使用升序的grp 数字:

SELECT *, count(*) FILTER (WHERE order_action = 'sell')
          OVER (ORDER BY ts ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS grp
FROM   orders
ORDER  BY ts;

见:

使用ts 而不是timestamp 作为列名以避免reserved words 作为标识符。

count() 使用the default frame definition 返回从帧开始(在本例中为整个表)到当前行(最后一个对等方)的运行计数。销售的运行计数按要求对您的行进行分组。
我在OVER 子句中订购降序,让每个组以尾随“卖出”结束,而不是前导“卖出”。这导致组数递减。但这不重要,您只需要组号。
重复的时间戳将是一个问题(无论如何!)。

组号升序的一种方法:对窗口函数使用自定义FRAME definition

在任何 Postgres 版本中

SELECT *, count(order_action = 'sell' OR NULL) OVER (ORDER BY ts DESC) AS grp
FROM   orders
ORDER  BY ts;
SELECT *, count(order_action = 'sell' OR NULL)
          OVER (ORDER BY ts ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS grp
FROM   orders;

count() 只计算非空值。表达式order_action = 'sell' OR NULL 导致“卖出”TRUE,否则NULL

dbfiddle here - 全部演示。
sqlfiddle

【讨论】:

    【解决方案2】:

    您可以通过计算每行或之后的sells 的数量来表征组。您可以使用累积总和来执行此操作,以获得可用于聚合的组。这是一个例子:

    select min(timestamp), max(timestamp), sum(case when order_action = 'buy' then 1 else 0 end) as buys
    from (select o.*,
                 sum(case when order_action = 'sell' then 1 else 0 end) over
                     (order by timestamp desc) as grp
          from orders o
         ) o
    group by grp
    

    【讨论】:

      【解决方案3】:

      我没有 PostgreSQL,所以我在 SQL Fiddle 上试了一下

      with sells as (
        select
          rank() over w grp,
          lag(timestamp,1,'2000-01-01') over w sd,
          timestamp td
        from
          orders
        where
          order_action = 'sell'
        window w as (order by timestamp)
      )
      select
        s.grp,
        o.order_action,
        o.timestamp
      from
        orders o
      join
        sells s
          on o.timestamp > s.sd
          and o.timestamp <= s.td
      order by o.timestamp
      

      让我知道这是否适合您。这是我第一次使用 PostgreSQL,我很喜欢。

      【讨论】:

        猜你喜欢
        • 2017-06-11
        • 1970-01-01
        • 2017-03-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-08
        • 2013-02-22
        相关资源
        最近更新 更多