Como criar tabelas Ledger no Azure SQL: passo a passo
As tabelas Ledger do Azure SQL guardam um histórico protegido por criptografia de todas as alterações aos dados. Criar tabelas Ledger no Azure SQL permite provar a um auditor que nenhum registo foi adulterado, sem escrever uma linha de código na aplicação: o próprio motor regista cada INSERT, UPDATE e DELETE.
Pré-requisitos
- Uma base de dados no Azure SQL Database (a funcionalidade também existe no SQL Server 2022 e superior).
- Permissões para criar tabelas (por exemplo, ser membro de
db_owner). - SQL Server Management Studio (SSMS) ou Azure Data Studio ligado à base de dados.
- Noções básicas de T-SQL.
Passo 1: Escolher o tipo de tabela Ledger
Existem dois tipos e a escolha depende do que a tabela precisa de fazer:
- Updatable ledger table — aceita INSERT, UPDATE e DELETE. Cada alteração fica registada numa tabela de histórico e fica visível numa vista Ledger.
- Append-only ledger table — só aceita INSERT. Qualquer tentativa de UPDATE ou DELETE é bloqueada pelo motor. Ideal para logs de eventos ou registos de acessos.
Para dados de negócio que mudam (pagamentos, contratos, saldos) use a versão atualizável. Para dados que nunca deveriam mudar, use append-only.
Passo 2: Criar uma tabela Ledger atualizável
A criação é um CREATE TABLE normal com a cláusula LEDGER = ON. Vale a pena dar nomes explícitos à tabela de histórico e à vista Ledger, para depois as encontrar facilmente:
CREATE TABLE dbo.Pagamentos
(
PagamentoID INT IDENTITY(1,1) PRIMARY KEY,
Cliente NVARCHAR(100) NOT NULL,
Valor DECIMAL(10,2) NOT NULL,
Estado NVARCHAR(20) NOT NULL
)
WITH (
SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Pagamentos_History),
LEDGER = ON (LEDGER_VIEW = dbo.Pagamentos_Ledger)
);
O motor cria automaticamente colunas de sistema que identificam a transação que inseriu ou apagou cada versão da linha.
Passo 3: Inserir e alterar dados
A partir daqui trabalha com a tabela como com qualquer outra. Não é preciso sintaxe especial:
INSERT INTO dbo.Pagamentos (Cliente, Valor, Estado)
VALUES (N'Ana Silva', 250.00, N'Pendente');
UPDATE dbo.Pagamentos
SET Estado = N'Pago'
WHERE Cliente = N'Ana Silva';
Passo 4: Consultar o histórico na vista Ledger
A vista Ledger mostra o que aconteceu a cada linha ao longo do tempo. Um UPDATE aparece como um par de operações: a versão antiga é marcada como DELETE e a nova como INSERT.
SELECT Cliente,
Valor,
Estado,
ledger_transaction_id,
ledger_sequence_number,
ledger_operation_type_desc
FROM dbo.Pagamentos_Ledger
ORDER BY ledger_transaction_id, ledger_sequence_number;
É este par INSERT/DELETE que permite reconstruir o valor de qualquer linha em qualquer momento do passado.
Passo 5: Descobrir quem fez cada transação
A vista de sistema sys.database_ledger_transactions guarda o autor e o momento de cada transação registada:
SELECT t.transaction_id,
t.principal_name,
t.commit_time
FROM sys.database_ledger_transactions AS t
ORDER BY t.commit_time DESC;
Cruzando o transaction_id com o ledger_transaction_id da vista Ledger fica a saber quem mudou o quê e quando.
Passo 6: Criar uma tabela append-only
Para registos que nunca devem ser alterados, como um log de acessos:
CREATE TABLE dbo.Acessos
(
AcessoID INT IDENTITY(1,1) PRIMARY KEY,
Utilizador NVARCHAR(100) NOT NULL,
DataHora DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME()
)
WITH (LEDGER = ON (APPEND_ONLY = ON));
Verificar o resultado
Primeiro, confirme que as tabelas foram criadas com o tipo Ledger correto:
SELECT name, ledger_type_desc
FROM sys.tables
WHERE ledger_type_desc IS NOT NULL;
Depois, faça o teste que realmente interessa: tente alterar uma linha da tabela append-only.
UPDATE dbo.Acessos SET Utilizador = N'outro' WHERE AcessoID = 1;
O motor devolve um erro e a operação não acontece — nem o administrador da base de dados a consegue forçar. Na tabela atualizável, o UPDATE funciona, mas a versão anterior continua visível na vista dbo.Pagamentos_Ledger. Se as duas coisas acontecerem, está tudo correto.
Conclusão
Em poucos minutos passou a ter, na base de dados, uma prova de integridade que antes exigia triggers, tabelas de auditoria manuais e muita confiança. O passo seguinte é automatizar a geração de digests para armazenamento imutável e depois usar os procedimentos de verificação do Ledger para provar, matematicamente, que o histórico não foi tocado. Uma dica final: uma tabela Ledger não desaparece com um DROP TABLE — fica marcada como dropped e o histórico mantém-se. Qual das suas tabelas atuais ganharia mais em passar a append-only?