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

Posted

技术标签:

【中文标题】如何在聚合中包含下一组的第一行?【英文标题】:How to include the first row from the next group in an aggregation? 【发布时间】:2021-04-12 16:46:06 【问题描述】:

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

示例表:

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 是主键并提供顺序。类别按顺序分组并连续。 像这样创建一个中间表是可以接受的(但不是必需的),它复制了相邻的行:

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 语句来实现?

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

【问题讨论】:

如样本值所示,类别编号是否总是增加 1?没有间隙? 是的,我们可以假设 【参考方案1】:

您确认类别数量稳步增加 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?

【讨论】:

嗨 Erwin,这是结合 DISTINCT、lag() 和 UNION 的好东西。 DISTINCT ON (category) 选择类别组的第一行。并且使用 lag(category) 您将此“新”行分配给前一个类别。我非常喜欢第二个通用解决方案,因为它更通用。 @Michael 是的!所以我称它为“通用” :) 第一个更快 - 如果适用的话。

以上是关于如何在聚合中包含下一组的第一行?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SQL 查询中选择每个组的第一行?

如何选择每组的第一行?

如何获得每组的第一行?

如何根据多个排序列选择每组的第一行?

如何将熊猫数据框值除以每组的第一行?

如何根据特定的顺序选择每组的第一行?