按期末而不是开始日期分组

时间:2015-09-09 02:31:53

标签: sql postgresql time-series postgresql-9.1 window-functions

我希望在数据集的结束日期之前汇总数据,其中包含一些前导期而非开始期。例如,我想查询一个表,并在结果中显示的日期的结束日期之前30天返回匹配结果的计数。原始表格仅包含销售日期(时间戳)。例如:

sales_timestamp
------------------
2015-08-05 12:00:00
2015-08-06 13:00:00
2015-08-25 12:31:00
2015-08-26 01:02:00
2015-08-27 02:03:00
2015-08-29 04:23:00
2015-09-01 12:00:00
2015-09-02 12:00:00
2015-09-08 00:00:00

结果查询输出的示例如下:

date_period  |   count_of_sales
--------------------------------
2015-08-24   |        2
2015-08-31   |        6
2015-09-07   |        6

其中2015-09-07的date_period意味着该公司在2015年9月7日结束的30天内销售了6件商品(如果真正的30天期限,则从2015年8月7日开始)。

我一直在玩date_trunc()函数的变体,但似乎无法将截断应用于结束日期而不是从开始分组。

这些数据将存放在PostgreSQL 9.1上。

1 个答案:

答案 0 :(得分:1)

此查询可以满足您的所有要求:

SELECT day::date AS date_period, count_of_sales
FROM (
   SELECT *, sum(ct) OVER (ORDER BY day ROWS 30 PRECEDING) AS count_of_sales
   FROM   generate_series(date '2015-08-24' - 30  -- start 30 days earlier
                        , date '2015-09-07'
                        , interval '1 day') day
   LEFT JOIN (
      SELECT date_trunc('day', sales_timestamp) AS day, count(*)::int AS ct
      FROM   sales
      GROUP  BY 1
      ) s USING (day)
   ) sub
JOIN  generate_series(date '2015-08-24'
                    , date '2015-09-07 '
                    , interval '1 week') day USING (day);

SQL Fiddle.

解释

  1. 生成一整套相关日期(第1 generate_series()
  2. LEFT JOIN每天的汇总计数。 LEFT保证每天一行行,这允许我们根据行数使用窗口函数。
  3. 使用sum()作为窗口聚合函数,前面有30天的自定义框架。 (您可能希望使用29,但不清楚您的计算方法。)

  4. 将结果加入到结果中您想要的实际天数。 (第2 generate_series(),每周一天)。

  5. 请注意,如果您使用timestamptz,则“日期”的定义来自会话的当前时区设置。不同时区的结果可能不同。不仅适用于timestamp,不依赖于当前时区。基本信息:

    相关答案以及具有自定义帧定义的窗口函数的说明: