Temos esse ecossistema de soluções contábeis e, basicamente, ele ficou com duas tabelas:
dbo.AccountFluxRecord
(
Id,
TotalCredits,
TotalDebits,
CreditMinusDebit,
Application (the application used to insert the record)
)
dbo.AccountFluxRecordDetail
(
Id,
AccountFluxId, (FK to above table)
Type, (Credit or Debit)
Value,
CreditBankAccountId, (nullable FK)
DebtBankAccountId (nullable FK)
)
Temos alguns aplicativos usando e todos usam um procedimento armazenado para inserir novos registros.
SP_InsertAccountFluxRecord
Passamos a lista de detalhes para o SP como um tipo de tabela de usuário personalizado. Ele faz muitas validações para garantir que todos os registros tenham pelo menos um crédito e um débito e a soma dos créditos menos os débitos seja zero. Também valida se BankAccountId é válido para cada tipo de operação.
Está funcionando perfeitamente, há alguns anos, em centenas de clientes, inserindo muitos milhões de registros a cada semana. Hospedamos o banco de dados para a maioria de nossos clientes, mas alguns deles insistem que seus dados devem ser hospedados internamente. Estamos bem com isso, pois podemos obter um canal aberto para manutenção.
Hoje tivemos um problema com um desses clientes auto-hospedando seu banco de dados.
Encontramos uma dúzia de registros "ruins". Esses registros estão faltando os detalhes da dívida, o que é impossível, pois esse aplicativo específico cria um AccountFluxRecord a partir de um par de detalhes de débito/crédito, um é o espelho do outro. Ele não pode nem aceitar uma entrada de usuário sem um par válido de contas bancárias, mas descobrimos que o campo DebtBankAccountId é nulo nesses registros inválidos.
Mantemos um registro de todas as operações, o registro reflete os registros ruins que são inseridos como estão, não há exclusões ou atualizações sobre eles. Obs: Usamos o modo de transação de snapshot para evitar alguns problemas de deadlock que tivemos no passado.
TLDR Eu tenho uma dúzia de registros inválidos, inseridos por um SP feito para bloquear registros inválidos a serem inseridos, como se todas as validações de aplicativos e procedimentos armazenados fossem ignoradas. Abaixo há um trecho mostrando como as validações são tratadas dentro do SP.
IF (@SumDetailDebit <> @SumDetailCredit)
BEGIN
set @Mesage = @ErrorPrefix + N'TOTAL DEBIT IS DIFFERENT FROM CREDIT!';
THROW 50000, @Mesage ,1
END
Descrição: Dentro de um SP existe um cursor inserindo um registro de detalhe para cada iteração, após realizar algumas validações. Uma inserção pode ser confirmada e outra pode ser retrovertida mesmo se não estivermos usando o controle de transação dentro do loop?
Depois de muito testar encontrei a resposta. Sim, pode desde que você o execute AD HOC, sem controle de transação.
A aplicação obteve um sólido controle de transações.
O problema se originou porque alguém estava adulterando a base. Acho que alguém colocou um perfil para executar e tentou imitar algumas chamadas de aplicativos.