如何在聚合中包含下一组的第一行?
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
:
【讨论】:
嗨 Erwin,这是结合 DISTINCT、lag() 和 UNION 的好东西。 DISTINCT ON (category) 选择类别组的第一行。并且使用 lag(category) 您将此“新”行分配给前一个类别。我非常喜欢第二个通用解决方案,因为它更通用。 @Michael 是的!所以我称它为“通用” :) 第一个更快 - 如果适用的话。以上是关于如何在聚合中包含下一组的第一行?的主要内容,如果未能解决你的问题,请参考以下文章