Considere uma tabela preenchida com datas de 2010 a 2050.
Suponha uma tabela de dados (chamada Lease
) com alguns dados e inclui uma coluna de data, Move_Out_Date
.
O objetivo é contar os registros com datas de saída em cada ano/mês.
Um simples Group By
obtém a resposta correta:
SELECT
YEAR(move_out_date) MOYear,
MONTH(move_out_date) MOMonth,
COUNT(move_out_date) AS Count
FROM
lease l
WHERE
YEAR(move_out_date) = '2019'
GROUP BY
YEAR(move_out_date),
MONTH(move_out_date)
ORDER BY
YEAR(move_out_date),
MONTH(move_out_date)
Esta consulta produz esta saída:
ANO | MÊS | CONTAR |
---|---|---|
2019 | 9 | 2 |
2019 | 10 | 4 |
2019 | 12 | 3 |
O que estou procurando é isto:
ANO | MÊS | CONTAR |
---|---|---|
2019 | 1 | 0 |
2019 | 2 | 0 |
2019 | 3 | 0 |
2019 | 4 | 0 |
2019 | 5 | 0 |
2019 | 6 | 0 |
2019 | 7 | 0 |
2019 | 8 | 0 |
2019 | 9 | 2 |
2019 | 10 | 4 |
2019 | 11 | 0 |
2019 | 12 | 3 |
Tenho mexido com junções cruzadas e junções externas esquerdas na tabela de datas, mas sempre acabo com números enormes para as contagens.
O truque é começar pela tabela de datas. Use isso para criar uma projeção dos anos/meses desejados. Em seguida, faça um JOIN da projeção para a tabela de arrendamentos. Como a tabela de datas é considerada completa, podemos usar um INNER JOIN e não precisamos de LEFT ou CROSS, embora um LEFT funcione.
Também é bom pensar cuidadosamente sobre a expressão condicional usada para o JOIN. Queremos evitar a mutação dos dados na
Lease
tabela, pois isso precisaria acontecer em todas as linhas ... mesmo nas linhas fora do intervalo. De que outra forma o banco de dados saberia se o resultado corresponde? Isso também impediria o uso do índice, provavelmente causando uma varredura completa da tabela quando uma operação mais eficiente estaria disponível.Então temos isto:
Assim como o Joel , eu começaria na tabela Calendário (se você não tiver uma, crie uma, há muitos recursos disponíveis sobre como fazer isso), mas, em vez disso, não usaria um CTE para filtrar as datas de início/término do mês. Em vez disso, eu pegaria todas as datas,
LEFT JOIN
as colocaria na sua tabela e depois as agruparia. Isso resulta no seguinte:Se você "preferir" filtrar por ano, filtre pela coluna de ano da tabela de calendário (
C.CalendarYear = 2019
). Apenas certifique-se de que sua Tabela de Calendário esteja bem indexada.Sem uma tabela de datas
Como alternativa, você pode construir sua tabela de meses programaticamente (com uma Common Table Expression recursiva ), sem precisar de uma tabela de datas (… e, portanto, sem ter que mantê-la e garantindo que você não deixou um buraco nela).
Você pode vê-lo funcionando em um violino (... limitado a 2019!).
O SQL adaptado do Snowflake produziu exatamente os resultados necessários.
Por curiosidade, implementarei esse método em uma consulta muito maior com muitos sindicatos e, em seguida, agruparei esses resultados em uma resposta para um KPI de fluxo de caixa líquido.
Aqui ele é adaptado para Snowflake.