【问题标题】:How to aggregate over unique values in Postgres with window functions如何使用窗口函数聚合 Postgres 中的唯一值
【发布时间】:2014-03-13 00:13:30
【问题描述】:

(使用 Postgres 9.1)

我的程序处理杀虫剂喷雾器试图根据原始目标喷洒多个实际单位。例如,喷雾器 Joe 应该喷洒 10 个目标,但他实际上喷洒了 7 个。

我得到了一个表格,其中汇总了所有计划目标(column= targets)和实际目标(column= actual),以及其他一些数据,包括喷雾器的名字。这是架构:

CREATE TABLE spray_summary
(
 id character varying(1),
 target integer,
 ref_id character varying(1),
 actual integer,
 sprayer character varying(25)
)

数据是 (id, target) 和 (ref_id, actual, sprayer) 之间的非规范化连接,但现在我只需要处理这个表。以下是完整结果:

SELECT * FROM spray_summary
+----+--------+--------+--------+---------+ |编号 |目标 | ref_id |实际 |喷雾器| +----+--------+--------+--------+---------+ |一个 | 1 | "l" | 10 | “乔” | |一个 | 1 | “米” | 10 | “乔” | |一个 | 1 | "p" | 10 | “乔” | | c | 3 | "n" | 10 | “乔” | | c | 3 | "o" | 10 | “乔” | +----+--------+--------+--------+---------+

您可以看到 id 值“a”由于一对多连接重复了 3 次,而“c”重复了 2 次。鉴于此,我想做的是显示 target 值的 SUM、“实际”值的 SUM 和喷雾器。我使用了以下查询:

SELECT SUM(target) targets, SUM(actual) actuals, sprayer FROM spray_summary GROUP BY sprayer

返回结果:

+--------+--------+---------+ |目标 |实际 |喷雾器| +--------+--------+---------+ | 9 | 50 | “乔” | +--------+--------+---------+

虽然 实际 值的总和是正确的 (5 * 10 = 50),但由于数据集非规范化,目标 值正在相乘。我希望“目标”相对于 idsprayer 是唯一的,所以我尝试了一个窗口函数:

SELECT SUM(target) OVER(PARTITION BY sprayer, id),
sprayer,
SUM(actual)
FROM spray_summary
GROUP BY sprayer, target, id

这给了我结果:

+--------+--------+---------+ |目标 |实际 |喷雾器| +--------+--------+---------+ | 1 | 30 | “乔” | | 3 | 20 | “乔” | +--------+--------+---------+

还是不对!正确的解决方案将提供以下内容:

+--------+--------+---------+ |目标 |实际 |喷雾器| +--------+--------+---------+ | 4 | 50 | “乔” | +--------+--------+---------+

但无论我如何尝试调整窗口函数,行都会被拆分,因为我 到 GROUP BY target 这会分解行。有任何想法吗?我知道这可以通过将表与自身连接来重写,一次用于 SUM target,一次用于 SUM actual,但我没有该选项。我只能修改列定义。

提前致谢。

编辑:我知道这可以通过将一些逻辑推入子查询来解决,但如果可能的话,我正在寻找列级解决方案。 SQL 是自动生成的,因此我对结构没有太多控制,但我可以修改列定义,因此我正在寻找列级解决方案,例如窗口函数。

最坏的情况是 Postgres 无法在列级别解决这个问题,我将不得不重新编写 SQL 生成器。

【问题讨论】:

  • 加入前不能聚合目标?
  • 您可以尝试从 select 中进行选择,但这不是分析查询的好用例
  • 一种解决方案是提前聚合,但是这段代码是通过Java自动生成的,我对整个结构没有太多控制权,只有列值。这是一个不幸的限制。

标签: sql postgresql aggregate-functions


【解决方案1】:

SQL Fiddle

select sum(target) as target, sum(actual) as actual, sprayer
from (
    select
        target,
        sum(actual) as actual,
        sprayer
    from spray_summary
    group by id, target, sprayer
) s
group by sprayer
order by sprayer
;
 target | actual | sprayer 
--------+--------+---------
      4 |     50 | joe

【讨论】:

  • 有没有办法在不重组查询的情况下做到这一点?查询是动态生成的,所以我对结构没有太多控制,但我可以修改列定义,因此我正在寻找 SELECT 级别的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-09
相关资源
最近更新 更多