PostgreSQL自定义周编号-包含2月1日的第一周 [英] PostgreSQL custom week number - first week containing Feb 1st

查看:67
本文介绍了PostgreSQL自定义周编号-包含2月1日的第一周的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是SQL函数的新手,我试图创建一个显示自定义星期数的日历表,每个星期从星期六开始,到星期五结束.每年的第一周始终包含该年的2月1日.例如,如果特定年份的2月1日星期几是星期二,则该年的第一周是1月29日至2月4日的

I'm new to SQL functions and trying to create a calendar table that displays custom week numbers, with each week starting with Saturday and ending on Friday. The first week of each year always contains Feb.1st of that year. For example, if the day of the week of Feb. 1st for a particular year is Tuesday, then the first week for that year is from Jan. 29 to Feb. 4.

我已经为这个问题苦苦挣扎了几天,我唯一能想到的解决方案如下:

I've been struggling with this problem for a couple days and the only solution I can come up with is as follows:

首先,我创建了一个日历表,该表的列名为"CustomizedWeekNo",以反映从2月1日开始的一周开始的年周期.但是每周的第一天是星期一.

First, I created a calendar table with a column called "CustomizedWeekNo" to reflect the year cycle starting from the week containing Feb. 1st. But the first day of each week is Monday.

Create Table Calendar
(CalendarDate Date, WeekNo smallInt, WeekDayNo text, CustomizedWeekNo smallInt)

Create or Replace Function CustomizeWeekNumber() 
Returns void
as $$
Declare beginDate Date :='2015-01-31'; endDate Date := '2017-01-27';
Begin 
While beginDate <= endDate loop
Insert Into Calendar (CalendarDate, WeekNo, WeekDayNo, CustomizedWeekNo)
Select
beginDate As CalendarDate    
,DATE_PART('week', beginDate::timestamp)::smallint As WeekNo
,(Case When DATE_PART('isodow', beginDate::timestamp)::smallint = 6
          Then 'Sat'
     When DATE_PART('isodow', beginDate::timestamp)::smallint = 7
          Then 'Sun'
     When DATE_PART('isodow', beginDate::timestamp)::smallint = 1
          Then 'Mon'
     When DATE_PART('isodow', beginDate::timestamp)::smallint = 2
          Then 'Tue'
     When DATE_PART('isodow', beginDate::timestamp)::smallint = 3
          Then 'Wed'
     When DATE_PART('isodow', beginDate::timestamp)::smallint = 4
          Then 'Thur'
     Else 'Fri'
     End) As WeekDayNo;
,(Case When beginDate < '2016-01-04' 
          Then DATE_PART('week', beginDate::timestamp)::smallint - 5 
     When beginDate >= '2016-01-04' and beginDate < '2016-01-30' 
          Then (date_part('week', '2016-01-03'::timestamp)::smallint - 5 + date_part('week', beginDate::timestamp)::smallint)
     When beginDate >= '2016-01-30' and beginDate < '2017-01-02'
          Then date_part('week', beginDate::timestamp)::smallint - 4
     Else
          date_part('week', '2017-01-01'::timestamp)::smallint - 4 + date_part('week', beginDate::timestamp)::smallint
     End) As CustomizedWeekNo; 
Select (beginDate + interval'1 day') into beginDate;
End loop;
End; $$
language plpgsql;  

# Run the function
select CustomizeWeekNumber()

接下来,我更新"CustomizedWeekNo"列

Next, I update the "CustomizedWeekNo" column

-- My customized week starts from every Saturday and ends on every Friday
 update calendar
 set CustomizedWeekNo = CustomizedWeekNo + 1 
 where WeekDayNo in ('Sat', 'Sun');

最后,我创建了另一个函数来返回所需的信息.我还重新格式化了"CustomizedWeekNo"的值,以包括特定的年份.

Lastly, I create another function to return the information I need. I also reformat the value of the "CustomizedWeekNo" to include the specific year.

create or replace function update_CustomizedWeek(date, date)
returns table(Calendar_Date Date, Week_No int, WeekDay_No text, Customized_Week_No int)
as $$
begin
  return query
  select t.CalendarDate, t.WeekNo, t.WeekDayNo, 
    case when t.CustomizedWeekNo <= 9 
         then (date_part('year', t.CalendarDate::timestamp)::text||'0'||t.CustomizedWeekNo::text)::int 
    else (date_part('year', t.CalendarDate::timestamp)::text||t.CustomizedWeekNo::text)::int 
    end
  from Calendar t 
  where t.CalendarDate >= $1 and t.CalendarDate <= $2
  order by t.CalendarDate;
end; $$
language plpgsql;

--Example
select * from update_CustomizedWeek('2015-01-30', '2015-02-10')

最终结果将如下所示:

Calendar_Date | Week_No | WeekDay_No | Customized_Week_No
------------- | ------- | ---------- | -------------------
2015-01-31    |   5     |   Sat      |  201501
2015-02-01    |   5     |   Sun      |  201501
2015-02-02    |   6     |   Mon      |  201501
2015-02-03    |   6     |   Tue      |  201501
2015-02-04    |   6     |   Wed      |  201501
2015-02-05    |   6     |   Thur     |  201501
2015-02-06    |   6     |   Fri      |  201501
2015-02-07    |   6     |   Sat      |  201502
2015-02-08    |   6     |   Sun      |  201502
2015-02-09    |   7     |   Mon      |  201502
2015-02-10    |   7     |   Tue      |  201502

如您所见,我在这里使用了很多硬编码".我希望能够生成日期范围以及任何年份(不仅是2016或2017)的自定义星期数.非常感谢您的帮助.

As you can see, I used a lot of "hard coding" here. I would like to be able to generate a date range along with the customized week number for any year, not just 2016 or 2017. Any help is really appreciated.

推荐答案

date_trunc() 会截断至上一个星期一.您仍然可以通过在输入中添加2天(周六和周一之间的差),然后从输出中减去2天来使用它.完美运行.

date_trunc() truncates to the previous Monday. You can still use it by adding 2 days to the input (the difference between Sat and Mon), then subtract 2 days from the output. Works perfectly.

此查询精确地产生您想要的输出 :

This query produces your desired output exactly:

SELECT d::date                     AS "Calendar_Date"
     , EXTRACT('WEEK' FROM d)::int AS "Week_No"
     , to_char(d, 'Dy')            AS "WeekDay_No"
     , base_nr + (rn::int - 1) / 7 AS "Customized_Week_No"
FROM  (
   SELECT date_trunc('week', feb3) - interval '2 days' AS day1 -- subtract 2 days
        , EXTRACT('year' FROM feb3)::int * 100 + 1     AS base_nr
   FROM  (SELECT timestamp '2015-02-03') input(feb3)           -- add 2 days, so Feb 3 (!)
   ) t, generate_series (day1
                       , day1 + interval '1 year - 1 day' 
                       , interval '1 day') WITH ORDINALITY AS d(d, rn);

只需提供相应年份的2月3日: timestamp'2015-02-03'.

Just provide Feb 3 of the respective year: timestamp '2015-02-03'.

这篇关于PostgreSQL自定义周编号-包含2月1日的第一周的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