如何使用具有开始日期和结束日期的数据集添加日期?

时间:2017-08-27 02:04:38

标签: postgresql date range

您好我是SQL的初学者,尤其是postgresql。 我有一张看起来像这样的表:

ID    | Entity   | Startdate | enddate
------| ------   | ------    | ------ 
1     | Hospital |2013-01-01 |2013-01-31
1     | Clinic   |2013-02-01 |2013-04-30
1     | Hospital |2013-05-01 |2013-05-31

在这种情况下,我想做的是,开始和结束日期超过一个月,以便将其分解,所以上表是这样的:

ID    | Entity   | Startdate | enddate
------| ------   | ------    | ------
1     | Hospital |2013-01-01 |2013-01-31
1     | Clinic   |2013-02-01 |2013-02-29
1     | Clinic   |2013-03-01 |2013-03-31
1     | Clinic   |2013-04-01 |2013-04-30
1     | Hospital |2013-05-01 |2013-05-31

如果您注意到第2,3和4行已按月分解,并且ID和实体也已重复。

有关如何在postgresql中运行此任何建议将不胜感激。

P.S道歉我正试图弄清楚如何正确创建上面的表格。遇到困难时,数字和单词之间的管道是表格中的线条。 希望它不要太混乱。

2 个答案:

答案 0 :(得分:0)

这样做的一种方法是创建一个end_of_month函数,如下所示:

CREATE FUNCTION end_of_month(date)
  RETURNS date AS
$BODY$
select (date_trunc('month', $1) + interval '1 month' - interval '1 day')::date;
$BODY$
  LANGUAGE sql IMMUTABLE STRICT
  COST 100;

然后你可以得到一个像这样的UNIONS字符串:

SELECT 
        id, 
        entity, 
        startdate, 
        least(end_of_month(startdate),enddate) enddate 
from hospital
union
SELECT 
        id, 
        entity, 
        startdate, 
        least(end_of_month((startdate + interval '1 month')::date),enddate) enddate 
        from hospital
union
        id, 
        entity, 
        startdate, 
        least(end_of_month((startdate + interval '1 month')::date),enddate) enddate 
        from hospital
ORDER BY startdate,enddate

这种方法的问题在于你需要拥有尽可能多的工会!

另一种方法是使用游标。

编辑

想到另一个(更好的)非游标解决方案。创建月末日期表。然后你可以简单地做:

select h.id, 
       h.entity, 
       h.startdate, 
       least(h.enddate, m.enddate) enddate
from hospital h 
INNER JOIN monthends m 
ON m.enddate > h.startdate and m.enddate <= end_of_month(h.enddate) 
ORDER BY startdate, enddate

答案 1 :(得分:0)

以下是如何根据数据克隆行的示例:

-- Demo data begin
with t(i,x,y) as (values
  (1, '2013-02-03'::date, '2013-04-27'::date),
  (2, current_date, current_date))
-- Demo data end
select
  *,
  greatest(x, z)::date as x1, least(y, z + '1 month - 1 day'::interval)::date as y1
from
  t,
  generate_series(date_trunc('month', x)::date, date_trunc('month', y)::date, '1 month') as z;
┌───┬────────────┬────────────┬────────────────────────┬────────────┬────────────┐
│ i │     x      │     y      │           z            │     x1     │     y1     │
╞═══╪════════════╪════════════╪════════════════════════╪════════════╪════════════╡
│ 1 │ 2013-02-03 │ 2013-04-27 │ 2013-02-01 00:00:00+02 │ 2013-02-03 │ 2013-02-28 │
│ 1 │ 2013-02-03 │ 2013-04-27 │ 2013-03-01 00:00:00+02 │ 2013-03-01 │ 2013-03-31 │
│ 1 │ 2013-02-03 │ 2013-04-27 │ 2013-04-01 00:00:00+03 │ 2013-04-01 │ 2013-04-27 │
│ 2 │ 2017-08-27 │ 2017-08-27 │ 2017-08-01 00:00:00+03 │ 2017-08-27 │ 2017-08-27 │
└───┴────────────┴────────────┴────────────────────────┴────────────┴────────────┘

只需删除Demo data阻止,然后用您的表/列名称替换txy

说明:

least()greatest()函数相应地返回最小和最大元素。 Link

generate_series(v1,v2,d)函数会返回以v1开头的一系列值,而不是v2与步骤d的最大值。 Link

'1 month - 1 day'::interval - interval数据类型表示法,<value>::<datatype>表示显式类型转换,SQL标准等效项为cast(<value> as <datatype>)Linklink

date_trunc()函数将日期/时间戳值截断为指定的精度。 Link