如何让 LAG() 忽略 SQL 服务器中的 NULLS?
How to make LAG() ignore NULLS in SQL Server?
有谁知道如何用字符串替换列中的空值,直到遇到新字符串然后该字符串替换它下面的所有空值?我有一个看起来像这样的专栏
原专栏:
PAST_DUE_COL
91 or more days pastdue
Null
Null
61-90 days past due
Null
Null
31-60 days past due
Null
0-30 days past due
Null
Null
Null
预期结果列:
PAST_DUE_COL
91 or more days past due
91 or more days past due
91 or more days past due
61-90 days past due
61-90 days past due
61-90 days past due
31-60 days past due
31-60 days past due
0-30 days past due
0-30 days past due
0-30 days past due
0-30 days past due
基本上我希望列中的第一个字符串替换它下面的所有空值,直到下一个字符串。然后该字符串将替换它下面的所有空值,直到下一个字符串,依此类推。
SQL 服务器不支持 window 函数的 ignore nulls
选项,例如 lead()
和 lag()
,这个问题很适合.
我们可以通过一些间隙和孤岛技术来解决这个问题:
select
t.*,
max(past_due_col) over(partition by grp) new_past_due_col
from (
select
t.*,
sum(case when past_due_col is null then 0 else 1 end)
over(order by id) grp
from mytable t
) t
子查询执行 window 求和,每次找到非空值时该求和都会递增:这定义了包含非空值后跟空值的行组。
然后,外部使用 window max()
检索每个组中的(唯一)非空值。
这假定一个列可用于排序记录(我称之为id
)。
ID | PAST_DUE_COL | grp | new_past_due_col
-: | :---------------------- | --: | :----------------------
1 | 91 or more days pastdue | 1 | 91 or more days pastdue
2 | null | 1 | 91 or more days pastdue
3 | null | 1 | 91 or more days pastdue
4 | 61-90 days past due | 2 | 61-90 days past due
5 | null | 2 | 61-90 days past due
6 | null | 2 | 61-90 days past due
7 | 31-60 days past due | 3 | 31-60 days past due
8 | null | 3 | 31-60 days past due
9 | 0-30 days past due | 4 | 0-30 days past due
10 | null | 4 | 0-30 days past due
11 | null | 4 | 0-30 days past due
12 | null | 4 | 0-30 days past due
如果您有一个 id 列并且 lead/lag 不可用,您可以使用:
SELECT (select top 1 PAST_DUE_COL from MyTablename
where id <= t.id and PAST_DUE_COL <> '' order by id desc)
FROM MyTablename T
这是 GMB 答案的变体。只是简单了一点:
select t.*,
max(past_due_col) over(partition by grp) as new_past_due_col
from (select t.*,
count(past_due_col) over (order by id) as grp
from mytable t
) t;
请注意,您的问题需要某种排序列才有意义。
另一种方法使用 apply
:
select t.*, t2.past_due_col
from mytable t outer apply
(select top (1) t2.*
from mytable t2
where t2.id <= t.id and t2.past_due_col is not null
order by t2.id desc
) t2;
有谁知道如何用字符串替换列中的空值,直到遇到新字符串然后该字符串替换它下面的所有空值?我有一个看起来像这样的专栏
原专栏:
PAST_DUE_COL
91 or more days pastdue
Null
Null
61-90 days past due
Null
Null
31-60 days past due
Null
0-30 days past due
Null
Null
Null
预期结果列:
PAST_DUE_COL
91 or more days past due
91 or more days past due
91 or more days past due
61-90 days past due
61-90 days past due
61-90 days past due
31-60 days past due
31-60 days past due
0-30 days past due
0-30 days past due
0-30 days past due
0-30 days past due
基本上我希望列中的第一个字符串替换它下面的所有空值,直到下一个字符串。然后该字符串将替换它下面的所有空值,直到下一个字符串,依此类推。
SQL 服务器不支持 window 函数的 ignore nulls
选项,例如 lead()
和 lag()
,这个问题很适合.
我们可以通过一些间隙和孤岛技术来解决这个问题:
select
t.*,
max(past_due_col) over(partition by grp) new_past_due_col
from (
select
t.*,
sum(case when past_due_col is null then 0 else 1 end)
over(order by id) grp
from mytable t
) t
子查询执行 window 求和,每次找到非空值时该求和都会递增:这定义了包含非空值后跟空值的行组。
然后,外部使用 window max()
检索每个组中的(唯一)非空值。
这假定一个列可用于排序记录(我称之为id
)。
ID | PAST_DUE_COL | grp | new_past_due_col -: | :---------------------- | --: | :---------------------- 1 | 91 or more days pastdue | 1 | 91 or more days pastdue 2 | null | 1 | 91 or more days pastdue 3 | null | 1 | 91 or more days pastdue 4 | 61-90 days past due | 2 | 61-90 days past due 5 | null | 2 | 61-90 days past due 6 | null | 2 | 61-90 days past due 7 | 31-60 days past due | 3 | 31-60 days past due 8 | null | 3 | 31-60 days past due 9 | 0-30 days past due | 4 | 0-30 days past due 10 | null | 4 | 0-30 days past due 11 | null | 4 | 0-30 days past due 12 | null | 4 | 0-30 days past due
如果您有一个 id 列并且 lead/lag 不可用,您可以使用:
SELECT (select top 1 PAST_DUE_COL from MyTablename
where id <= t.id and PAST_DUE_COL <> '' order by id desc)
FROM MyTablename T
这是 GMB 答案的变体。只是简单了一点:
select t.*,
max(past_due_col) over(partition by grp) as new_past_due_col
from (select t.*,
count(past_due_col) over (order by id) as grp
from mytable t
) t;
请注意,您的问题需要某种排序列才有意义。
另一种方法使用 apply
:
select t.*, t2.past_due_col
from mytable t outer apply
(select top (1) t2.*
from mytable t2
where t2.id <= t.id and t2.past_due_col is not null
order by t2.id desc
) t2;