如何在两个给定日期之间将一行拆分为多行?

How to split a row into multiple rows between two given dates?

如何用

拆分一行
Start Date : 02-OCT-2015
End Date   : 31-DEC-2015

在 Oracle 中进入下面的行?

02-OCT-2015 31-OCT-2015
01-NOV-2015 30-NOV-2015
01-DEC-2015 31-DEC-2015
SELECT SUBSTR(t.column_one, 1, INSTR(t.column_one, ' ')-1) AS col_one,
SUBSTR(t.column_one, INSTR(t.column_one, ' ')+1) AS col_two
FROM YOUR_TABLE t

或者 您可以使用正则表达式或子字符串函数。它在非常大的数据集上不会很快,但它会完成工作。作为 -

SELECT REGEXP_SUBSTR(t.column_one, '[^-]+', 1, 1) col_one,
REGEXP_SUBSTR(t.column_one, '[^-]+', 1, 2) col_two,
REGEXP_SUBSTR(t.column_one, '[^-]+', 1, 3) col_three,
FROM YOUR_TABLE t;

希望这对您有所帮助。

您想要的 DATE 算法 可以使用以下方法完成:

  • ADD_MONTHS
  • LAST_DAY
  • TRUNC
  • CONNECT BY 即典型的行生成器
  • CASE表达式

假设您有两个日期 startend 日期,以下查询将 将日期拆分为基于 MONTHS.

的多行
SQL> WITH sample_data AS
  2    (SELECT DATE '2015-10-02' Start_Date, DATE '2015-12-25' End_Date FROM DUAL)
  3  -- end of sample_date to mock an actual table
  4  SELECT CASE
  5         WHEN start_date >= TRUNC(add_months(start_date,COLUMN_VALUE - 1),'MM')
  6         THEN
  7            TO_CHAR(start_date, 'YYYY-MM-DD')
  8         ELSE
  9            TO_CHAR(TRUNC(add_months(start_date,COLUMN_VALUE - 1),'MM'),'YYYY-MM-DD')
 10         END new_start_date,
 11         CASE
 12         WHEN end_date <= last_day(TRUNC(add_months(start_date,COLUMN_VALUE - 1),'MM'))
 13         THEN
 14            TO_CHAR(end_date, 'YYYY-MM-DD')
 15         ELSE
 16            TO_CHAR(last_day(TRUNC(add_months(start_date,COLUMN_VALUE - 1),'MM')),
 17                    'YYYY-MM-DD')
 18         END new_end_date
 19  FROM sample_data,
 20    TABLE(
 21          CAST(
 22               MULTISET
 23                       (SELECT LEVEL
 24                        FROM dual
 25               CONNECT BY add_months(TRUNC(start_date,'MM'),LEVEL - 1) <= end_date
 26                       ) AS sys.OdciNumberList
 27              )
 28         )
 29  ORDER BY column_value;

NEW_START_DATE NEW_END_DATE
-------------- ------------
2015-10-02     2015-10-31
2015-11-01     2015-11-30
2015-12-01     2015-12-25

查询的工作原理:

如果您应该了解如何使用 CONNECT BY 子句 生成行,剩下的就是简单的 DATE 算法。

  • TRUNC(date, 'MM') 给出该月的第一天,在您的情况下成为开始日期。

  • ADD_MONTHS(date, value) 中指定的日期添加尽可能多的月份。

  • LAST_DAY 给出该月的最后一天,在您的情况下成为结束日期。

所以你有你想要比较的范围:第一个范围是你感兴趣的时期(2015 年 10 月 2 日到 2015 年 12 月 31 日)其他范围是月份(2015 年 1 月 1 日到 31 -OCT-2015)...是的,我知道您只想要 02-OCT-2015 因为您的数据,但让我们一次做一件事...我们会到达那里!

你感兴趣的时期从今天开始进入未来,所以你的答案需要展望未来。

我就是这样做的:

第 1 步: 列出月份,定义每个月的开始日期和结束日期: 这可以是 table 或视图。我发表看法。

create view mymonths as 
select 
  add_months(trunc(sysdate,'MONTH'), - rownum + 2) month_start,
  add_months(trunc(sysdate,'MONTH'), - rownum + 3) -1 month_end,
from all_objects -- (or any table or view with enough rows)
where rownum < 13
-- today is 10-NOV-2015. 
-- First row will month_start = 01-NOV-2015, minus 1 (the rownum) month = 01-OCT-2015 + 2 = 01-DEC-2015 and month_end = 31-DEC-2015 (find the next month_start, minus a day) 
-- Next row = 01-NOV-2015 and 30-NOV-2015
-- Next row = 01-OCT-2015 and 31-OCT-2015...for 12 rows down
-- adjust the +3 and < 13 as necessary for the scope of your data

现在我们需要准备好数据以 'join' 与我们的 month_starts 和 month_ends:

如果列数据如下所示: 开始日期:2015 年 10 月 2 日结束日期:2015 年 12 月 31 日 然后按照回复中的建议,我们可以使用substr来获取数据部分。

为了证明思路,我们做一个这样的小视图:

create view mydata as
select 
   to_date(substr(column,13,11),'dd-MON-yyyy') period_start,
   to_date(substr(column,35,11),'dd-MON-yyyy') period_end
from MYTABLE

现在我们可以 'join' 像这样:

Select
   mydata.period_start, mydata.period_end,
   mymonths.month_start, mymonths.month_end
From
   mytable, mymonths
Where
   -- this is tricky, but work it out: it's right...
   mydata.period_start <= mymonths.month_end 
   and
   mydata.period_end => my_months.month_start

-- 查看结果,我们就快完成了。但是你想要 2015 年 10 月 2 日到 2015 年 10 月 31 日,或者说 period_start 或月开始 - 以最晚者为准,在月末,我们会说 period_end 或月末, 谁是最早的:

Select
   case
     when mydata.period_start > mymonths.month_start
       then mydata.period_start 
     else mymonths.month_start
   end COL1,
   case
     when mydata.period_end < mymonths.month_end
       then mydata.period_end
     else mymonths.month_end
   end COL2
From
   mytable, mymonths
Where
   -- this is tricky, but work it out: it's right...
   mydata.period_start <= mymonths.month_end 
   and
   mydata.period_end => my_months.month_start