Este é apenas um exemplo bobo sobre o qual comecei a pensar, mas não consigo encontrar uma solução elegante. Assumindo uma tabela Calendar (vou usar a sintaxe do Db2):
create table calendar
( dt date not null primary key
);
insert into calendar (dt)
with t (dt) as ( values cast('2020-01-01' as date)
union all
select dt + 1 day from t where dt < '2020-01-11')
select dt from t
;
e uma mesa que contém o saldo:
create table balance
( dt date not null
, amount int not null
, primary key (dt)
);
insert into balance (dt, amount)
values ('2020-01-03',100) ,('2020-01-05', -50);
Se quisermos copiar o último saldo conhecido, podemos usar LAST_VALUE e 'IGNORE NULLS' como:
select c.dt, last_value(b.amount, 'IGNORE NULLS') over (order by c.dt)
from calendar c
left join balance b
on c.dt = b.dt;
No entanto, se adicionarmos uma dimensão, digamos cid (customer_id), não será mais óbvio o que last_value significa. Normalmente particionamos por cid, mas cid é perdido devido à junção esquerda:
create table balance1
( cid int not null
, dt date not null
, amount int not null
, primary key (cid, dt)
);
insert into balance1 (cid, dt, amount)
values (1, '2020-01-03',100) ,(1, '2020-01-05', -50)
, (2, '2020-01-04',75), (2, '2020-01-08',55), (2, '2020-01-10', -35);
select c.dt, last_value(b.amount, 'IGNORE NULLS') over (partition by ?
order by c.dt)
from calendar c
left join balance b
on c.dt = b.dt;
O melhor que consegui foi usar um produto cartesiano entre calendário e clientes distintos de saldos:
select cid, dt, last_value(amount, 'IGNORE NULLS') over (partition by cid order by dt)
from (
select cid, dt, amount from balance1
union
select distinct b.cid, c.dt, null
from balance1 b
cross join calendar c
where not exists (
select 1 from balance1
where dt = c.dt and cid = b.cid
)
) t
order by dt, cid
;
Não é tão bonito, e estou procurando uma solução mais elegante. Eu usei a sintaxe do Db2 acima e neste Fiddle , mas é o princípio que procuro, então qualquer sintaxe de fornecedor servirá.
Por que não isso?
Comparação no dbfiddle