使用postgresql中的月份或年份的天数定义自定义周

时间:2017-01-17 00:37:49

标签: postgresql

我正在运行一个简单的查询,以便从我们的销售中获得每周收入

SELECT date_trunc('week', payment_date) AS week, sum(payment_amount)  
FROM payment 
WHERE payment_date BETWEEN '2010-jan-1' AND '2016-dec-31' 
GROUP BY week  

现在我需要每周的开始和结束日期为静态。一年中所有52周都需要计算,例如

Week 1: Jan 1-7
Week 2: Jan8-14
Week 3: Jan15-21
Week 4: Jan22-28
Week 5: Jan29-Feb4 and so forth

我做了一些调查并发现我需要使用payment_date作为参数的用户定义函数并返回一周值。然后,我可以在上面的SQL查询中调用此函数,代替date_trunc()函数。

如何使用增量循环为payment_date分配周值? 我还可以在SQL查询的group by子句中使用此返回值吗? 由于我具有基本的SQL中间知识,因此我将高度赞赏一些详细示例的解释。

---------------编辑--------------

我现在尝试使用2个功能来考虑闰年,我仍然希望将3月4日纳入第9周。香港专业教育学院尝试使用& klin的功能并将其转换为SQL,我一直在" int'在第9行。我的代码如下。

创建或替换函数is_leap_year(int)

返回布尔语言sql为$$

select $1 % 4 = 0 and ($1 % 100 <> 0 or $1 % 400 = 0)

$$;

创建或替换函数week_no(timestamp)

将int language sql作为$ body $

返回

声明     y int;     day_shift int; 开始

y = extract(year from $1);

    day_shift = 1 + (is_leap_year(y) and $1 > make_date(y, 2, 28))::int;
    return ((extract(doy from $1)::int)- day_shift) / 7+ 1;

$ $体;

选择week_no(payment_date)作为week_number,sum(payment_amount)

从付款p加入pay_event pe on p.payment_event_id = pe.payment_event_id

其中payment_date介于&#39; 2016-jan-1&#39;和&#39; 2017-jan-1&#39; 和pe.payment_event_type_id!= 2

按周数分组

按week_number排序

3 个答案:

答案 0 :(得分:1)

首先,您的要求存在问题。

  

现在我需要每周的开始和结束日期为静态。

他们不可能。闰年发生了。 2月29日将每四年中的开始和结束日期改变一年,或者您需要允许一周有八天。

  

一年中所有52周都需要定制。 。 。

我认为你的意思是所有52周都需要计算。但是52 * 7 = 364.你错过了一天。

我认为从日期计算周数的最简单表达式是(extract(doy from payment_date)::integer / 7) as week。我不知道是否值得将它放入一个函数中。相反,我可能首先创建一个使用该表达式的视图。

但计算不会对2月29日做任何特别的事情,或者每年有超过52 * 7天的事实。

我认为你最好的选择是建立一个表而不是使用计算。

create table weeks (
  calendar_date date primary key,
  week_num integer not null 
    check (week_num between 1 and 53)
);

使用此日期填充2016年和2017年,并使用计算周,为我们提供一个起点。 (2016年是闰年。)

insert into weeks 
select 
    ('2016-01-01'::date + (n || ' days')::interval)::date as calendar_date
  , extract(doy from ('2016-01-01'::date + (n || ' days')::interval)::date)::integer / 7 + 1 as calencar_week
from generate_series (0, 730) n;

让我们看看第9周。

select * 
from weeks
where week_num = 9
order by calendar_date;
calendar_date  week_num
--
2016-02-25     9
2016-02-26     9
2016-02-27     9
2016-02-28     9
2016-02-29     9
2016-03-01     9
2016-03-02     9

2017-02-25     9
2017-02-26     9
2017-02-27     9
2017-02-28     9
2017-03-01     9
2017-03-02     9
2017-03-03     9

2016年,计算第9周从2016-02-25到2016-03-02。 2017年,它从2016-02-25到2017-03-03。但是现在所有这些周数都在一个表中,你可以按照自己喜欢的方式调整它们。如果有意义的话,你甚至可以每年更改一次调整。

答案 1 :(得分:0)

通过将问题分解为月 - 日字符串,您可以在多年内使用相同的逻辑

mysql> SELECT "01-07" < "01-08";
+-------------------+
| "01-07" < "01-08" |
+-------------------+
|                 1 |
+-------------------+
1 row in set (0.08 sec)

%m-%d的简单日期格式用于将付款日期与您要分配的星期数据库进行比较。

要手动分配所有52周范围,您可以使用案例陈述:

SET @md_format="%m-%d";

SELECT
  CASE
    WHEN (date_format(`input_date`, @md_format) < "01-08") THEN 1
    WHEN (date_format(`input_date`, @md_format) < "01-15") THEN 2
    WHEN (date_format(`input_date`, @md_format) < "01-22") THEN 3
    -- ... All other cases
    ELSE 52
  END;

See the docs for the syntax to define a function

函数将允许您执行以下操作:

SELECT week_bucket(payment_date) `week`, SUM(revenue) `revenue`
FROM my_table
WHERE week_bucket(payment_date) > 13
  AND week_bucket(payment_date) < 15
GROUP BY `week`;

答案 2 :(得分:0)

以这样的方式使用doy(一年中的某一天):

create or replace function week_no(date)
returns int language sql as $$
    select ((extract(doy from $1)::int)- 1) / 7+ 1
$$;

with the_table(a_date) as (
values
    ('2017-01-01'::date),
    ('2017-01-07'),
    ('2017-01-08'),
    ('2017-01-14'),
    ('2017-01-15'),
    ('2017-01-22')
)

select extract(doy from a_date)::int as doy, week_no(a_date)
from the_table;

 doy | week_no 
-----+---------
   1 |       1
   7 |       1
   8 |       2
  14 |       2
  15 |       3
  22 |       4
(6 rows)

如果你想更正周数,以便3月4日总是在第9周(即使在闰年),使用这个方便的功能:

create or replace function is_leap_year(int)
returns boolean language sql as $$
    select $1 % 4 = 0 and ($1 % 100 <> 0 or $1 % 400 = 0)
$$;

您的函数可能看起来像这样(我使用了plpgsql语言以获得更好的可读性,尽管这也可以编码为sql函数):

create or replace function week_no_corrected(date)
returns int language plpgsql as $$
declare
    y int = extract (year from $1);
    day_shift int = 1 + (is_leap_year(y) and $1 > make_date(y, 2, 28))::int;
begin
    return ((extract(doy from $1)::int)- day_shift) / 7+ 1;
end;
$$;

with the_table(a_date) as (
values
    ('2016-03-03'::date),
    ('2016-03-04'),
    ('2016-03-05'),
    ('2017-03-03'),
    ('2017-03-04'),
    ('2017-03-05')
)

select a_date, week_no(a_date), week_no_corrected(a_date)
from the_table;

   a_date   | week_no | week_no_corrected 
------------+---------+-------------------
 2016-03-03 |       9 |                 9
 2016-03-04 |      10 |                 9
 2016-03-05 |      10 |                10
 2017-03-03 |       9 |                 9
 2017-03-04 |       9 |                 9
 2017-03-05 |      10 |                10
(6 rows)

在SQL函数中,您不能使用变量,分配可能会被派生表替换:

create or replace function week_no_corrected(date)
returns int language sql as $$
    select ((extract(doy from $1)::int)- day_shift) / 7 + 1
    from (
        select 1 + (is_leap_year(y) and $1 > make_date(y, 2, 28))::int as day_shift
        from (
            select extract (year from $1)::int as y
        ) s
    ) s
$$;