Histórico temporal no SQL Server: o significado de AS OF
Consulte versões antigas distinguindo tempo do sistema, vigência de negócio, início de transações e requisitos adicionais de auditoria.
Quando um preço muda de 10 para 12, uma tabela comum guarda apenas o novo valor, salvo histórico criado pela aplicação. Uma tabela temporal versionada pelo sistema pode preservar automaticamente versões anteriores. Isso ajuda investigações, mas perguntas históricas diferentes exigem respostas diferentes.
Separe o valor registrado pelas regras de tempo do SQL Server, a vigência pretendida pelo negócio e quem autorizou a alteração. O recurso temporal atende diretamente à primeira questão. As demais precisam de informações e processos complementares.
Observar presente e histórico
O exemplo usa SQL Server 2016 ou posterior e cria tabelas permanentes de prática. Execute com autocommit, sem uma transação externa.
-- Use a disposable database, autocommit, and no enclosing transaction.
IF @@TRANCOUNT <> 0 THROW 50001, 'Use a separate practice connection.', 1;
CREATE TABLE dbo.PriceTemporalDemo (
ProductId int NOT NULL PRIMARY KEY,
Price decimal(12,2) NOT NULL,
ValidFrom datetime2(7) GENERATED ALWAYS AS ROW START NOT NULL,
ValidTo datetime2(7) GENERATED ALWAYS AS ROW END NOT NULL,
PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo)
) WITH (SYSTEM_VERSIONING = ON (
HISTORY_TABLE = dbo.PriceTemporalDemoHistory
));
INSERT dbo.PriceTemporalDemo(ProductId, Price) VALUES (1, 10.00);
DECLARE @BeforeChange datetime2(7) = SYSUTCDATETIME();
WAITFOR DELAY '00:00:01';
UPDATE dbo.PriceTemporalDemo SET Price = 12.00 WHERE ProductId = 1;
SELECT ProductId, Price FROM dbo.PriceTemporalDemo WHERE ProductId = 1;
SELECT ProductId, Price, ValidFrom, ValidTo
FROM dbo.PriceTemporalDemo FOR SYSTEM_TIME AS OF @BeforeChange
WHERE ProductId = 1;
A consulta atual retorna 12,00 e AS OF @BeforeChange retorna 10,00. A sintaxe temporal procura as versões pertinentes nas tabelas atual e histórica sem união manual.
O período usa datetime2 em UTC. AS OF escolhe uma versão com início anterior ou igual ao instante solicitado e fim posterior. O limite final é exclusivo. Forneça um parâmetro convertido corretamente para UTC, não um horário local com deslocamento presumido.
A pausa apenas separa os instantes da demonstração. Não faz parte de uma solução produtiva. Sem separação, um teste rápido pode tornar os limites difíceis de distinguir, especialmente após mudança de precisão.
Esta consulta mostra os intervalos disponíveis.
SELECT ProductId, Price, ValidFrom, ValidTo
FROM dbo.PriceTemporalDemo FOR SYSTEM_TIME ALL
WHERE ProductId = 1
ORDER BY ValidFrom, ValidTo;
Uma atualização pode criar histórico mesmo atribuindo valores iguais. Evite gravações desnecessárias quando geram crescimento caro, mas preserve mudanças que tenham significado de negócio.
Entender o relógio transacional
Os limites utilizam o início da transação, não seu commit. Uma transação longa pode criar uma versão cujo início antecede o momento em que outra conexão conseguiu observar o valor confirmado. AS OF segue essa semântica; não registra exatamente o que cada leitor concorrente enxergou.
Múltiplas alterações na mesma linha e transação podem produzir versões de duração zero. As cláusulas temporais excluem essas versões. Consultar diretamente a tabela histórica pode mostrar registros ausentes de FOR SYSTEM_TIME. Nem todo estado intermediário ausente representa perda de dados.
Se um preço informado hoje deve valer no próximo mês, armazene uma data ou intervalo de negócio separado. Não tente usar o período gerado para representar essa agenda. Uma correção retroativa também deve preservar a diferença entre vigência e momento em que a base recebeu a informação.
Usar o mesmo AS OF em várias tabelas temporais facilita junções históricas. Ainda assim, confira tabelas não temporais participantes. Uma ordem antiga combinada ao nome atual de uma categoria mutável produz um relatório de tempos misturados.
Operar histórico como dados de verdade
Estime crescimento pela frequência de atualização e largura das linhas, não apenas pela quantidade atual. Uma tabela pequena e muito modificada pode acumular histórico significativo. Indexe o padrão real: versões de um produto diferem de reconstruir todo o conjunto em um instante.
Retenção e limpeza devem ser intencionais. Relatórios não reconstroem versões já removidas. Documente o horizonte disponível e monitore a limpeza adequada à versão implantada.
Histórico temporal não substitui backup nem auditoria imutável. Não registra automaticamente responsável e motivo, e manutenção privilegiada pode alterar sua configuração. Guarde atribuições necessárias separadamente com controle de acesso apropriado.
Planeje alterações de esquema e manutenção nas tabelas atual e histórica. Desligar SYSTEM_VERSIONING cria um intervalo sem captura automática. Ao religar, informe explicitamente a tabela histórica pretendida para não iniciar outra por acidente.
Teste atualização, exclusão, transação longa, várias mudanças na mesma transação e limites exatos de versão. O projeto útil permite distinguir o que o sistema registrou do que o negócio desejava representar.
Referências técnicas: Microsoft Learn: Query temporal data · Microsoft Learn: Temporal considerations.