【问题标题】:How to fill in missing month data in an Oracle query如何在 Oracle 查询中填写缺失的月份数据
【发布时间】:2015-09-15 14:50:30
【问题描述】:

这与Mysql to select month-wise record even if data not existHow to fill in missing months? 有关,但与Oracle 相关,并带有其他数据。我想使用 Crystal Reports 交叉表,但数据需要为零才能获取月份的列(请参阅Keeping same number of columns at cross tab report

我的查询返回诸如

之类的数据
  NewRate   OldRate   Month   Count   
   Rate1     Rate2     8        1  
   Rate1     Rate3     2        3
   Rate1     Rate3     3        2
   Rate1     Rate3     7        2
   Rate1     Rate3     8       12
   Rate3     Rate1     1        1
   Rate3     Rate1     2        1
   Rate3     Rate1     5        1
   Rate3     Rate1     7        3
   Rate3     Rate1     8        9

我想为每个 NewRate 创建一个交叉表,但为了获得每个月的列,我需要返回额外的行,其中包含每个 NewRate 值和所有月份的记录。所以,我需要一个查询来获取以下附加记录(OldRate 只是每个 NewRate 的选项之一)

   NewRate   OldRate   Month   Count   
   Rate1     Rate2     1        0  
   Rate1     Rate2     2        0
   Rate1     Rate2     3        0
   Rate1     Rate2     4        0
   Rate1     Rate2     5        0
   Rate1     Rate2     6        0
   Rate1     Rate2     7        0
   Rate1     Rate2     9        0
   Rate1     Rate2    10        0
   Rate1     Rate2    11        0
   Rate1     Rate2    12        0
   Rate3     Rate1     3        0
   Rate3     Rate1     4        0
   Rate3     Rate1     6        0
   Rate3     Rate1     9        0
   Rate3     Rate1    10        0
   Rate3     Rate1    11        0
   Rate3     Rate1    12        0

我当前的查询有具体的日期,而不仅仅是月份,这可能会更好一些。我愿意使用 2015 年 1 月 1 日、2015 年 2 月 1 日等日期,如果已经有 1 月 5 日,那么添加一个 0 计数的 1 月 1 日不会有任何影响。

但是,我不想为不存在的费率分组添加记录。将 Rate1 到 Rate2 和 Rate1 到 Rate3 都用零就可以了。但由于原始数据集没有 Rate3 到 Rate2,我不想添加这些。汇率是 Oracle 过程的参数,日期范围(也是一个参数)默认为当前日历年。

【问题讨论】:

    标签: sql oracle oracle11g


    【解决方案1】:

    您可以为此使用partitioned outer join,这将消除交叉连接。

    感谢@Wolf 提供SQL Fiddle

    查询 1

    with 
    -- Your sample data
    sample_data as (   
       select 'Rate1' NewRate, 'Rate2' OldRate, to_date(8, 'MM') Month,  1 Count from dual union all  
       select 'Rate1', 'Rate3', to_date(2, 'MM'),  3 from dual union all
       select 'Rate1', 'Rate3', to_date(3, 'MM'),  2 from dual union all
       select 'Rate1', 'Rate3', to_date(7, 'MM'),  2 from dual union all
       select 'Rate1', 'Rate3', to_date(8, 'MM'), 12 from dual union all
       select 'Rate3', 'Rate1', to_date(1, 'MM'),  1 from dual union all
       select 'Rate3', 'Rate1', to_date(2, 'MM'),  1 from dual union all
       select 'Rate3', 'Rate1', to_date(5, 'MM'),  1 from dual union all
       select 'Rate3', 'Rate1', to_date(7, 'MM'),  3 from dual union all
       select 'Rate3', 'Rate1', to_date(8, 'MM'),  9 from dual),
    
    -- Using the "with clause" we can generate a set of months as rows
    dense_months as (
       select to_date(level, 'MM') mo 
       from dual
       connect by level <=12)
    
    select 
       sd.newrate, sd.oldrate, dm.mo, nvl(sd.count,0) count
    from sample_data sd
    partition by (sd.newrate, sd.oldrate)
    right outer join dense_months dm 
    on dm.mo = sd.month
    order by 1, 2, 3
    

    Results

    | NEWRATE | OLDRATE |                          MO | COUNT |
    |---------|---------|-----------------------------|-------|
    |   Rate1 |   Rate2 |   January, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate2 |  February, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate2 |     March, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate2 |     April, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate2 |       May, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate2 |      June, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate2 |      July, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate2 |    August, 01 2015 00:00:00 |     1 |
    |   Rate1 |   Rate2 | September, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate2 |   October, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate2 |  November, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate2 |  December, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate3 |   January, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate3 |  February, 01 2015 00:00:00 |     3 |
    |   Rate1 |   Rate3 |     March, 01 2015 00:00:00 |     2 |
    |   Rate1 |   Rate3 |     April, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate3 |       May, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate3 |      June, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate3 |      July, 01 2015 00:00:00 |     2 |
    |   Rate1 |   Rate3 |    August, 01 2015 00:00:00 |    12 |
    |   Rate1 |   Rate3 | September, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate3 |   October, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate3 |  November, 01 2015 00:00:00 |     0 |
    |   Rate1 |   Rate3 |  December, 01 2015 00:00:00 |     0 |
    |   Rate3 |   Rate1 |   January, 01 2015 00:00:00 |     1 |
    |   Rate3 |   Rate1 |  February, 01 2015 00:00:00 |     1 |
    |   Rate3 |   Rate1 |     March, 01 2015 00:00:00 |     0 |
    |   Rate3 |   Rate1 |     April, 01 2015 00:00:00 |     0 |
    |   Rate3 |   Rate1 |       May, 01 2015 00:00:00 |     1 |
    |   Rate3 |   Rate1 |      June, 01 2015 00:00:00 |     0 |
    |   Rate3 |   Rate1 |      July, 01 2015 00:00:00 |     3 |
    |   Rate3 |   Rate1 |    August, 01 2015 00:00:00 |     9 |
    |   Rate3 |   Rate1 | September, 01 2015 00:00:00 |     0 |
    |   Rate3 |   Rate1 |   October, 01 2015 00:00:00 |     0 |
    |   Rate3 |   Rate1 |  November, 01 2015 00:00:00 |     0 |
    |   Rate3 |   Rate1 |  December, 01 2015 00:00:00 |     0 |
    

    【讨论】:

      【解决方案2】:

      您正在寻找加密您的数据,并且有很多关于如何做到这一点的帖子和答案。本质上,您需要生成一组所有可能的行,然后将您的实际数据左外连接到理论集。

      在本例中,我使用CONNECT BY 子句生成一组月份。您提到您的实际数据集使用实际日期,因此我在此示例中使用日期类型。

      select to_date(level, 'MM') mo 
      from dual
      connect by level <=12
      

      我在 WITH 子句中具体化这些日期(连同您的示例数据),但您也可以在内联视图中执行相同操作。

      然后在主查询中,您可以获取这些密集日期并将它们与唯一的汇率组合集交叉连接,以创建一组实际汇率集和日期的所有可能组合。为此,我们然后LEFT OUTER JOIN您的实际数据,用您默认的0 填充缺失的计数。

      with 
      -- Your sample data
      sample_data as (   
         select 'Rate1' NewRate, 'Rate2' OldRate, to_date(8, 'MM') Month,  1 Count from dual union all  
         select 'Rate1', 'Rate3', to_date(2, 'MM'),  3 from dual union all
         select 'Rate1', 'Rate3', to_date(3, 'MM'),  2 from dual union all
         select 'Rate1', 'Rate3', to_date(7, 'MM'),  2 from dual union all
         select 'Rate1', 'Rate3', to_date(8, 'MM'), 12 from dual union all
         select 'Rate3', 'Rate1', to_date(1, 'MM'),  1 from dual union all
         select 'Rate3', 'Rate1', to_date(2, 'MM'),  1 from dual union all
         select 'Rate3', 'Rate1', to_date(5, 'MM'),  1 from dual union all
         select 'Rate3', 'Rate1', to_date(7, 'MM'),  3 from dual union all
         select 'Rate3', 'Rate1', to_date(8, 'MM'),  9 from dual),
      
      -- Using the "with clause" we can generate a set of months as rows
      dense_months as (
         select to_date(level, 'MM') mo 
         from dual
         connect by level <=12)
      
      select 
         rg.newrate, rg.oldrate, dm.mo, nvl(sd.count,0) count
      -- Here we are creating a cartesian product of your rate groups and twelve calendar months
      from dense_months dm
      cross join 
        (select distinct newrate, oldrate
         from sample_data) rg
      -- Then we can left join our actual data to the cartesian product.
      left outer join sample_data sd on rg.newrate = sd.newrate and rg.oldrate = sd.oldrate and dm.mo = sd.month
      order by 1, 2, 3;
      

      这是一个SQL Fiddle 工作示例。

      【讨论】:

      • 我只想大声疾呼并支持partitioned outer join 的建议,根据@Noel 在下面的回答。这是一个很好的简化,坦率地说,当我写回复时,它让我忘记了(我可能很着急)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-16
      • 2021-06-11
      • 2016-05-11
      • 1970-01-01
      • 2018-01-23
      • 1970-01-01
      相关资源
      最近更新 更多