将LAG()应用于具有空值

时间:2015-07-15 20:40:31

标签: sql sql-server sql-server-2012

假设:

with    
m as (
    select  1 ID, cast('03/01/2015' as datetime) PERIOD_START, cast('3/31/2015' as datetime) PERIOD_END
    union all
    select  1 ID, '04/01/2015', '4/28/2015'
    union all
    select  1 ID, '05/01/2015', '5/31/2015'
    union all
    select  1 ID, '06/01/2015', '06/30/2015'
    union all
    select  1 ID, '07/01/2015', '07/31/2015'
)

,
a as (

    SELECT  1 ID, cast('2015-03-13 14:17:00.000' as datetime) AUDIT_TIME, 'READ [2]' STATUS
    UNION ALL
    SELECT  1 ID, '2015-04-27 15:51:00.000' AUDIT_TIME, 'HELD [2]' STATUS
    UNION ALL
    SELECT  1 ID, '2015-07-08 17:54:00.000' AUDIT_TIME, 'COMPLETED [5]' STATUS
)

此查询:

select  m.ID,PERIOD_START,PERIOD_END
        ,a.AUDIT_TIME,STATUS
from    m
LEFT OUTER JOIN a on m.id=a.id 
    and a.audit_time between m.period_start and m.period_end

生成此记录集:

ID  PERIOD_START    PERIOD_END  AUDIT_TIME  STATUS
1   2015-03-01 00:00:00.000 2015-03-31 00:00:00.000 2015-03-13 14:17:00.000 READ [2]
1   2015-04-01 00:00:00.000 2015-04-28 00:00:00.000 2015-04-27 15:51:00.000 HELD [2]
1   2015-05-01 00:00:00.000 2015-05-31 00:00:00.000 NULL    NULL
1   2015-06-01 00:00:00.000 2015-06-30 00:00:00.000 NULL    NULL
1   2015-07-01 00:00:00.000 2015-07-31 00:00:00.000 2015-07-08 17:54:00.000 COMPLETED [5]

我需要在5月和6月重复4/27/15条目:

ID  PERIOD_START    PERIOD_END  AUDIT_TIME  STATUS
1   2015-03-01 00:00:00.000 2015-03-31 00:00:00.000 2015-03-13 14:17:00.000 READ [2]
1   2015-04-01 00:00:00.000 2015-04-28 00:00:00.000 2015-04-27 15:51:00.000 HELD [2]
1   2015-05-01 00:00:00.000 2015-05-31 00:00:00.000 2015-04-27 15:51:00.000 HELD [2]
1   2015-06-01 00:00:00.000 2015-06-30 00:00:00.000 2015-04-27 15:51:00.000 HELD [2]
1   2015-07-01 00:00:00.000 2015-07-31 00:00:00.000 2015-07-08 17:54:00.000 COMPLETED [5]

使用LAG()功能:

select  m.ID,PERIOD_START,PERIOD_END
        ,a.AUDIT_TIME
        ,LAG(audit_time) OVER (partition by m.ID order by period_start) PRIOR_AUDIT_TIME
        ,STATUS
        ,LAG(STATUS) OVER (partition by m.ID order by period_start) PRIOR_STATUS
from    m
LEFT OUTER JOIN a on m.id=a.id 
    and a.audit_time between m.period_start and m.period_end

仅适用于单行:

ID  PERIOD_START    PERIOD_END  AUDIT_TIME  PRIOR_AUDIT_TIME    STATUS  PRIOR_STATUS
1   2015-03-01 00:00:00.000 2015-03-31 00:00:00.000 2015-03-13 14:17:00.000 NULL    READ [2]    NULL
1   2015-04-01 00:00:00.000 2015-04-28 00:00:00.000 2015-04-27 15:51:00.000 2015-03-13 14:17:00.000 HELD [2]    READ [2]
1   2015-05-01 00:00:00.000 2015-05-31 00:00:00.000 NULL    2015-04-27 15:51:00.000 NULL    HELD [2]
1   2015-06-01 00:00:00.000 2015-06-30 00:00:00.000 NULL    NULL    NULL    NULL
1   2015-07-01 00:00:00.000 2015-07-31 00:00:00.000 2015-07-08 17:54:00.000 NULL    COMPLETED [5]   NULL

有没有办法在不必使用游标的情况下执行此操作?

1 个答案:

答案 0 :(得分:0)

您可以使用窗口函数执行此操作:

with q as (
      select m.ID, PERIOD_START, PERIOD_END, a.AUDIT_TIME, STATUS
      from m LEFT OUTER JOIN
           a
           on m.id = a.id and
              a.audit_time between m.period_start and m.period_end
     )
select q.*,
       max(status) over (partition by id, audit_grp) as imputed_status
from (select q.*,
             max(audit_time) over (partition by id order by period_start) as audit_grp
      from q
     ) q

我们的想法是使用audit_time作为累积窗口函数来复制max()值。然后,这定义了组,因此您也可以获得状态。

ANSI将IGNORE NULLS指令提供给LAG(),但SQL Server(尚未)支持它。