查询以基于聚合值显示其他列

时间:2013-02-06 21:09:28

标签: sql postgresql

我一直在考虑这个问题几个小时而现在没有运气,所以我虽然SO上的人也许可以提供帮助:)。

我有一张表格,里面有关于商店处理量的数据。可以从该表中查询下面显示的前三列。我要做的是添加第4列,基本上是关于商店是否处理过> = $ 150的标志,如果是,则显示相应的日期。这种方式的工作方式是商店超过150美元的第一个例子是显示的日期。在第一个实例激活日期被命中后,后续处理量不计算。例如,对于商店4,只有一个激活日期的实例。

store_id  sales_volume   date        activated_date
----------------------------------------------------    
2         5              03/14/2012    
2         125            05/21/2012   
2         30             11/01/2012   11/01/2012    
3         100            02/06/2012
3         140            12/22/2012   12/22/2012
4         300            10/15/2012   10/15/2012
4         450            11/25/2012
5         100            12/03/2012

有关如何构建第四列的任何见解?提前谢谢!

2 个答案:

答案 0 :(得分:1)

解决方案首先计算累计销售额。然后,只有累积销售额首次通过$ 150级别时才需要激活日期。添加当前销售额时会发生这种情况,将累积金额推到阈值以上。以下case表达式处理此问题。

select t.store_id, t.sales_volume, t.date,
       (case when 150 > cumesales - t.sales_volume and 150 <= cumesales
             then date
        end) as ActivationDate
from (select t.*,
             sum(sales_volume) over (partition by store_id order by date) as cumesales
      from t
     ) t

如果你有一个不支持累积金额的旧版Postgres,你可以通过以下子查询获得累计销售额:

(select sum(sales_volume) from t t2 where t2.store_id = t.store_id and t2.date <= t.date) as cumesales

答案 1 :(得分:1)

变式1

您可以LEFT JOIN到一个表格,计算超过每家商店150美元限额的第一个日期:

SELECT t.*, b.activated_date
FROM   tbl t
LEFT   JOIN (
   SELECT store_id, min(thedate) AS activated_date
   FROM  (
      SELECT store_id, thedate
            ,sum(sales_volume) OVER (PARTITION BY store_id
                                     ORDER BY thedate) AS running_sum
      FROM   tbl
      ) a
   WHERE  running_sum >= 150
   GROUP  BY 1
   ) b ON t.store_id = b.store_id AND t.thedate = b.activated_date
ORDER  BY t.store_id, t.thedate;

第一天的计算必须分两步完成,因为累积运行总和的窗函数必须在单独的SELECT中应用。

变式2

另一个窗口函数而不是LEFT JOIN。可能不会更快。使用EXPLAIN ANALYZE进行测试。

SELECT *
      ,CASE WHEN running_sum >= 150 AND thedate = first_value(thedate)
               OVER (PARTITION BY store_id, running_sum >= 150 ORDER BY thedate)
       THEN thedate END AS activated_date
FROM  (
   SELECT *
         ,sum(sales_volume)
              OVER (PARTITION BY store_id ORDER BY thedate) AS running_sum
   FROM   tbl
   ) b
ORDER  BY store_id, thedate;

->sqlfiddle展示了两者。