为什么聚合函数不能与DISTINCT ON(...)一起使用?

时间:2018-09-28 13:16:51

标签: postgresql aggregate-functions

问题是:

  

如何获取由汇总函数选择的行?

问题was answered并部分解决了我的问题。但由于以下原因,我仍然无法用GROUP BY替换DISTINCT ON

我都需要:

  1. 选择聚合行的id(可以用DISTINCT ON解决)
  2. ratio列求和(可以用GROUP BY进行解析)

用户消耗了amount的某些资源。第10天的一天用户消耗8的一部分,而另一天的10h用户消耗3,而4h的用户不消耗资源。该任务是最大程度地为消耗的资源开票,而不在不消耗资源时开票

 id | name | amount | ratio 
----+------+--------+-------
  1 | a    |      8 |    10
  2 | a    |      3 |    10

我通过下一个查询完成此任务:

SELECT 
    (
       SELECT id FROM t2 
       WHERE id = ANY ( ARRAY_AGG( tf.id ) ) AND amount = MAX( tf.amount ) 
    ) id,
    name, 
    MAX(amount) ma,
    SUM( ratio )
FROM t2  tf
GROUP BY name

为什么不允许将聚合函数与DISTINCT ON一起使用?

select distinct on ( name ) id, name, amount, sum( ratio )
from t2
order by name, amount desc

或更简单:

select distinct on ( name ) id, name, max(amount), sum( ratio )
from t2

这还将解决ORDER BY的问题。不需要workaround with subquery

是否有技术上的原因不允许上一个示例中的查询按所述方式工作?

UPD
从理论上讲,它可以像下一个一样工作:

第一个示例:

select distinct on ( name ) id, name, amount, sum( ratio )
from t2
order by name, amount desc

找到第一行后,它将保存其idname

下次找到第二行和下一个非重复行时,它将调用sum并累积ratio

第二个示例:

select distinct on ( name ) id, name, max(amount), sum( ratio )
from t2

找到第一行后,它将保存其idname,累加ratio并将ratio的当前值设置为最大值

下次找到第二行和下一个非重复行时,它将调用sum并累积ratio

如果第二行和/或下一个非区别行中的任何一个对于ratio列具有更大的值,则将其另存为最大值,并为id保存的值将被更新

UPD
如果more than one row where amount = max(amount) Postgres可以从任一行返回值。因为这是针对不在DISTINCT ON下的任何字段完成的

为确保返回哪个查询,查询可以通过ORDER BY子句进行限定。像这样here

2 个答案:

答案 0 :(得分:1)

我不确定我是否能完全理解您的问题(我不理解“ 10h用户”部分)。

但是我相信您正在搜索window functions。我从另一个问题中拉开了小提琴,并借助这种窗口函数添加了SUM(ratio)

这是您期望的吗?

demo: db<>fiddle

SELECT DISTINCT ON (name)
    id, 
    name, 
    amount,
    SUM(ratio) OVER (PARTITION BY name)
FROM test
ORDER BY name, amount DESC

当然,您也可以使用相同的窗口函数来计算MAX(amount)

SELECT 
    id, 
    name, 
    max_amount, 
    sum_ratio 
FROM (
    SELECT 
        t.*,
        MAX(amount) OVER w as max_amount,
        SUM(ratio) OVER w as sum_ratio
    FROM test t
    WINDOW w as (PARTITION BY name)
    ORDER BY name
) s 
WHERE amount = max_amount

不需要GROUP BY。是的,但是在这种情况下,您需要一个额外的子查询,其中您必须过滤窗口函数(amount = max_amount)的结果

答案 1 :(得分:0)

回答我的问题:

  

是否有技术原因不允许上一个示例中的查询按所述方式工作?

如果amount = max(amount)

中有多行,我们必须考虑如何提取ID。
 id | name | amount | ratio 
----+------+--------+-------
  1 | a    |      8 |    10
  2 | a    |      8 |    10

对于此数据,上面的查询将产生错误:

ERROR:  more than one row returned by a subquery used as an expression