Ao preparar uma resposta para outra pergunta aqui , codifiquei uma consulta que continha múltiplas funções de janela com a mesma OVER(...)
cláusula. Os resultados foram os esperados.
select ...
sum(sum(s.price)) over (partition by p.productid
order by c.date
rows between 6 preceding and current row)
/ nullif(
sum(count(s.price)) over(partition by p.productid
order by c.date
rows between 6 preceding and current row),
0)
as avg7DayPrice
...
Quando tentei substituir as OVER(...)
cláusulas duplicadas por uma WINDOW
referência de cláusula compartilhada OVER Last7Days
, definida posteriormente como WINDOW Last7Days AS (...)
, os resultados foram inesperadamente diferentes. (Consulte WINDOW
cláusula - nova em 2022.)
select ...
sum(sum(s.price)) over last7days
/ nullif(sum(count(s.price)) over last7days, 0)
as avg7DayPrice
...
window last7days as (partition by p.productid
order by c.date
rows between 6 preceding and current row)
Consulta original completa:
-- Inline window over() clauses
-- Results are as expected
with calendar as (
select min(date) as date, max(date) as endDate
from sales
union all
select dateadd(day, 1, date), endDate
from calendar
where date < enddate
),
products as (
select distinct productid
from sales
)
select
p.productid,
c.date as salesDate,
avg(price) as avg1DayPrice,
sum(sum(s.price)) over (partition by p.productid
order by c.date
rows between 6 preceding and current row)
/ nullif(
sum(count(s.price)) over(partition by p.productid
order by c.date
rows between 6 preceding and current row),
0)
as avg7DayPrice
from calendar c
cross join products p
left join sales s
on s.date = c.date
and s.productid = p.productid
group by p.productid, c.date
Consulta modificada completa:
-- Reference to common defined window clause
-- Expect same results, but that is not what I get.
with calendar as (
select min(date) as date, max(date) as endDate
from sales
union all
select dateadd(day, 1, date), endDate
from calendar
where date < enddate
),
products as (
select distinct productid
from sales
)
select
p.productid,
c.date as salesDate,
avg(price) as avg1DayPrice,
sum(sum(s.price)) over last7days
/ nullif(
sum(count(s.price)) over last7days,
0)
as avg7DayPrice
from calendar c
cross join products p
left join sales s
on s.date = c.date
and s.productid = p.productid
group by p.productid, c.date
window last7days as (partition by p.productid
order by c.date
rows between 6 preceding and current row)
Tabela de vendas:
ID do produto | data | preço |
---|---|---|
1 | 2025-02-01 | 10.00 |
1 | 2025-02-02 | 20,00 |
1 | 2025-02-02 | 30,00 |
1 | 2025-02-03 | 40,00 |
Resultados originais:
produtoid | data de venda | preço médio de 1 dia | preço médio do dia7 |
---|---|---|---|
1 | 2025-02-01 | 10.000000 | 10.000000 |
1 | 2025-02-02 | 25.000000 | 20.000000 |
1 | 2025-02-03 | 40.000000 | 25.000000 |
Resultados modificados:
produtoid | data de venda | preço médio de 1 dia | preço médio do dia7 |
---|---|---|---|
1 | 2025-02-01 | 10.000000 | 2.500000 |
1 | 2025-02-02 | 25.000000 | 15.000000 |
1 | 2025-02-03 | 40.000000 | 25.000000 |
Veja este db<>fiddle .
Não entendo por que os resultados são diferentes. Parece que a sum(count(s.price)) over(...)
parte do cálculo é 1, 3, 4
para a primeira consulta, mas 4, 4, 4
na segunda. O plano de execução também é significativamente diferente. Alguém pode explicar isso?
Este é certamente um bug ( relatado aqui ), uma reprodução mais simples é
que retorna
O problema é que isso
NULLIF
é expandido para uma expressão em que o primeiro argumento aparece duas vezes e a moldura da janela só é preservada corretamente para a primeira referência.ou seja, em vez de ser expandido conforme
correct_expansion
acima, ele é expandido para afaulty_expansion
variante.Executando o abaixo...
mostra isso em parte da saída (nenhum quadro de janela após o segundo
stopSum
)