【问题标题】:How do I write a query to display the months between two dates?如何编写查询以显示两个日期之间的月份?
【发布时间】:2016-05-12 16:24:50
【问题描述】:

我有一个 ID 数据库,其收入和开始/结束日期如下,但在给定的开始/结束日期范围内,我无法打破每个 ID 每月的收入。

表格数据示例如下:

ID | INCOME | START_DATE | END_DATE
 1 |   2000 | 02/01/2016 | 05/31/2016
 1 |   1500 | 12/01/2015 | 01/31/2016
 2 |   1000 | 01/01/2016 | 04/30/2016

结果应该是:

ID | INCOME | MONTH
 1 |   2000 | 05/2016
 1 |   2000 | 04/2016
 1 |   2000 | 03/2016
 1 |   2000 | 02/2016
 1 |   1500 | 01/2016
 1 |   1500 | 12/2015
 2 |   1000 | 04/2016
 2 |   1000 | 03/2016
 2 |   1000 | 02/2016
 2 |   1000 | 01/2016

我将如何编写 Oracle SQL 以便能够有效地产生上述结果(假设表有数千个唯一 ID)?

【问题讨论】:

  • 什么版本的Oracle数据库?
  • ?当前的Oracle版本是12,你确定不是Oracle 3?要查找您的版本,请运行 select * from v$version;
  • 11.2.0.4.0 我相信,11g。对不起。
  • 那么好消息 - 您可以使用收到的两个答案中的任何一个。如果您仔细研究它们,您将学习两种不同的技术,您将来可能会使用它们。是的,您的 Oracle 版本是 11.2,更具体地说是 11.2.0.4.0; 11g 是 Oracle 使用的营销名称,但无论何时发布它都有助于添加您使用的是 11.2 或更高版本,即 11.2.0.4。

标签: sql oracle


【解决方案1】:

您可以使用 connect by 来执行此操作,如下所示:

with sample_data as (select 1 id, 2000 income, to_date('01/02/2016', 'dd/mm/yyyy') start_date, to_date('31/05/2016', 'dd/mm/yyyy') end_date from dual union all
                     select 1 id, 1500 income, to_date('01/12/2015', 'dd/mm/yyyy') start_date, to_date('31/01/2016', 'dd/mm/yyyy') end_date from dual union all
                     select 2 id, 1000 income, to_date('01/01/2016', 'dd/mm/yyyy') start_date, to_date('30/04/2016', 'dd/mm/yyyy') end_date from dual)
select id,
       income,
       add_months(trunc(start_date, 'mm'), -1 + level) mnth
from   sample_data
connect by prior id = id
           and prior income = income
           and prior sys_guid() is not null
           and add_months(trunc(start_date, 'mm'), -1 + level) <= trunc(end_date, 'mm')
order by id, income desc, mnth desc;


        ID     INCOME MNTH     
---------- ---------- ---------
         1       2000 01-MAY-16
         1       2000 01-APR-16
         1       2000 01-MAR-16
         1       2000 01-FEB-16
         1       1500 01-JAN-16
         1       1500 01-DEC-15
         2       1000 01-APR-16
         2       1000 01-MAR-16
         2       1000 01-FEB-16
         2       1000 01-JAN-16

【讨论】:

  • "prior sys_guid() is not null"的目的是什么?
  • @Matt 当有多行要处理时,它会阻止 Oracle 处理超出必要的行如果你排除它,你会得到很多你(可能)不想要的额外行.
【解决方案2】:

如果您使用的是 11gR2 或更高版本,则可以使用 recursive subquery factoring

with r (id, income, this_date, end_date) as (
  select id, income, trunc(start_date, 'MM'), trunc(end_date, 'MM')
  from your_table
  union all
  select id, income, this_date + interval '1' month, end_date
  from r
  where end_date > this_date
)
select id, income, to_char(this_date, 'MM/YYYY') as month
from r
order by id, this_date desc;

        ID     INCOME MONTH 
---------- ---------- -------
         1       2000 05/2016
         1       2000 04/2016
         1       2000 03/2016
         1       2000 02/2016
         1       1500 01/2016
         1       1500 12/2015
         2       1000 04/2016
         2       1000 03/2016
         2       1000 02/2016
         2       1000 01/2016

锚成员获取起始信息 - 我将其截断到月初,可能是多余的,但以防万一在该月开始的时间太晚而导致间隔添加出现问题。然后,递归成员不断向每个现有成员添加一个月,直到它到达结束日期。

【讨论】:

  • 如果你有很多数据检查哪个表现更好;我怀疑 connect-by 会稍微快一些,尽管它可能会因大量数据而失败。 (好吧,我想,在某个时候也可以!)
猜你喜欢
  • 2019-03-28
  • 2012-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多