获取连接表列的不同总和

时间:2012-11-01 00:29:02

标签: sql postgresql aggregate-functions duplicate-removal window-functions

我有一个问题,我希望有一个简单的解决方案。我会尝试尽可能简单:

  • 门票属于与会者
  • 示例:

select * from tickets JOIN attendees ON attendee.id = tickets.attendee_id
  • 与会者有一个名为“收入”的小数列

那就是说,我需要运行一个查询,返回有关门票的各种信息,包括总收入。问题是,如果2张门票属于同一位与会者,则会将其收入计入两次。如何仅将与会者收入相加一次?

我不想使用子查询,因为我的ORM使这很困难。如果我想为多列执行此操作,则子查询解决方案无法扩展。

这就是我所拥有的:

  • 1位收入为100
  • 的与会者
  • 两张属于该与会者的门票

Select count(tickets.*) as tickets_count
     , sum(attendees.revenue) as atendees_revenue
from tickets LEFT OUTER JOIN attendees ON attendees.id = tickets.attendee_id;

=>这告诉我attendees_revenue是200.我希望它是100.因为数据库中有一个参与者,其现有的回报为100.我不希望参与者被重复计算。

如果可能,请告诉我。

4 个答案:

答案 0 :(得分:7)

要获得结果不带子查询,您必须使用高级窗口函数技巧:

SELECT sum(count(*))       OVER () AS tickets_count
      ,sum(min(a.revenue)) OVER () AS atendees_revenue
FROM   tickets   t
JOIN   attendees a ON a.id = t.attendee_id
GROUP  BY t.attendee_id
LIMIT  1;

SQL Fiddle.

解释

理解这一点的关键是查询中的事件序列

汇总功能 - >窗口函数 - > DISTINCT - > LIMIT

此处有更多详情:

一步一步:

  1. GROUP BY t.attendee_id - 您通常会在子查询中执行此操作。

  2. 然后我总结了计数以获得门票的总数。效率不高,但受到您的要求的限制。聚合函数count(*)包含在窗口函数sum( ... ) OVER ()中,以得到不常见的表达式:sum(count(*)) OVER ()

    并将每位与会者的最低收入相加,以获得没有重复的金额。

    您也可以使用max()avg()代替min(),效果与revenue相同,每位与会者每行保证相同。

    如果在窗口函数中允许DISTINCT,这可能会更简单,但PostgreSQL尚未实现此功能。 Per documentation

      

    与普通聚合函数不同,聚合窗口函数不会   允许在函数参数列表中使用DISTINCTORDER BY

  3. 最后一步是获得一行。这可以使用DISTINCT(SQL标准)来完成,因为所有行都是相同的。不过,LIMIT 1会更快。或者SQL标准格式FETCH FIRST 1 ROWS ONLY

答案 1 :(得分:3)

如何进行简单的划分:

 Select count(tickets.*) as tickets_count
 , sum(attendees.revenue) / count(attendees.id) as atendees_revenue
 from tickets LEFT OUTER JOIN attendees ON attendees.id = 
 tickets.attendee_id;

这应该处理重复,重复,等等。

答案 2 :(得分:0)

你实际上非常接近,有很多方法可以做到这一点,如果我理解你的问题,这应该做到:

SELECT
   COUNT(*) AS ticketsCount,
   SUM(DISTINCT attendees.revenue) AS revenueSum
FROM
   tickets
   LEFT JOIN attendees ON
      attendees.id = tickets.attendee_id

答案 3 :(得分:0)

以前的答案几乎是正确的。你只需要在相同的收入情况下做好明显的工作。如果你的id有数字类型,你可以解决这个问题:

SELECT
  COUNT(*) AS ticketsCount,
  SUM(DISTINCT attendees.id + attendees.revenue) - SUM(DISTINCT attendees.id) AS revenueSum
FROM
  tickets
LEFT JOIN attendees ON
  attendees.id = tickets.attendee_id