用 outerapply 填充

Filldown with outerapply

我的源数据如下

    declare @dim as table
(
    name varchar(10),period int
)
insert into @dim
select * from
(
    values
        ('a', 202001),('a', 202002),('a', 202003),('a', 202004),
        ('a', 202005),('a', 202006),('a', 202007),('a', 202008),
        ('a', 202009),('a', 202010),('a', 202011),('a', 202012),
        ('b', 202001),('b', 202002),('b', 202003),('b', 202004),
        ('b', 202005),('b', 202006),('b', 202007),('b', 202008),
        ('b', 202009),('b', 202010),('b', 202011),('b', 202012)
) t (one, two)


declare @fact as table
(
    name varchar(max),period bigint,val decimal(19, 2)
)
insert into @fact
select * from
(
    values
        ('a', 202002, 100),
        ('a', 202005, 600),
        ('a', 202010, 700),
        ('b', 202004, 500),
        ('b', 202007, 600),
        ('b', 20208, 1000)
) t (one, two, three)

我想知道是否有可能在 outer-apply 中实现向下填充。我试过如下但没有用

select a.name,a.period,x.val, y.FD
from @dim a
outer apply (select * from @fact b where a.name=b.name and a.period=b.period) as x
outer apply (select max(x.val) over (partition by a.name order by a.period ASC ROWS UNBOUNDED PRECEDING)) as y (FD)

我之所以热衷于在 outer-apply 中实现向下填充,是因为我可以在同一查询中继续利用 outer-apply 中的向下填充列,继而 apply 到在没有任何临时 table 帮助的情况下创建进一步的计算列或转换,如下所示

select a.name,a.period,x.val, y.FD, z.bucket
from @dim a
outer apply (select * from @fact b where a.name=b.name and a.period=b.period) as x
outer apply (successful fill down) as y (FD)
outer apply (VALUES(CASE WHEN FD>1000 then 'bucket1' else 'bucket2' end) as z(bucket)

我想要的结果

姓名 时期 FD
一个 202001
一个 202002 100.00 100.00
一个 202003 100.00
一个 202004 100.00
一个 202005 600.00 600.00
一个 202006 600.00
一个 202007 600.00
一个 202008 600.00
一个 202009 600.00
一个 202010 700.00 700.00
一个 202011 700.00
一个 202012 700.00
b 202001
b 202002
b 202003
b 202004 500.00 500.00
b 202005 500.00
b 202006 500.00
b 202007 600.00 600.00
b 202008 600.00
b 202009 600.00
b 202010 600.00
b 202011 600.00
b 202012 600.00

这可以通过关注但在我不想要的 outer-apply 之外来实现。

select a.name,a.period,x.val, MAX(x.val) over (partition by a.name order by a.period ASC ROWS UNBOUNDED PRECEDING) as FD
from @dim a
outer apply (select * from @fact b where a.name=b.name and a.period=b.period) as x

您的问题是您将 MAX window 函数放在 APPLY.

当您使用 APPLY 时,将(逻辑上)为每一行评估整个子查询。因此,仅包含 select 的子查询只有一行,并且会根据外部 table.

的每一行进行评估

不能以这种方式使用window函数。Window函数仅(逻辑上)求值在加入和分组之后,在 select 阶段和排序之前。 APPLY 进入连接阶段,这是较早的阶段。

Note that your first APPLY can be rewritten as a simple LEFT JOIN.

select
    a.name,
    a.period,
    x.val, 
    FD = max(x.val) over 
         (partition by a.name
         order by a.period ROWS UNBOUNDED PRECEDING)
from @dim a
left join @fact x on a.name = x.name and a.period = x.period

如果您希望 MAX 在查询的其他部分使用,您 必须 将它放在派生的 table.

select *,
  SomeOtherCalculationInvolvingFD
from (
  select
    a.name,
    a.period,
    x.val, 
    FD = max(x.val) over 
         (partition by a.name
         order by a.period ROWS UNBOUNDED PRECEDING)
  from @dim a
  left join @fact x on a.name = x.name and a.period = x.period
) t

我给你一个提示:APPLY 非常有用,但不要到处敲钉子。了解它的工作原理,适当使用。