3

我对 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 周(从星期一开始)

4

2 回答 2

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;

重点是您可以在 PostgreSQL 中增加天数integer。返回 a ,所以我们需要转换到另一个时间。date
date_trunc()timestampdate

我从 1 月 4 日开始,因为(在此处引用手册):

根据定义 (ISO 8601),周从星期一开始,一年的第一周包含当年的 1 月 4 日。

该函数愉快地接受像or这样的不可能的星期,并返回一个有点合理的结果。为了禁止这种情况,我会将其设为 plpgsql 函数并检查输入值。-155

这将返回一个集合而不是一个数组。将其改为数组(SELECT ARRAY(SELECT ...)很简单。但这应该更快。您的查询可能如下所示:

SELECT *
FROM   some_report_cache_table
JOIN   week_dates(1, 2013) w("date") USING ("date")

旁白:您不应该date用作列名,因为它是SQL 中的保留字

于 2013-05-17T16:02:14.463 回答
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
于 2013-05-17T12:38:44.020 回答