Snapshot de inventário em Data Warehouse: passo a passo
Este tutorial mostra como criar uma snapshot de inventário em Data Warehouse para registar, dia a dia, o saldo de cada produto. Uma tabela de snapshot facilita relatórios históricos, análises de ruptura de stock e optimiza consultas em vez de recalcular saldos a partir de transacções. Em cenários reais, onde podem existir 100k produtos e 10M transacções por ano, uma snapshot diária reduz o tempo de resposta de relatórios críticos de minutos para milissegundos.
Pré-requisitos
- Conhecimentos básicos de SQL (T-SQL ou SQL similar).
- Uma base de dados para o Data Warehouse (por exemplo Azure SQL, SQL Server).
- Tabelas fonte:
inventory_transactions(transacções) eproducts(catálogo). - Processo ETL/ELT para agendar cargas diárias (por exemplo Azure Data Factory, SQL Agent).
- Bom entendimento de retenção de dados — por exemplo manter snapshots diárias por 2 anos e depois consolidar para mensais.
Passo 1: Conceito da snapshot de inventário
Uma snapshot regista o saldo no final de cada dia para cada produto: uma linha por data+produto. Isto evita cálculos on-the-fly sobre milhões de transacções. Decida a granularidade (diária, semanal) consoante a necessidade do negócio: para operações logísticas a granularidade diária é comum; para análises executivas pode bastar semanal. As colunas essenciais são snapshot_date, product_id, quantity, load_ts, e campos de auditoria como loaded_by ou source_batch_id se precisar de rastreabilidade. Planeie também o crescimento: se tiver 100k produtos e guardar snapshots diárias, terá 36.5M de linhas por ano — considerar compressão e particionamento torna-se obrigatório.
Passo 2: Criar a tabela de snapshot
Crie uma tabela no Data Warehouse para armazenar as snapshots. É importante definir uma chave natural (snapshot_date + product_id) para garantir unicidade e facilitar UPSERTs. Exemplo em T-SQL (ajuste tipos conforme a sua plataforma):
CREATE TABLE dbo.inventory_snapshot (
snapshot_date date NOT NULL,
product_id int NOT NULL,
quantity bigint NOT NULL,
load_ts datetime2 NOT NULL DEFAULT SYSUTCDATETIME(),
PRIMARY KEY (snapshot_date, product_id)
);
-- Index para consultas por produto
CREATE INDEX IX_inventory_snapshot_product ON dbo.inventory_snapshot(product_id, snapshot_date);
Se o SGBD suportar, defina particionamento por snapshot_date (por exemplo por mês) para acelerar manutenção e limpeza. Configure compressão de dados (ROW ou PAGE) quando disponível: tipicamente reduz o espaço em disco 3x-10x para este tipo de tabela densa.
Passo 3: Carga inicial (full) da snapshot de inventário
Para a carga inicial, calcule o saldo acumulado até cada data para cada produto. Um método simples usa um conjunto de datas (dias) e um CROSS JOIN com produtos e depois um subselect para somar transacções até ao fim do dia. Esta abordagem é fácil de entender mas pode ser dispendiosa: por exemplo, gerar 365 dias x 100k produtos = 36.5M linhas, e cada subselect pode implicar scans; use por isso agregações prévias quando possível.
-- Exemplo: intervalo de datas (ajuste conforme necessário)
WITH days AS (
SELECT CAST(MIN(transaction_ts) AS date) AS start_day,
CAST(MAX(transaction_ts) AS date) AS end_day
FROM dbo.inventory_transactions
), gen AS (
SELECT DATEADD(day, n.number, d.start_day) AS day
FROM days d
JOIN ( -- gera números sequenciais; ajustar para mais dias
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 AS number
FROM sys.objects
) n ON n.number <= DATEDIFF(day, d.start_day, d.end_day)
)
INSERT INTO dbo.inventory_snapshot(snapshot_date, product_id, quantity, load_ts)
SELECT g.day AS snapshot_date,
p.product_id,
(SELECT SUM(t.qty_change)
FROM dbo.inventory_transactions t
WHERE t.product_id = p.product_id
AND CAST(t.transaction_ts AS date) <= g.day) AS quantity,
SYSUTCDATETIME()
FROM gen g
CROSS JOIN dbo.products p;
Notas: para melhorar performance, primeiro agregue inventory_transactions por dia e produto, e depois faça um cumulative sum por produto. Em muitos cenários uma operação em batch que leva 30-90 minutos para a carga inicial é aceitável; se demorar mais, divida por lotes ou faça paralelismo.
Passo 4: ETL incremental diário para a snapshot de inventário
O objectivo diário é calcular o saldo no final do dia corrente e fazer UPSERT na tabela de snapshot. Use MERGE (T-SQL) para inserir/actualizar de forma idempotente. Primeiro, calcule o saldo desse dia (ex.: @target_date). Para 100k produtos, uma execução bem optimizada deve completar em alguns minutos; se demorar muito, verifique índices e agregações prévias.
DECLARE @target_date date = CAST(GETUTCDATE() AS date);
WITH daily_balance AS (
SELECT p.product_id,
(SELECT SUM(t.qty_change)
FROM dbo.inventory_transactions t
WHERE t.product_id = p.product_id
AND CAST(t.transaction_ts AS date) <= @target_date) AS quantity
FROM dbo.products p
)
MERGE dbo.inventory_snapshot AS target
USING daily_balance AS src
ON target.snapshot_date = @target_date AND target.product_id = src.product_id
WHEN MATCHED THEN
UPDATE SET quantity = src.quantity, load_ts = SYSUTCDATETIME()
WHEN NOT MATCHED BY TARGET THEN
INSERT (snapshot_date, product_id, quantity, load_ts)
VALUES (@target_date, src.product_id, src.quantity, SYSUTCDATETIME());
Agende este script diariamente no seu scheduler de ETL/ELT (Azure Data Factory, SQL Agent, etc.). Para idempotência, assegure que o processo pode ser reexecutado sem criar duplicados — é por isso que usamos MERGE e chave primária.
Passo 5: Optimização e boas práticas
Para grandes volumes, considere:
- Pré-agregar transacções por dia e produto antes do MERGE para reduzir scans (p.ex. reduzir 10M linhas para 100k agregações diárias).
- Particionar a tabela por
snapshot_date(se o SGBD suportar) para melhorar limpeza e performance — por exemplo por mês ou por trimestre. - Manter uma tabela de datas para evitar geração dinâmica e facilitar joins de calendário.
- Monitorizar tempo de execução, bloqueios e utilização de I/O. Pense em realizar cargas fora de pico e em usar índices de cobertura.
- Definir política de retenção: manter diárias 2 anos, depois consolidar para mensais, reduzindo o espaço em 12x para dados antigos.
Verificar o resultado
Para confirmar que a snapshot de inventário está correcta:
- Consultar alguns produtos e datas e comparar com a soma de
inventory_transactionsaté essa data:SELECT SUM(qty_change) .... Faça amostragens (10-20 produtos) e validações automatizadas. - Verificar que há exactamente uma linha por
snapshot_date+product_id(a chave primária garante isto). - Testar o processo incremental reenviando o mesmo dia e confirmando que é idempotente (não cria duplicados e actualiza
load_ts). - Medir performance: comparar tempo de relatório com e sem snapshot; é comum observar redução de 70-99% no tempo de execução.
Conclusão
Criar uma snapshot de inventário em Data Warehouse simplifica análises históricas e acelera relatórios ao evitar recomputação a partir de transacções. Próximos passos: automatize com o seu ETL, implemente particionamento, avalie compressão/retenção de dados e crie testes de regressão. Dica: compare a performance entre calcular saldos on-demand e usar a tabela de snapshot para o seu relatório mais crítico — os ganhos costumam justificar o esforço de implementação e operação.