DB2 - 有一种简单的方法可以获得“以前的有效值”吗?

时间:2012-05-18 06:28:42

标签: sql group-by db2 sql-order-by

我不确定将问题称为“之前的有效值”是否合适。事情如下:

我有一张桌子“A”:

create table A (
  name varchar(16),
  performanceDate date,
  value int
);

name和PerformanceDate都是主键。

有一个流程每天都会为每个用户插入数据。因此,数据看起来如下所示:

select * from A;
|------+-----------------+-------|
| name | performanceDate | value |
|------+-----------------+-------|
| Joe  |      2012-05-18 | null  |
| Joe  |      2012-05-17 | 2     |
| Joe  |      2012-05-16 | null  |
| Joe  |      2012-05-15 | null  |
| Joe  |      2012-05-14 | 3     |
|------+-----------------+-------|

目前,我想在2012-05-16和2012-05-18之间获得preformanceDate的结果集如果当前日期的值为null,则应将其替换为之前的vaild值。例如,2012-05-16的值为null,2012-05-16之前的第一个有效值为2012-05-14的3。所以,结果如下:

|------+-----------------+-------|
| name | performanceDate | value |
|------+-----------------+-------|
| Joe  |      2012-05-18 |     2 |
| Joe  |      2012-05-17 |     2 |
| Joe  |      2012-05-16 |     3 |
|------+-----------------+-------|

到目前为止,我计划首先将数据插入临时表(因为表“A”只为我读取),然后逐个更新值。但这种方式很慢。你对此有什么想法吗?

2 个答案:

答案 0 :(得分:2)

您可以使用LATERAL JOIN,DB2尚未在SQLFiddle上,以下是SQL Server中的等效项。 LATERAL等同于SQL Server的APPLY:

select x.name, x.performanceDate, coalesce(x.value, y.value) as value
from tbl x

outer apply 
( 
  -- find nearest

  select top 1 value
  from tbl
  where 

    x.value is null

    and 

    (
       name = x.name
       and value is not null 
       and performanceDate < x.performanceDate 
    )

  order by performanceDate desc

) as y
order by x.name, x.performanceDate desc

数据:

| NAME |            PERFORMANCEDATE |  VALUE |
|------|----------------------------|--------|
|  Joe | May, 18 2012 08:00:00-0700 | (null) |
|  Joe | May, 17 2012 08:00:00-0700 |      2 |
|  Joe | May, 16 2012 08:00:00-0700 | (null) |
|  Joe | May, 15 2012 08:00:00-0700 | (null) |
|  Joe | May, 14 2012 08:00:00-0700 |      3 |

输出:

| NAME |            PERFORMANCEDATE | VALUE |
|------|----------------------------|-------|
|  Joe | May, 18 2012 08:00:00-0700 |     2 |
|  Joe | May, 17 2012 08:00:00-0700 |     2 |
|  Joe | May, 16 2012 08:00:00-0700 |     3 |
|  Joe | May, 15 2012 08:00:00-0700 |     3 |
|  Joe | May, 14 2012 08:00:00-0700 |     3 |

实时测试:http://www.sqlfiddle.com/#!6/e0158/8


基于IBM文档.. http://publib.boulder.ibm.com/infocenter/iseries/v7r1m0/index.jsp?topic=%2Fdb2%2Frbafztabref.htm

..,我希望这是:

select x.name, x.performanceDate, coalesce(x.value, y.value) as value
from tbl x,

lateral 
( 
  -- find nearest

  select top 1 value
  from tbl
  where 

    x.value is null

    and 

    (
       name = x.name
       and value is not null 
       and performanceDate < x.performanceDate 
    )

  order by performanceDate desc
  fetch first 1 rows only

) as y
order by x.name, x.performanceDate desc

有关CROSS APPY / OUTER的有趣指标:http://explainextended.com/2009/07/16/inner-join-vs-cross-apply/

使用OUTER APPLY的另一个例子:http://www.ienablemuch.com/2012/04/outer-apply-walkthrough.html

答案 1 :(得分:0)

with tmp1(name, performanceDate, value, cid) as (
select 
   name,
   performanceDate,
   value,
   count(value) over (partition by name 
                          order by performanceDate,
                                   value nulls last) as cid from A),
tmp2(name, performanceDate, value) as (
select 
   name,
   performanceDate,
   first_value(value) over (partition by name, cid 
                                order by performanceDate, 
                                         value nulls last from tmp1)
select * from tmp2;

with tmp1 (name, performanceDate_Begin, performanceDate_End, value) as (
select 
   name,
   performanceDate as performanceDate_Begin,
   lead(performanceDate) over (partition by name 
                                   order by performanceDate,
                                            value nulls last) as performanceDate_End,value from A where value is not null),
tmp2 (name, performanceDate, value) as (
select 
   A.name,
   A.performanceDate,
   tmp1.value 
from A 
left join tmp1 on A.name = B.name 
              and A.performanceDate >= tmp1.performanceDate_Begin 
              and A.performanceDate < coalesce(tmp1.performanceDate_End,date '9999-12-31'))
select * from tmp2