Generate series of week intervals for given month

与世无争的帅哥 提交于 2019-11-30 07:03:59
select
    greatest(date_trunc('week', dates.d), date_trunc('month',dates.d)) as start
from generate_series('2013-02-01'::date, '2013-02-28', '1 day') as dates(d)
group by 1
order by 1
SELECT generate_series(date_trunc('week', date '2013-02-01' + interval '6 days')
                     , date_trunc('week', date '2013-02-01' + interval '1 month - 1 day')
                     , interval '1 week')::date AS day
UNION  SELECT date '2013-02-01'
ORDER  BY 1;

This variant does not need a subselect, GREATEST or GROUP BY and only generates the required rows. Simpler, faster. It's cheaper to UNION one row.

  • Add 6 days to the first day of the month before date_trunc('week', ...) to compute the first Monday of the month.

  • Add 1 month and subtract 1 day before date_trunc('week', ...) to get the last Monday of the month.
    This can conveniently be stuffed into a single interval expression: '1 month - 1 day'

  • UNION (not UNION ALL) the first day of the month to add it unless it's already included as Monday.

  • Note that date + interval results in timestamp, which is the optimum here. Detailed explanation:

Automation

You can provide the start of the date series in a CTE:

WITH t(d) AS (SELECT date '2013-02-01')  -- enter 1st of month once
SELECT generate_series(date_trunc('week', d + interval '6 days')
                     , date_trunc('week', d + interval '1 month - 1 day')
                     , interval '1 week')::date AS day
FROM   t
UNION  SELECT d FROM t
ORDER  BY 1;

Or wrap it into a simple SQL function for convenience with repeated calls:

CREATE OR REPLACE FUNCTION f_week_starts_this_month(date)
  RETURNS SETOF date AS
$func$
SELECT generate_series(date_trunc('week', $1 + interval '6 days')
                     , date_trunc('week', $1 + interval '1 month - 1 day')
                     , interval '1 week')::date AS day
UNION
SELECT $1
ORDER  BY 1
$func$  LANGUAGE sql IMMUTABLE;

Call:

SELECT * FROM f_week_starts_this_month('2013-02-01');

You would pass the date for the first day of the month, but it works for any date. You the first day and all Mondays for the following month.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!