从上一行中拉出非空白
Pull non blank from previous row
我有如下数据(前 3 列)。我想创建第 4 列 - newstatus
.
newstatus
列的逻辑是
- 每个 pk2
- 如果第
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);
我有如下数据(前 3 列)。我想创建第 4 列 - newstatus
.
newstatus
列的逻辑是
- 每个 pk2
- 如果第
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);