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;
Denna variant behöver inte ett underval, GREATEST
eller GROUP BY
och genererar bara de rader som krävs. Enklare, snabbare. Det är billigare att UNION
en rad.
-
Lägg till 6 dagar till den första dagen i månaden före
date_trunc('week', ...)
för att beräkna den första måndagen i månaden . -
Lägg till 1 månad och subtrahera 1 dag före
date_trunc('week', ...)
för att få den sista måndagen i månaden .
Detta kan enkelt stoppas i ett endainterval
uttryck:'1 month - 1 day'
-
UNION
(inteUNION ALL
) den första dagen i månaden för att lägga till den om den inte redan är inkluderad som måndag. -
Observera att
date
+interval
resulterar itimestamp
, vilket är det optimala här. Detaljerad förklaring:
Automatisering
Du kan ange början av datumserien i en 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;
Eller slå in den i en enkel SQL-funktion för bekvämlighet med upprepade anrop:
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;
Ring:
SELECT * FROM f_week_starts_this_month('2013-02-01');
Du skulle passera datumet för den första dagen i månaden, men det fungerar för alla datum. Du första dagen och alla måndagar för följande månad.