在sql中对连续的时间间隔进行分组

Posted

技术标签:

【中文标题】在sql中对连续的时间间隔进行分组【英文标题】:group consecutive time intervals in sql 【发布时间】:2016-09-02 07:28:02 【问题描述】:

假设数据结构的类型

stock_name, action, start_date, end_date
google, growing, 1, 2
google, growing, 2, 3
google, falling, 3, 4
google, growing, 4, 5
yahoo, growing, 1, 2

如何聚合它以合并连续的时间间隔?

输出如下:

stock_name, action, start_date, end_date
google, growing, 1, 3
google, falling, 3, 4
google, growing, 4, 5
yahoo, growing, 1, 2

我曾想过使用排名窗口函数用一个常数对连续的数字进行编号,然后按该常数​​和动作/名称进行分组,但我无法让它发挥作用,如下所示:

stock_name, action, start_date, end_date, rank
google, growing, 1, 2, 1
google, growing, 2, 3, 1
google, falling, 3, 4, 1
google, growing, 4, 5, 2
yahoo, growing, 1, 2, 1

如果这是mysql,我很容易用变量解决它,但这在postgres中是不可能的。

可以有任意数量的连续间隔,因此不能选择自行加入预定的 nr 次。

解决方案的优雅(性能、可读性)很重要。

【问题讨论】:

连续时间间隔是否可以包含超过 2 条记录(例如 3、4、5)? 这对我来说实际上看起来像是一个空白和孤岛问题。 @AdrianBR If this were Mysql, I would easily solve it with variables, but this is not possible in postgres - 在 postgres 中什么是不可能的? @AdrianBR 如果您需要用户定义的变量,请使用 postgres 过程语言 这通常使用窗口函数来解决。这似乎很接近:***.com/questions/38979745/… 或此:***.com/questions/24474307/… 或此:***.com/questions/12467567/… 或此:***.com/questions/8246687/… 【参考方案1】:

您可以在 PL/pgSQL 中很好地使用变量。

我会用一个表函数来解决这个问题。

假设表名为stock,我的代码如下所示:

CREATE OR REPLACE FUNCTION combine_periods() RETURNS SETOF stock
   LANGUAGE plpgsql STABLE AS
$$DECLARE
   s stock;
   period stock;
BEGIN
   FOR s IN
      SELECT stock_name, action, start_date, end_date
      FROM stock
      ORDER BY stock_name, action, start_date
   LOOP
      /* is this a new period? */
      IF period IS NOT NULL AND
         (period.stock_name <> s.stock_name
            OR period.action <> s.action
            OR period.end_date <> s.start_date)
      THEN
         /* new period, output last period */
         RETURN NEXT period;
         period := NULL;
      ELSE
         IF period IS NOT NULL
         THEN
            /* period continues, update end_date */
            period.end_date := s.end_date;
         END IF;
      END IF;

      /* remember the beginning of a new period */
      IF period IS NULL
      THEN
         period := s;
      END IF;
   END LOOP;

   /* output the last period */
   IF period IS NOT NULL
   THEN
      RETURN NEXT period;
   END IF;

   RETURN;
END;$$;

我会这样称呼它:

test=> SELECT * FROM combine_periods();
┌────────────┬─────────┬────────────┬──────────┐
│ stock_name │ action  │ start_date │ end_date │
├────────────┼─────────┼────────────┼──────────┤
│ google     │ falling │          3 │        4 │
│ google     │ growing │          1 │        3 │
│ google     │ growing │          4 │        5 │
│ yahoo      │ growing │          1 │        2 │
└────────────┴─────────┴────────────┴──────────┘
(4 rows)

【讨论】:

以上是关于在sql中对连续的时间间隔进行分组的主要内容,如果未能解决你的问题,请参考以下文章

如何在postgres中对几个月的日期记录分组后填补时间间隔

SQL GROUP BY:连续的间隔?

hive sql 经典题目 连续登陆|间隔连续登陆|行列转换|累加|topN | 炸裂

如何在 Oracle SQL 上进行查询以获取时间间隔,按特定字段分组

按 1 分钟间隔分组操作链 sql BigQuery

在 SQL (Redshift) 中对连续块进行分组以进行聚合