当不同的值更改时返回值

时间:2013-03-26 21:14:03

标签: sql oracle analytic-functions

我有一个查询,它返回以下内容,EXCEPT用于最后一列,这是我需要弄清楚如何创建的。对于每个给定的ObservationID,我需要返回状态变化的日期;类似于LEAD()函数,它可以采取条件,而不仅仅是偏移。可以吗?

我需要计算列更改日期;它应该是状态不是当前状态的最后日期。

+---------------+--------+-----------+--------+-------------+
| ObservationID | Region |   Date    | Status | Change Date | <-This field
+---------------+--------+-----------+--------+-------------+
|             1 |     10 | 1/3/2012  | Ice    | 1/4/2012    |
|             2 |     10 | 1/4/2012  | Water  | 1/6/2012    |
|             3 |     10 | 1/5/2012  | Water  | 1/6/2012    |
|             4 |     10 | 1/6/2012  | Gas    | 1/7/2012    |
|             5 |     10 | 1/7/2012  | Ice    |             |
|             6 |     20 | 2/6/2012  | Water  | 2/10/2012   |
|             7 |     20 | 2/7/2012  | Water  | 2/10/2012   |
|             8 |     20 | 2/8/2012  | Water  | 2/10/2012   |
|             9 |     20 | 2/9/2012  | Water  | 2/10/2012   |
|            10 |     20 | 2/10/2012 | Ice    |             |
+---------------+--------+-----------+--------+-------------+

3 个答案:

答案 0 :(得分:1)

模型子句(10g +)可以以紧凑的方式做到这一点:

SQL> create table observation(ObservationID ,  Region  ,obs_date,  Status)
  2  as
  3  select  1, 10, date '2012-03-01', 'Ice' from dual union all
  4  select  2, 10, date '2012-04-01', 'Water' from dual union all
  5  select  3, 10, date '2012-05-01', 'Water' from dual union all
  6  select  4, 10, date '2012-06-01', 'Gas' from dual union all
  7  select  5, 10, date '2012-07-01', 'Ice' from dual union all
  8  select  6, 20, date '2012-06-02', 'Water' from dual union all
  9  select  7, 20, date '2012-07-02', 'Water' from dual union all
 10  select  8, 20, date '2012-08-02', 'Water' from dual union all
 11  select  9, 20, date '2012-09-02', 'Water' from dual union all
 12  select 10, 20, date '2012-10-02', 'Ice' from dual ;

Table created.

SQL> select ObservationID, obs_date, Status, status_change
  2            from observation
  3          model
  4          dimension by (Region, obs_date, Status)
  5          measures ( ObservationID, obs_date obs_date2, cast(null as date) status_change)
  6          rules (
  7            status_change[any,any,any] = min(obs_date2)[cv(Region), obs_date > cv(obs_date), status != cv(status)]
  8          )
  9   order by 1;

OBSERVATIONID OBS_DATE  STATU STATUS_CH
------------- --------- ----- ---------
            1 01-MAR-12 Ice   01-APR-12
            2 01-APR-12 Water 01-JUN-12
            3 01-MAY-12 Water 01-JUN-12
            4 01-JUN-12 Gas   01-JUL-12
            5 01-JUL-12 Ice
            6 02-JUN-12 Water 02-OCT-12
            7 02-JUL-12 Water 02-OCT-12
            8 02-AUG-12 Water 02-OCT-12
            9 02-SEP-12 Water 02-OCT-12
           10 02-OCT-12 Ice

小提琴:http://sqlfiddle.com/#!4/f6687/1

即。我们将根据区域,日期和状态确定维度,因为我们希望查看具有相同区域的单元格,但获取状态不同的第一个日期。

我们还必须测量日期,所以我创建了一个别名obs_date2来执行此操作,我们希望新列status_change保存状态更改的日期。

这一行是为我们完成所有工作的路线:

status_change[any,any,any] = min(obs_date2)[cv(Region), obs_date > cv(obs_date), status != cv(status)]

它说,对于我们的三维,只查看具有相同区域(cv(Region),)的行,并查看日期跟随当前行日期(obs_date > cv(obs_date))的行,以及状态与当前行(status != cv(status))不同,最终得到满足这组条件(min(obs_date2))的最小日期,并将其分配给status_change。左侧的any,any,any部分表示此计算适用于所有行。

答案 1 :(得分:1)

我已多次尝试理解MODEL子句并且从未真正管理过它,所以我想添加另一种解决方案

此解决方案采用了Ronnis所做的一些操作,但使用了LEAD函数的IGNORE NULLS子句。我认为这只是Oracle 11的新功能,但如果需要,您可以用Oracle 10的FIRST_VALUE函数替换它。

select
  observation_id,
  region,
  observation_date,
  status,
  lead(case when is_change = 'Y' then observation_date end) ignore nulls 
    over (partition by region order by observation_date) as change_observation_date
from (
  select
    a.observation_id,
    a.region,
    a.observation_date,
    a.status,
    case 
      when status = lag(status) over (partition by region order by observation_date) 
        then null
        else 'Y' end as is_change
       from observations a
)
order by 1

答案 2 :(得分:0)

我经常在清理/到日期和重复行的重叠时执行此操作。 你的情况要简单得多,因为你只有“来自约会”:)

设置测试数据

create table observations(
   observation_id   number       not null
  ,region           number       not null
  ,observation_date date         not null
  ,status           varchar2(10) not null
);


insert 
  into observations(observation_id, region, observation_date, status)
   select 1,  10, date '2012-03-01', 'Ice'   from dual union all
   select 2,  10, date '2012-04-01', 'Water' from dual union all
   select 3,  10, date '2012-05-01', 'Water' from dual union all
   select 4,  10, date '2012-06-01', 'Gas'   from dual union all
   select 5,  10, date '2012-07-01', 'Ice'   from dual union all
   select 6,  20, date '2012-06-02', 'Water' from dual union all
   select 7,  20, date '2012-07-02', 'Water' from dual union all
   select 8,  20, date '2012-08-02', 'Water' from dual union all
   select 9,  20, date '2012-09-02', 'Water' from dual union all
   select 10, 20, date '2012-10-02', 'Ice'   from dual;

commit;

以下查询有三个兴趣点:

  1. 识别重复信息(录制内容与之前的录制内容相同)
  2. 忽略重复录制
  3. 确定“下一次”更改的日期
  4. with lagged as(
       select a.*
             ,case when status = lag(status, 1) over(partition by region 
                                                         order by observation_date) 
                   then null 
                   else rownum 
               end as change_flag -- 1
         from observations a
    )
    select observation_id
          ,region
          ,observation_date
          ,status
          ,lead(observation_date, 1) over(
             partition by region 
                 order by observation_date
          ) as change_date --3
          ,lead(observation_date, 1, sysdate) over(
             partition by region 
                 order by observation_date
          ) - observation_date as duration
      from lagged
     where change_flag is not null -- 2
     ;