我对PostgreSQL很新,最近在我的项目中,我遇到了检索一年中特定一周的所有记录的问题。主要问题当然是边界周(1和52/53)。
为此,我写了一个函数:
CREATE OR REPLACE FUNCTION week_dates(_week integer, _year integer) RETURNS date[] AS $$
DECLARE
result date[];
BEGIN
WITH RECURSIVE dates(wdate) AS (
SELECT MAX(date) AS wdate FROM time WHERE woy = _week AND year = _year AND dow > 3
UNION ALL
SELECT wdate-1 AS wdate FROM dates WHERE EXTRACT(WEEK from (wdate-1)) = _week
),
dates2(wdate) AS (
SELECT MAX(wdate) AS wdate FROM dates
UNION ALL
SELECT wdate+1 AS wdate FROM dates WHERE EXTRACT(WEEK from (wdate+1)) = _week
),
sorted AS ((SELECT * FROM dates) UNION (SELECT * FROM dates2) ORDER BY wdate ASC)
-- sorted AS (SELECT wdate FROM dates ORDER BY wdate ASC)
SELECT array_agg(wdate) INTO result FROM sorted;
-- SELECT wdate FROM sorted;
RETURN result;
END;
$$ LANGUAGE plpgsql;
它的用法是,例如:
SELECT * FROM "some_report_cache_table" WHERE "date" = ANY(week_dates(1, 2013));
是否有更好/更快/更简单的解决方案(可能是一些内置功能)?
我正在使用PostgreSQL 9.2,按周我的意思是ISO周年(星期一开始)
答案 0 :(得分:4)
更简单,但是:
CREATE OR REPLACE FUNCTION week_dates(_week integer, _year integer)
RETURNS SETOF date AS
$func$
SELECT date_trunc('week', ($2::text || '-1-4')::timestamp)::date
+ 7 * ($1 - 1) -- fix off-by-one
+ generate_series (0,6)
$func$ LANGUAGE sql;
重点是您可以将integer
添加到date
以增加PostgreSQL中的天数。
date_trunc()
会返回timestamp
,因此我们需要再次投放date
。
我从1月4日开始,因为(quoting the manual here):
根据定义(ISO 8601),周数从周一和第一周开始 一年中包含当年的1月4日。
该功能乐意接受不可能的周,如-1
或55
,并返回一个有些合理的结果。为了禁止这种情况,我会将它设为plpgsql函数并检查输入值。
这会返回 set 而不是数组。将它变成一个数组(SELECT ARRAY(SELECT ...
)是微不足道的。但这应该更快。您的查询可能如下所示:
SELECT *
FROM some_report_cache_table
JOIN week_dates(1, 2013) w("date") USING ("date")
除此之外:您不应将date
用作列名,因为它是reserved word in SQL。
答案 1 :(得分:3)
我认为这更简单。它将返回给定年份中给定周的所有日子。
create or replace function week_dates(_week integer, _year integer)
returns setof date as $$
with first_day_of_first_week as (
select distinct date_trunc('week', d)::date
from generate_series(
to_date(_year::text, 'YYYY'),
to_date(_year::text, 'YYYY') + 3,
'1 day'
) s(d)
where extract(week from d) = 1
)
select
(select first_day_of_first_week)
+ (_week - 1) * 7
+ generate_series(0, 6) "day"
;
$$ language sql;
select * from week_dates(1, 2012) s("day");
day
------------
2012-01-02
2012-01-03
2012-01-04
2012-01-05
2012-01-06
2012-01-07
2012-01-08