从上一行中拉出非空白

Pull non blank from previous row

我有如下数据(前 3 列)。我想创建第 4 列 - newstatus.

newstatus 列的逻辑是

  1. 每个 pk2
  2. 如果第 status 列为空,则从 最近的行,其中 status 是新的或旧的。

因为第 3 行从第 2 行获取新值,而第 9 行从第 7 行获取旧值。第 8 行被忽略,因为它具有值 ignore

pk  status  pk2        newstatus
1           1   
2   new     1   
3           1          new
4   ignore  1   
5   ignore  1   
6           2   
7   old     2   
8   ignore  2   
9           2          old
10  new     2   

您可以使用 window 函数。

我们的想法是构建具有累积计数的行组,每次满足“新”或“旧”状态时该计数都会增加。然后,您可以在需要时使用 first_value() 访问相应的状态。

select 
    t.*,
    case when status is null and grp > 0
        then first_value(status) over(partition by pk2, grp order by pk1)
        else status
    end as new_status
from (
    select 
        t.*,
        sum(case when status in ('old', 'new') then 1 else 0 end)
            over(partition by pk2 order by pk1) grp
    from mytable t
) t

嗯。 . .使用 outer apply

可能是最简单的
select t.*,
       (case when status is null then t2.status end) as new_status
from t outer apply
     (select top (1) t2.*
      from t t2
      where t2.pk2 = t.pk2 and t2.status in ('old', 'new') and
            t2.pk1 < t.pk1
      order by t2.pk1 desc
     ) t2;

实际上有一种无需子查询的方法。 . .这可能是最有效的方法:

select t.*,
       (case when status is null and
                  max(case when status = 'old' then pk1 end) over (partition by pk2 order by pk1) >
                  max(case when status = 'new' then pk2 else 0 end order by pk1) over (partition by pk2)
             then 'old'
             when status is null and
                  max(case when status = 'new' then pk1 end) over (partition by pk2 order by pk1) >
                  max(case when status = 'old' then pk2 else 0 end) over (partition by pk2 order by pk1)
             then 'new'
      end) as new_status
from t;

你可以使用这个脚本

WITH OuterT AS
(
SELECT *,ROW_NUMBER() OVER(PARTITION BY pk2 ORDER BY pk1) num FROM tbl
)
SELECT pk1,[Status],pk2,
(CASE WHEN num=1 or [Status] !='' THEN ''
WHEN num !=1 THEN
(SELECT TOP 1 innerT.[Status] FROM OuterT innerT WHERE innerT.pk2 =OuterT.pk2 and ([status] ='new' or [status] ='old') and num != 1 and innerT.pk1 < OuterT.pk1  ORDER BY pk1 DESC)
END) newstatus
FROM OuterT

工作原理:

我用的common_table_expression它的名字是OuterT

WITH OuterT AS
(
SELECT *,ROW_NUMBER() OVER(PARTITION BY pk2 ORDER BY pk1) num FROM tbl
)

而我用的是ROW_NUMBER它的名字是num

OuterT 的内部结果是:

然后我使用了一个 CASE 来包含你基于 OuterT

的逻辑
(CASE WHEN num=1 or [Status] !='' THEN ''
WHEN num !=1 THEN
(SELECT TOP 1 innerT.[Status] FROM OuterT innerT WHERE innerT.pk2 =OuterT.pk2 and ([status] ='new' or [status] ='old') and num != 1 and innerT.pk1 < OuterT.pk1  ORDER BY pk1 DESC)
END) newstatus

最终结果是:

注意:如果您想使用脚本,只需将“tbl”替换为您的 table 名称

这是老派,没有窗口函数,也没有外部应用。

;with max_prior_pk_cte as (
    select tt.pk, tt.pk2,
           max(ttt.pk) max_pk
    from
      #testTable tt
     join
      #testTable ttt on tt.pk2=ttt.pk2
                        and tt.pk>ttt.pk
    where tt.[status] is null
          and ttt.[status] in ('old', 'new')
    group by tt.pk, tt.pk2)
select
  t.*, t_prior.[status] new_status
from
  #testTable t
 left join
  max_prior_pk_cte mppc on t.pk=mppc.pk
 left join
  #testTable t_prior on mppc.max_pk=t_prior.pk;

下面是一些示例数据:

drop table if exists #testTable;
go
create table #testTable(
  pk        int unique not null,
  [status]  varchar(20),
  pk2       int not null);

insert into #testTable(pk, [status], pk2) values
(1, null, 1),
(2, 'new', 1),   
(3, null, 1),
(4, 'ignore', 1),
(5, 'ignore', 1),
(6, null, 2),   
(7, 'old', 2),   
(8, 'ignore', 2),   
(9, null, 2),
(10, 'new', 2);