1

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

我的日期总计
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
4

2 回答 2

4

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

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在我的示例中)以保留结果中的所有周,即使没有找到数据行。

  • 其余的应该是显而易见的。COALESCE输出0而不是NULL空周。

  • mydate date我假设并且total int由于缺乏信息,数据类型必须匹配。(sum()an 的intbigint。)

  • 解释我的特殊用途generate_series()

于 2012-05-15T09:28:10.090 回答
0

使用此功能

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;
于 2012-05-15T09:30:57.373 回答