在PostgreSQL(我的版本为9.6.6)中,最简单的方法是从星期日开始获取星期数?
DATE_PART('week',x)
返回:
ISO 8601的星期编号,是一年中的第几周。根据定义,ISO周从星期一开始,一年的第一周包含该年的1月4日。换句话说,一年的第一个星期四在该年的第1周。 (doc)
说我的查询就像:
WITH dates as (SELECT generate_series(timestamp '2014-01-01',
timestamp '2014-01-31',
interval '1 day'
)::date AS date
)
SELECT
date,
TO_CHAR(date,'Day') AS dayname,
DATE_PART('week',date) AS weekofyear
FROM dates
返回:
date dayname weekofyear
--------------------------------
2014-01-01 Wednesday 1
2014-01-02 Thursday 1
2014-01-03 Friday 1
2014-01-04 Saturday 1
2014-01-05 Sunday 1 <- I want this to be 2
2014-01-06 Monday 2
2014-01-07 Tuesday 2
2014-01-08 Wednesday 2
到目前为止,我已经尝试过:
SELECT
date,
TO_CHAR(date,'Day') AS dayname,
DATE_PART('week',date) AS week_iso,
DATE_PART('week',date + interval '1 day') AS week_alt
FROM dates
如果年份是从星期日开始的话,这是行不通的。
此外,我希望第1周包含该年的1月1日。因此,如果1月1日为星期六,我希望第1周为一天(而不是ISO风格的第53周)。此行为与Excel WEEKNUM function一致。
答案 0 :(得分:1)
要获取一年中的第几周,从星期日开始的几周,我们需要知道从一年的第一天到目标日期之间有多少个星期日。
我改编了@Erwin Brandstetter的解决方案here。此解决方案计算包括一年中第一天在内的星期日,但不包括目标日期。
然后,因为我希望第一(部分)周成为第一周(而不是零),所以我需要添加1 ,除非一年的第一天是星期日(在这种情况下为已经第一周了。
WITH dates as (SELECT generate_series(timestamp '2014-01-01',
timestamp '2014-01-31',
interval '1 day'
)::date AS date
)
SELECT
date,
TO_CHAR(date,'Day') AS dayname,
DATE_PART('week',date) AS week_iso,
((date - DATE_TRUNC('year',date)::date) + DATE_PART('isodow', DATE_TRUNC('year',date)) )::int / 7
+ CASE WHEN DATE_PART('isodow', DATE_TRUNC('year',date)) = 7 THEN 0 ELSE 1 END
AS week_sundays
FROM dates
返回
date dayname weekofyear week_sundays
--------------------------------
2014-01-01 Wednesday 1 1
2014-01-02 Thursday 1 1
2014-01-03 Friday 1 1
2014-01-04 Saturday 1 1
2014-01-05 Sunday 1 2
2014-01-06 Monday 2 2
2014-01-07 Tuesday 2 2
显示从星期日开始的几年中如何工作:
2017-01-01 Sunday 52 1
2017-01-02 Monday 1 1
2017-01-03 Tuesday 1 1
2017-01-04 Wednesday 1 1
2017-01-05 Thursday 1 1
2017-01-06 Friday 1 1
2017-01-07 Saturday 1 1
2017-01-08 Sunday 1 2
答案 1 :(得分:0)
以下方法可能有效-考虑到以下两种情况进行了测试:
WITH dates as (SELECT generate_series(timestamp '2014-01-01',
timestamp '2014-01-10',
interval '1 day'
)::date AS date
union
SELECT generate_series(timestamp '2017-01-01',
timestamp '2017-01-10',
interval '1 day'
)::date AS date
)
, alt as (
SELECT
date,
TO_CHAR(date,'Day') AS dayname,
DATE_PART('week',date) AS week_iso,
DATE_PART('week',date + interval '1 day') AS week_alt
FROM dates
)
select date, dayname,
week_iso, week_alt, case when week_alt <> week_iso
then week_alt
else week_iso end as expected_week
from alt
order by date
输出:
date dayname week_iso week_alt expected_week
2014-01-01 Wednesday 1 1 1
2014-01-02 Thursday 1 1 1
2014-01-03 Friday 1 1 1
2014-01-04 Saturday 1 1 1
2014-01-05 Sunday 1 2 2
2014-01-06 Monday 2 2 2
2014-01-07 Tuesday 2 2 2
....
2017-01-01 Sunday 52 1 1
2017-01-02 Monday 1 1 1
2017-01-03 Tuesday 1 1 1
2017-01-04 Wednesday 1 1 1
2017-01-05 Thursday 1 1 1
2017-01-06 Friday 1 1 1
2017-01-07 Saturday 1 1 1
2017-01-08 Sunday 1 2 2
答案 2 :(得分:0)
任务并不像它第一次出现时那样令人生畏。它主要需要在1月1日或之后找到第一个太阳。该日期成为第一周的最后一天。从那里开始,仅计算随后的几周。一个额外的问题。另一个重要的意义是,在每周定义中,每年始终有53周,而最后一周的最后一天是12月31日。以下为该周定义生成年度日历。
create or replace function non_standard_cal(year_in integer)
returns table (week_number integer, first_day_of_week date, last_day_of_week date)
language sql immutable leakproof strict rows 53
as $$
with recursive cal as
(select 1 wk, d1 start_of_week, ds end_of_week, de stop_date
from (select d1+substring( '0654321'
, extract(dow from d1)::integer+1
, 1)::integer ds
, d1, de
from ( select make_date (year_in, 1,1) d1
, make_date (year_in+1, 1,1) -1 de
) a
) b
union all
select wk+1, end_of_week+1, case when end_of_week+7 > stop_date
then stop_date
else end_of_week+7
end
, stop_date
from cal
where wk < 53
)
select wk, start_of_week, end_of_week from cal;
$$ ;
作为一般规则,我避免使用幻数,但有时它们很有用;在这种情况下。在魔术数字(实际上是字符串)中,“ 0654321”中的每个数字表示当通过标准日期编号系统(0-6表示星期日)进行索引时,在1月1日当天或之后到达第一个星期一所需的天数。结果是星期一是第一周的最后一天。生成递归CTE的第一行。剩余的行只为每个星期添加适当的天数,直到生成53周为止。
以下显示了确保每周的每一天轮到1月1日(是的,有些日子重复)所需的年数。逐年运行以验证其日历。
do $$
declare
cal record;
yr_cal cursor (yr integer) for
select * from non_standard_cal(2000+yr) limit 1;
begin
for yr in 18 .. 26
loop
open yr_cal(yr);
fetch yr_cal into cal;
raise notice 'For Year: %, week: %, first_day: %, Last_day: %, First day is: %'
, 2000+yr
,cal.week_number
,cal.first_day_of_week
,cal.last_day_of_week
,to_char(cal.first_day_of_week, 'Day');
close yr_cal;
end loop;
end; $$;
答案 3 :(得分:0)
此查询完美地将星期一替换为星期日作为一周的开始。
查询
SELECT CASE WHEN EXTRACT(day from '2014-01-05'::date)=4 AND
EXTRACT(month from '2014-01-05'::date)=1 THEN date_part('week',
'2014-01-05'::date) ELSE date_part('week', '2014-01-05'::date + 1)
END;
输出
date_part
-----------
2
(1 row)