Procedimento armazenado no SQL Server INSERT INTO table1 VALUES A , B
O conjunto de valores A tem como um dos parâmetros: (SELECT TOP 1 cashbalance - 1 FROM table1 WHERE userid = __ ORDER BY createdUTC, ID)
O conjunto de valores B tem como um dos parâmetros: (SELECT TOP 1 cashbalance + 1 FROM table1 WHERE userid = __ ORDER BY createdUTC, ID)
Quando executado como uma inserção múltipla então a instrução SELECT sóéexecutada uma vez portanto se o id de utilizador for o mesmo para ambos os conjuntos de valores então o campo de saldo de caixa que diz começou em 0 regista -1 na primeira linha inserida e 1 (isto é, não volta a 0 como deveria) na segunda linha inserida. Além disso, o campo createdUTC para ambas as linhas registra exatamente o mesmo valor.
Quando executado como instruções INSERT separadas e sequenciais, o campo cashbalance calcula corretamente, mas o campo createdUTC tem uma pequena lacuna - mas infere a preocupação com outra inserção/leitura entrando.
Obviamente relacionado a bloqueios e/ou serialização - que estou pesquisando e postarei as descobertas (embora sugestões sejam muito bem-vindas). Eu poderia, alternativamente, SELECT SUM(table1.amounts) +/- 1 WHERE userid = __ (e observe que 1 é na verdade uma variável), mas isso não resolve nenhum dos problemas. Além disso, este sp é uma etapa de um processo de quatro sp (mais outro sp de chamada) e isso lança mais complexidade sobre onde implementar quaisquer bloqueios ou trans.
Idealmente, estou tentando implementar contabilidade de entrada dupla (ou seja, cada transação tem uma linha debitando A e outra linha creditando B no mesmo valor simultaneamente) com IDs imediatamente sequenciais e o mesmo createdUTC.
Para o caso de uso que você apresentou, você pode realmente se safar apenas lendo uma
cashbalance
vez, fazendo suas manipulações e salvando-as em variáveis locais e, em seguida, usando-as como parâmetros para a chamada do procedimento da seguinte forma:Ao ler a tabela apenas uma vez, primeiro e depois fazer suas manipulações, você tem uma leitura atômica do
cashbalance
valor que é constante para todas as suas manipulações. Isso também melhora o tempo de execução de sua consulta e reduz o tempo de bloqueio da tabela que está sendo lida. Todas as coisas boas.Isso depende apenas do nível de isolamento da transação e de como você deseja que o sistema funcione. Por padrão, com
READ COMMITTED
o nível de isolamento, os escritores bloqueiam os leitores (como você sabe). A ideia é que, se uma gravação começou antes da ocorrência da leitura, está ocorrendo uma alteração que deve ser concluída antes que a leitura obtenha o valor, para estar correto.Se você deseja que as leituras possam acessar a versão anterior da linha, quando ela está atualmente bloqueada por uma gravação, você pode alterar seu nível de isolamento para
SNAPSHOT
ouREAD COMMITED SNAPSHOT ISOLATION
também conhecido como RCSI. Então a leitura não esperará que a gravação termine. Aqui estão algumas informações adicionais sobre como habilitar esses níveis de isolamento .