2017-08-27 118 views
0

你好,我是一个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道歉我试图弄清楚如何正确地创建上述表格。困难的是,数字和单词之间的管道是表格中的线条。 希望它不要太混乱。要做到这一点

回答

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; 

然后你就可以有工会这样的字符串:

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 
+0

monthends可能是使用generate_series进行的CTE。 – Jasen

+0

@Jasen是的,你可以做到这一点。感谢您的建议。最后,在永久性表格的使用和执行速度之间实际上是一种折衷。 –

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步骤dLink

'1 month - 1 day'::interval - interval数据类型的符号,<value>::<datatype>意味着显式类型铸造,SQL标准当量cast(<value> as <datatype>)Linklink

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

相关问题