用值替换NULL

时间:2014-05-09 21:21:49

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

这是我的挑战: 我有一个日志表,每次更改记录时都会添加一条新记录,但会为每条记录中的每个未更改值设置一个NULL值。换句话说,仅设置更改的值,每行中其余未更改的字段仅具有NULL值。 现在我想用上面的值替换每个NULL值,这些值不是NULL值,如下所示:

源表:Task_log

ID  Owner       Status      Flag
1   Bob         Registrar   T
2   Sue         NULL        NULL
3   NULL        NULL        F
4   Frank       Admission   T
5   NULL        NULL        F
6   NULL        NULL        T

所需的输出表:Task_log

ID  Owner       Status      Flag
1   Bob         Registrar   T
2   Sue         Registrar   T
3   Sue         Registrar   F
4   Frank       Admission   T
5   Frank       Admission   F
6   Frank       Admission   T

如何编写将生成所需输出表的查询?

4 个答案:

答案 0 :(得分:2)

一个SQLServer 2012的新窗口函数是FIRST_VALUE,它有一个非常直接的名称,它可以通过OVER子句进行分区,在使用它之前必须划分数据块中的每一列,当找到值时,列的块开始。

With Block As (
  Select ID
       , Owner
       , OBlockID = SUM(Case When Owner Is Null Then 0 Else 1 End) 
                    OVER (ORDER BY ID)
       , Status
       , SBlockID = SUM(Case When Status Is Null Then 0 Else 1 End) 
                    OVER (ORDER BY ID)
       , Flag
       , FBlockID = SUM(Case When Flag Is Null Then 0 Else 1 End) 
                    OVER (ORDER BY ID)
  From   Task_log
)
Select ID
     , Owner = FIRST_VALUE(Owner) OVER (PARTITION BY OBlockID ORDER BY ID)
     , Status = FIRST_VALUE(Status) OVER (PARTITION BY SBlockID ORDER BY ID)
     , Flag = FIRST_VALUE(Flag) OVER (PARTITION BY FBlockID ORDER BY ID)
FROM   Block

SQLFiddle演示

UPDATE查询很容易导出

答案 1 :(得分:0)

正如我在评论中提到的,我会尝试修复创建记录而不是修复垃圾数据的过程。如果这不是一个选项,下面的代码应该指向正确的方向。

UPDATE   t1
set      t1.owner = COALESCE(t1.owner, t2.owner),
         t1.Status = COALESCE(t1.status, t2.status),
         t1.Flag = COALESCE(t1.flag, t2.flag)
FROM     Task_log as t1
INNER JOIN    Task_log as t2
         ON t1.id = (t1.id + 1)
where    t1.owner is null 
         OR t1.status is null 
         OR t1.flag is null

答案 2 :(得分:0)

我可以想到几种方法。

您可以将COALESCE与数组聚合函数结合使用。不幸的是,它看起来并不像SQL Server本身支持array_agg(尽管有些nice people have developed some workarounds)。

您还可以为每列使用子选择。

SELECT id,
       (SELECT TOP 1 FROM (SELECT owner FROM ... WHERE id = outer_id AND owner IS NOT NULL order by ID desc )) AS owner,
       -- other columns

您也可以使用窗口函数执行某些操作。

答案 3 :(得分:0)

香草解决方案将是:

select id , owner , coalesce(owner, ( select owner from t t2 where id = (select max(id) from t t3 where id < t1.id and owner is not null)) ) as new_owner , flag , coalesce(flag, ( select flag from t t2 where id = (select max(id) from t t3 where id < t1.id and flag is not null)) ) as new_flag from t t1

相当低效,但应该适用于大多数DBMS