Oracle SQL(Toad):展开表

Oracle SQL(Toad):展开表,sql,oracle,Sql,Oracle,假设我有一个名为test的SQL Oracle Toad表,其中包含以下字段和条目日期为dd/mm/yyyy格式: id ref_date value --------------------- 1 01/01/2014 20 1 01/02/2014 25 1 01/06/2014 3 1 01/09/2014 6 2 01/04/2015 7 2 01/08/2015 43 2 01/0

假设我有一个名为test的SQL Oracle Toad表,其中包含以下字段和条目日期为dd/mm/yyyy格式:

id   ref_date    value
---------------------
1    01/01/2014     20
1    01/02/2014     25
1    01/06/2014      3
1    01/09/2014      6
2    01/04/2015      7
2    01/08/2015     43
2    01/09/2015     85
2    01/12/2015      4
我从表格的创建过程中了解到,由于2014年2月和2014年6月存在id=1的值条目,因此2014年3月至5月的值必须为0。对于id=1,同样适用于2014年7月和8月,对于id=2,同样适用于2015年5月至7月和2015年10月至11月

现在,如果我想计算,比方说,给定id的值列的中值,我将无法使用现有的表得出正确的结果-因为每个id缺少5个零条目

因此,我想创建/使用以下可能只是临时表

id   ref_date    value
---------------------
1    01/01/2014     20
1    01/02/2014     25
1    01/03/2014      0
1    01/04/2014      0
1    01/05/2014      0
1    01/06/2014      3
1    01/07/2014      0
1    01/08/2014      0
1    01/09/2014      6
2    01/04/2015      7
2    01/05/2015      0
2    01/06/2015      0
2    01/07/2015      0
2    01/08/2015     43
2    01/09/2015     85
2    01/10/2015      0
2    01/11/2015      0
2    01/12/2015      4
…在此基础上,我可以通过id计算中值:

select id, median(value) as med_value from test group by id
我该怎么做?还是会有别的办法

非常感谢,


如果ref_date是date,而ref_date是second,那么Mr无能

with int1 as (select id
                   , max(ref_date) as max_date
                   , min(ref_date) as min_date from test group by id )
    , s(n) as (select level -1 from dual connect by level <= (select max(months_between(max_date, min_date)) from int1 ) ) 
select i.id
     , add_months(i.min_date,s.n) as ref_date
     , nvl(value,0) as value
 from int1 i 
 join s on add_months(i.min_date,s.n) <= i.max_date
 LEFT join test t on t.id = i.id and add_months(i.min_date,s.n) = t.ref_date
和中位数

with int1 as (select id
                   , max(ref_date) as max_date
                   , min(ref_date) as min_date from test group by id )
    , s(n) as (select level -1 from dual connect by level <= (select max(months_between(max_date, min_date)) from int1 ) ) 
select i.id
     , MEDIAN(nvl(value,0)) as value
 from int1 i 
 join s on add_months(i.min_date,s.n) <= i.max_date
 LEFT join test t on t.id = i.id and add_months(i.min_date,s.n) = t.ref_date
 group by i.id

如果ref_date是date并且是second

with int1 as (select id
                   , max(ref_date) as max_date
                   , min(ref_date) as min_date from test group by id )
    , s(n) as (select level -1 from dual connect by level <= (select max(months_between(max_date, min_date)) from int1 ) ) 
select i.id
     , add_months(i.min_date,s.n) as ref_date
     , nvl(value,0) as value
 from int1 i 
 join s on add_months(i.min_date,s.n) <= i.max_date
 LEFT join test t on t.id = i.id and add_months(i.min_date,s.n) = t.ref_date
和中位数

with int1 as (select id
                   , max(ref_date) as max_date
                   , min(ref_date) as min_date from test group by id )
    , s(n) as (select level -1 from dual connect by level <= (select max(months_between(max_date, min_date)) from int1 ) ) 
select i.id
     , MEDIAN(nvl(value,0)) as value
 from int1 i 
 join s on add_months(i.min_date,s.n) <= i.max_date
 LEFT join test t on t.id = i.id and add_months(i.min_date,s.n) = t.ref_date
 group by i.id

在这个解决方案中,我构建了一个表,其中包含所有需要的日期,并且所有日期的值都为0。然后,我执行union all、group by id和ref_date,而不是join,并在每个组中添加值。如果日期有一行在原始表中有一个值,那么这就是结果值;如果没有,则该值将为0。这样可以避免连接。几乎在所有情况下,union all+聚合都会更快,有时甚至比join快得多

我添加了更多的输入数据以进行更彻底的测试。在你最初的问题中,你有两个id,对于这两个id,你有四个正值。每种情况下缺少五个值,因此将有五个零0,这意味着两种情况下的中值均为0。对于我加上去的id=3,我有三个正值和三个零;中位数是最小正数的一半。对于id=4,我只有一个值,也就是中间值

解决方案特别包括对您的特定问题的回答—如何创建临时表,该临时表很可能根本不需要是临时表,而是一个内联视图。对于With子句中的分解子查询,优化器决定是否将它们视为临时表或内联视图;如果查看解释计划,您可以看到优化器决定了什么

