如果row为NULL,则从前一行获取值

时间:2015-07-21 11:58:07

标签: sql sql-server tsql null

我有这个透视表

+---------+----------+----------+-----+----------+
| Date    | Product1 | Product2 | ... | ProductN |
+---------+----------+----------+-----+----------+
| 7/1/15  | 5        | 2        | ... | 7        |
| 8/1/15  | 7        | 1        | ... | 9        |
| 9/1/15  | NULL     | 7        | ... | NULL     |
| 10/1/15 | 8        | NULL     | ... | NULL     |
| 11/1/15 | NULL     | NULL     | ... | NULL     |
+---------+----------+----------+-----+----------+

我想在NULL列中填入上面的值。所以,输出应该是这样的。

+---------+----------+----------+-----+----------+
| Date    | Product1 | Product2 | ... | ProductN |
+---------+----------+----------+-----+----------+
| 7/1/15  | 5        | 2        | ... | 7        |
| 8/1/15  | 7        | 1        | ... | 9        |
| 9/1/15  | 7        | 7        | ... | 9        |
| 10/1/15 | 8        | 7        | ... | 9        |
| 11/1/15 | 8        | 7        | ... | 9        |
+---------+----------+----------+-----+----------+

我发现这个article可能对我有帮助,但这只会操纵一列。如何将此应用于我的所有列,或者如何实现此类结果,因为我的列是动态的。

非常感谢任何帮助。谢谢!

2 个答案:

答案 0 :(得分:0)

ANSI标准在IGNORE NULLS上有LAG()选项。这完全你想要的。唉,SQL Server还没有实现这个功能。

所以,你可以用几种方式做到这一点。一个是使用多个outer apply。另一个使用相关子查询:

select p.date,
       (case when p.product1 is not null else p.product1
             else (select top 1 p2.product1 from pivoted p2 where p2.date < p.date order by p2.date desc)
        end) as product1,
       (case when p.product1 is not null else p.product1
             else (select top 1 p2.product1 from pivoted p2 where p2.date < p.date order by p2.date desc)
        end) as product1,
       (case when p.product2 is not null else p.product2
             else (select top 1 p2.product2 from pivoted p2 where p2.date < p.date order by p2.date desc)
        end) as product2,
       . . .
from pivoted p ;

对于此查询,我建议在date上使用索引。

答案 1 :(得分:0)

我想建议你一个解决方案。如果你有一个只包含两列的表,我的解决方案将完美运行。

+---------+----------+
| Date    | Product  |
+---------+----------+
| 7/1/15  | 5        |
| 8/1/15  | 7        |
| 9/1/15  | NULL     |
| 10/1/15 | 8        |
| 11/1/15 | NULL     |
+---------+----------+

select  x.[Date], 
        case
            when x.[Product] is null
            then min(c.[Product])
        else
            x.[Product]
        end as Product
from
(
    -- this subquery evaluates a minimum distance to the rows where Product column contains a value
    select  [Date], 
            [Product], 
            min(case when delta >= 0 then delta else null end) delta_min,
            max(case when delta < 0 then delta else null end) delta_max
    from
    (
        -- this subquery maps Product table to itself and evaluates the difference between the dates
        select  p.[Date],
                p.[Product], 
                DATEDIFF(dd, p.[Date], pnn.[Date]) delta
        from @products p
        cross join (select * from @products where [Product] is not null) pnn
    ) x
    group by [Date], [Product]
) x
left join @products c on x.[Date] = 
    case
        when abs(delta_min) < abs(delta_max) then DATEADD(dd, -delta_min, c.[Date]) 
        else DATEADD(dd, -delta_max, c.[Date])
    end
group by x.[Date], x.[Product]
order by x.[Date]

在这个查询中,我将表映射到自己的行,这些行包含CROSS JOIN语句的值。然后我计算了日期之间的差异,以便选择最接近的日期,然后用值填充空单元格。

结果:

+---------+----------+
| Date    | Product  |
+---------+----------+
| 7/1/15  | 5        |
| 8/1/15  | 7        |
| 9/1/15  | 7        |
| 10/1/15 | 8        |
| 11/1/15 | 8        |
+---------+----------+

实际上,建议的查询不会选择以前的值。而不是这个,它选择最接近的值。换句话说,我的代码可以用于许多不同的目的。