如何将下一组的第一行包含在聚合中?

How to include the first row from the next group in an aggregation?

我在 Postgres 中有一个 table,其中包含类别和值。我想执行聚合,例如每个类别的 avg(value),但包括聚合中下一类别的第一行。

样本table:

id  category  value
-------------------
1   1         5.4
2   1         2.1
3   2         1.0
4   2         2.6
5   2         0.3
6   3         4.4
7   3         3.8

id是一个主键,提供了一个顺序。类别按顺序分组并连续。
acceptable(但不是必需的)创建一个像这样的中间 table,它复制相邻的行:

id  category  value
-------------------
1   1         5.4
2   1         2.1
3   1         1.0  <-- new row
4   2         1.0
5   2         2.6
6   2         0.3
7   2         4.4  <-- new row
8   3         4.4
9   3         3.8

...然后做:

select category, avg(value) group by category from sample_table

如何使用 SQL 语句实现这一点?

我怀疑这可以通过 window 函数和一些复杂的框架子句(如 GROUPS)来完成,但我不知道如何实现。 (见 https://www.postgresql.org/docs/12/sql-expressions.html#SYNTAX-WINDOW-FUNCTIONS

您确认类别数正在稳步增加 1,没有差距
这是简单案例的简单方法:

SELECT category, avg(value)
FROM  (
   SELECT category, value
   FROM   tbl

   UNION ALL
   (  -- parentheses required
   SELECT DISTINCT ON (category)
          category - 1, value
   FROM   tbl
   WHERE  category > (SELECT min(category) FROM tbl)  -- eliminate corner case
   ORDER  BY category, id
   )   
   ) sub
GROUP  BY 1
ORDER  BY 1;

UNION ALL 之后的第二个术语按照您的建议添加行:我取每组的第一行,然后从类别中减去 1。

极端情况:使用 min(category) - 1 添加新类别。可以轻松消除...


任何种类别的通用解决方案(只要定义了顺序):

SELECT category, avg(value)
FROM  (
   SELECT category, value
   FROM   tbl

   UNION ALL
   SELECT lag(category) OVER (ORDER BY category), value
   FROM  (
      SELECT DISTINCT ON (category)
             category, value
      FROM   tbl
      ORDER  BY category, id
      ) unicat
   ) sub
WHERE  category IS NOT NULL  -- eliminate corner case
GROUP  BY 1
ORDER  BY 1;

使用 window function lag().

将每个组的第一个值添加到上一个类别

关于DISTINCT ON

  • Select first row in each GROUP BY group?