with
     inputs ( id, ref_date, value ) as (
       select 1, to_date('01/01/2014', 'dd/mm/yyyy'), 20 from dual union all
       select 1, to_date('01/02/2014', 'dd/mm/yyyy'), 25 from dual union all
       select 1, to_date('01/06/2014', 'dd/mm/yyyy'),  3 from dual union all
       select 1, to_date('01/09/2014', 'dd/mm/yyyy'),  6 from dual union all
       select 2, to_date('01/04/2015', 'dd/mm/yyyy'),  7 from dual union all
       select 2, to_date('01/08/2015', 'dd/mm/yyyy'), 43 from dual union all
       select 2, to_date('01/09/2015', 'dd/mm/yyyy'), 85 from dual union all
       select 2, to_date('01/12/2015', 'dd/mm/yyyy'),  4 from dual union all
       select 3, to_date('01/01/2016', 'dd/mm/yyyy'), 12 from dual union all
       select 3, to_date('01/03/2016', 'dd/mm/yyyy'), 23 from dual union all
       select 3, to_date('01/06/2016', 'dd/mm/yyyy'),  2 from dual union all
       select 4, to_date('01/11/2014', 'dd/mm/yyyy'),  9 from dual
     ),
-- the "inputs" table constructed above is for testing only,
-- it is not part of the solution.
     ranges ( id, min_date, max_date ) as (
       select id, min(ref_date), max(ref_date)
       from   inputs
       group by id
     ),
     prep ( id, ref_date, value ) as (
       select id, add_months(min_date, level - 1), 0
       from   ranges
       connect by level <= 1 + months_between( max_date, min_date )
              and prior id = id
              and prior sys_guid() is not null
     ),
     v ( id, ref_date, value ) as (
       select   id, ref_date, sum(value)
       from     ( select id, ref_date, value from prep union all
                  select id, ref_date, value from inputs
                )
       group by id, ref_date
     )
select id, median(value) as median_value
from   v
group by id
order by id   -- ORDER BY is optional
;

ID MEDIAN_VALUE
-- ------------
 1            0
 2            0
 3            1
 4            9

在这个解决方案中,我构建了一个表,其中包含所有需要的日期,并且所有日期的值都为0。然后,我执行union all、group by id和ref_date,而不是join,并在每个组中添加值。如果日期有一行在原始表中有一个值,那么这就是结果值;如果没有,则该值将为0。这样可以避免连接。几乎在所有情况下,union all+聚合都会更快,有时甚至比join快得多

我添加了更多的输入数据以进行更彻底的测试。在你最初的问题中,你有两个id,对于这两个id,你有四个正值。每种情况下缺少五个值,因此将有五个零0,这意味着两种情况下的中值均为0。对于我加上去的id=3,我有三个正值和三个零;中位数是最小正数的一半。对于id=4,我只有一个值,也就是中间值

解决方案特别包括对您的特定问题的回答—如何创建临时表,该临时表很可能根本不需要是临时表,而是一个内联视图。对于With子句中的分解子查询,优化器决定是否将它们视为临时表或内联视图;如果查看解释计划,您可以看到优化器决定了什么

with
     inputs ( id, ref_date, value ) as (
       select 1, to_date('01/01/2014', 'dd/mm/yyyy'), 20 from dual union all
       select 1, to_date('01/02/2014', 'dd/mm/yyyy'), 25 from dual union all
       select 1, to_date('01/06/2014', 'dd/mm/yyyy'),  3 from dual union all
       select 1, to_date('01/09/2014', 'dd/mm/yyyy'),  6 from dual union all
       select 2, to_date('01/04/2015', 'dd/mm/yyyy'),  7 from dual union all
       select 2, to_date('01/08/2015', 'dd/mm/yyyy'), 43 from dual union all
       select 2, to_date('01/09/2015', 'dd/mm/yyyy'), 85 from dual union all
       select 2, to_date('01/12/2015', 'dd/mm/yyyy'),  4 from dual union all
       select 3, to_date('01/01/2016', 'dd/mm/yyyy'), 12 from dual union all
       select 3, to_date('01/03/2016', 'dd/mm/yyyy'), 23 from dual union all
       select 3, to_date('01/06/2016', 'dd/mm/yyyy'),  2 from dual union all
       select 4, to_date('01/11/2014', 'dd/mm/yyyy'),  9 from dual
     ),
-- the "inputs" table constructed above is for testing only,
-- it is not part of the solution.
     ranges ( id, min_date, max_date ) as (
       select id, min(ref_date), max(ref_date)
       from   inputs
       group by id
     ),
     prep ( id, ref_date, value ) as (
       select id, add_months(min_date, level - 1), 0
       from   ranges
       connect by level <= 1 + months_between( max_date, min_date )
              and prior id = id
              and prior sys_guid() is not null
     ),
     v ( id, ref_date, value ) as (
       select   id, ref_date, sum(value)
       from     ( select id, ref_date, value from prep union all
                  select id, ref_date, value from inputs
                )
       group by id, ref_date
     )
select id, median(value) as median_value
from   v
group by id
order by id   -- ORDER BY is optional
;

ID MEDIAN_VALUE
-- ------------
 1            0
 2            0
 3            1
 4            9
你需要一个参考日期是一个日期吗?如果是,则没有格式。或者它是一个具有特定格式的varchar2?您需要一个ref_日期是一个日期吗?如果是,则没有格式。或者它是具有特定格式的varchar2?