Sql 当其他值更改时返回一个值

Sql 当其他值更改时返回一个值,sql,oracle,analytic-functions,Sql,Oracle,Analytic Functions,我有一个查询,它返回以下内容,除了最后一列,这是我需要弄清楚如何创建的。对于每个给定的ObservationID,我需要返回状态更改的日期;类似于LEAD()函数的东西,它将接受条件而不仅仅是偏移量。能做到吗 我需要计算列的更改日期;它应该是状态不是当前状态的最后日期 +---------------+--------+-----------+--------+-------------+ | ObservationID | Region | Date | Status | Chang

我有一个查询,它返回以下内容,除了最后一列,这是我需要弄清楚如何创建的。对于每个给定的
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    |             |
+---------------+--------+-----------+--------+-------------+
+---------------+--------+-----------+--------+-------------+

|ObservationID | Region | Date | Status | Change Date |我在清理日期之间的重叠和重复行时经常这样做。 不过,您的情况要简单得多,因为您只有“开始日期”:

设置测试数据

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;
以下查询有三个关注点:

  • 识别重复信息(记录显示与先前记录相同)
  • 忽略重复的录音
  • 确定“下一次”变更的日期
  • 范本条款(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
    
    小提琴:

    i、 e.我们将根据区域、日期和状态进行尺寸标注,因为我们希望查看具有相同区域的单元格,但得到状态不同的第一个日期

    我们还必须测量日期,因此我创建了一个别名
    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\u date>cv(obs\u date)
    ),并且状态与当前行不同(
    status!=cv(status)
    )最后,获取满足这组条件的最小日期(
    min(obs\u date2)
    ),并将其分配给
    状态更改
    。左边的
    any,any,any
    部分表示此计算适用于所有行。

    我曾多次尝试理解MODEL子句,但从未真正理解它,因此我想添加另一个解决方案

    此解决方案采用了Ronnis所做的一些工作,但使用了LEAD函数的
    ignorenulls
    子句。我认为这只是Oracle11的新增功能,但如果需要,您可能会将其替换为Oracle10的
    FIRST\u 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
    

    至于代码,我需要它为每个地区的每个特定状态提供开始和结束日期,即a地区的冻结持续了多长时间?我尝试过子查询,它为我提供每个状态的最早日期,但无法将它们与状态的正确更改正确配对。与LEAD类似的功能是理想的,但比下一条记录的范围更广。冰/水/气体不是真实的数据,只是代表我的问题…真实的查询很长,肯定会浪费你的时间。这是一个简单的选择,其中四列来自四个不同的表,“更改日期”是我无法创建的列。如果这还不够的话,请道歉,这是一个新手,我不想浪费你的时间。@David:这里的很多SQL问题都倾向于使用SQL Fiddle来显示你的架构设置和你迄今为止所做的尝试;这里有一个开始的地方:您能添加一个到目前为止您已经尝试过的示例查询吗?不幸的是,目前在fam度假,无法测试这些…将在下周试用后立即报告!Dazzal的建模方法正在发挥作用,将与Ronnis和Mike的建模方法进行比较,显然可以使用这种方法……Ronnis,非常感谢,将尝试这种方法,并让大家知道下周有多少条通往罗马的路?非常感谢,迈克,下个星期我的结果Dazzal,我先尝试了这个方法,它很有魅力。在将其调整到真实数据后,只需添加一个唯一的引用即可。我已经向模特迈出了第一步!非常感谢,我们将了解如何给你适当的网站荣誉。
    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