每周总金额

时间:2012-05-15 08:43:54

标签: sql postgresql aggregate-functions date-range

我在PostgreSQL数据库中有一个表,其中包含日期和每天的总计数。

mydate       total
2012-05-12      12 
2012-05-14       8 
2012-05-13       4 
2012-05-12      12 
2012-05-15       2 
2012-05-17       1 
2012-05-18       1 
2012-05-21       1 
2012-05-25       1 

现在我需要获得给定日期范围的每周总数 防爆。我希望将每周总计从2012-05-01提升到2012-05-31

我正在看这个输出:

2012-05-01 2012-05-07   0
2012-05-08 2012-05-14  36
2012-05-15 2012-05-22   5
2012-05-23 2012-05-29   1
2012-05-30 2012-05-31   0

2 个答案:

答案 0 :(得分:3)

这适用于任何给定的日期范围:

CREATE FUNCTION f_tbl_weekly_sumtotals(_range_start date, _range_end date)
  RETURNS TABLE (week_start date, week_end date, sum_total bigint)
  LANGUAGE sql AS
$func$
SELECT w.week_start, w.week_end, COALESCE(sum(t.total), 0)
FROM  (
   SELECT week_start::date, LEAST(week_start::date + 6, _range_end) AS week_end
   FROM   generate_series(_range_start::timestamp
                        , _range_end::timestamp
                        , interval '1 week') week_start
   ) w
LEFT   JOIN tbl t ON t.mydate BETWEEN w.week_start and w.week_end
GROUP  BY w.week_start, w.week_end
ORDER  BY w.week_start
$func$;

呼叫:

SELECT * FROM f_tbl_weekly_sumtotals('2012-05-01', '2012-05-31');

重点

  • 为方便起见,我将其包装在一个函数中,因此日期范围必须只提供一次。

  • 子查询w生成从给定日期范围的第一天开始的一系列周。上限的上限为LEAST,以保持在给定日期范围的上限。

  • 然后LEFT JOIN到数据表(我的示例中为tbl),以便在结果中保留所有周,即使没有找到数据行。< / p>

  • 其余应该是显而易见的。 <{1}}在空周内输出COALESCE而不是0

  • 数据类型必须匹配,我假设NULLmydate date缺少信息。 (total int的{​​{1}}为sum()。)

  • 我对int的具体用法的解释:

答案 1 :(得分:0)

使用此function

CREATE OR REPLACE FUNCTION last_day(date)
RETURNS date AS
$$
  SELECT (date_trunc('MONTH', $1) + INTERVAL '1 MONTH - 1 day')::date;
$$ LANGUAGE 'sql' IMMUTABLE STRICT;

AND generate_series(从8.4开始)我们可以创建日期分区。

SELECT wk.wk_start, 
       CAST(
            CASE (extract(month from wk.wk_start) = extract(month from wk.wk_start + interval '6 days'))
            WHEN true THEN wk.wk_start + interval '6 days'
            ELSE last_day(wk.wk_start)
            END 
           AS date) AS wk_end
  FROM
    (SELECT CAST(generate_series('2012-05-01'::date,'2012-05-31'::date,interval '1 week') AS date) AS wk_start) AS wk;

然后将其与数据放在一起

CREATE TABLE my_tab(mydate date,total integer);
INSERT INTO my_tab 
values    
('2012-05-12'::date,12),
('2012-05-14'::date,8),
('2012-05-13'::date,4),
('2012-05-12'::date,12),
('2012-05-15'::date,2),
('2012-05-17'::date,1),
('2012-05-18'::date,1),
('2012-05-21'::date,1),
('2012-05-25'::date,1); 

WITH month_by_week AS
    (SELECT wk.wk_start, 
       CAST(
            CASE (extract(month from wk.wk_start) = extract(month from wk.wk_start + interval '6 days'))
            WHEN true THEN wk.wk_start + interval '6 days'
            ELSE last_day(wk.wk_start)
            END 
           AS date) AS wk_end
  FROM
    (SELECT CAST(generate_series('2012-05-01'::date,'2012-05-31'::date,interval '1 week') AS date) AS wk_start) AS wk
  )
SELECT month_by_week.wk_start,
       month_by_week.wk_end,
       SUM(COALESCE(mt.total,0))
  FROM month_by_week 
       LEFT JOIN my_tab mt ON  mt.mydate BETWEEN month_by_week.wk_start AND month_by_week.wk_end
 GROUP BY month_by_week.wk_start,
          month_by_week.wk_end
 ORDER BY month_by_week.wk_start;