简体   繁体   English

如何在Oracle查询中填写缺少的月份数据

[英]How to fill in missing month data in an Oracle query

This is related to Mysql to select month-wise record even if data not exist and How to fill in missing months? 这与Mysql有关, 即使数据不存在也可以选择按月记录,以及如何填写缺少的月份? , but for Oracle and with additional data. ,但适用于Oracle和其他数据。 I want to use a Crystal Reports Crosstab, but need zeros for data in order to get columns for the months (see Keeping same number of columns at cross tab report ) 我想使用Crystal Reports交叉表,但需要零数据才能获取月份的列(请参阅在交叉表报表中保持相同的列数

My query returns data such as 我的查询返回的数据如

  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

I want to make a crosstab for each NewRate, but in order to get a column for each month, I need to have additional rows returned, with a record for each NewRate value and all months. 我想为每个NewRate创建一个交叉表,但是为了获得每个月的列,我需要返回其他行,并为每个NewRate值和所有月份记录。 So, I'd need a query to get me the following additional records (with OldRate being just one of the options for each 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

My current query has specific dates, not just the month number, which probably works a bit better. 我当前的查询具有特定的日期,而不仅仅是月份号,这可能会更好一些。 I'm willing to use dates such as 1-1-2015, 2-1-2015, etc, and if there is already a Jan 5, adding a Jan 1 with a 0 count won't hurt. 我愿意使用诸如1-1-2015、2-1-2015等日期,如果已经有1月5日,则添加1且计数为0不会影响1月1日。

But, I don't want to add records for Rate groupings that don't exist. 但是,我不想为不存在的费率分组添加记录。 It would be fine to have a Rate1 to Rate2 and Rate1 to Rate3, all with zeros. 将Rate1更改为Rate2,将Rate1更改为Rate3都为零会很好。 But since the original data set doesn't have a Rate3 to Rate2, I wouldn't want to add those. 但是由于原始数据集没有Rate3到Rate2,所以我不想添加这些数据。 The rates are parameters to the Oracle procedure, and the date range (also a parameter) defaults to the current calendar year. 费率是Oracle过程的参数,日期范围(也是一个参数)默认为当前日历年。

You can use partitioned outer join for this, that will eliminate the cross join. 您可以为此使用分区外部联接 ,这将消除交叉联接。

Thanks @Wolf for the SQL Fiddle 感谢@Wolf提供的SQL Fiddle

Query 1 : 查询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 |

You are looking to densify your data, and there are many posts and answers on how to do this. 您正在寻找使数据更致密的方法,有关此操作的文章和答案很多。 Essentially you need to generate a set of all possible rows, and then left outer join your actual data to the theoretical set. 本质上,您需要生成一组所有可能的行,然后将外部实际数据与理论集进行外部连接。

In this example, I'm using the CONNECT BY clause to generate a set of months. 在此示例中,我使用CONNECT BY子句来生成一组月份。 You mentioned that your actual data set uses actual dates, so I'm using a date type in this example. 您提到您的实际数据集使用实际日期,因此在此示例中我使用的是日期类型。

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

I am materializing those dates in a WITH clause (along with your sample data), but you can do the same in an inline view as well. 我正在WITH子句中(与您的示例数据一起)实现这些日期,但是您也可以在嵌入式视图中执行相同的操作。

Then in the main query, you can take those dense dates and cross join them with the unique set of rate combinations to create a set of all possible combinations of actual rate sets and dates. 然后,在主查询中,您可以获取那些密集的日期,并将它们与唯一的比率组合集交叉连接,以创建一组实际比率组和日期的所有可能组合。 To this, we then LEFT OUTER JOIN your actual data, filling in the missing counts with your default of 0 . 为此,我们然后将LEFT OUTER JOIN数据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;

Here is a SQL Fiddle working example. 这是一个SQL Fiddle工作示例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM