Acho que provavelmente pretendia acrescentar esse comentário à resposta anterior, sobre duas declarações separadas. Foi há mais de um ano, então não tenho mais certeza.
A consulta baseada no wCTE realmente não resolve o problema que deveria, mas após revisá-la mais de um ano depois, não vejo a possibilidade de atualizações perdidas na versão do wCTE.
(Observe que todas essas soluções só funcionarão bem se você tentar alterar exatamente uma linha com cada transação. Assim que você tentar fazer várias alterações em uma transação, as coisas ficarão complicadas devido à necessidade de repetir loops nas reversões. No mínimo você precisaria usar um ponto de salvamento entre cada alteração.)
Versão de duas instruções sujeita a atualizações perdidas.
A versão que usa duas instruções separadas está sujeita a atualizações perdidas, a menos que o aplicativo verifique a contagem de linhas afetadas da UPDATE
instrução e a INSERT
instrução e tente novamente se ambas forem zero.
Imagine o que acontece se você tiver duas transações READ COMMITTED
isoladas.
- TX1 executa o
UPDATE
(sem efeito)
- TX1 executa o
INSERT
(insere uma linha)
- TX2 executa o
UPDATE
(sem efeito, a linha inserida por TX1 ainda não está visível)
- TX1
COMMIT
s.
- TX2 executa o
INSERT
*, que obtém um novo instantâneo que pode ver a linha confirmada por TX1. A EXISTS
cláusula retorna true, porque o TX2 agora pode ver a linha inserida pelo TX1.
Então TX2 não tem efeito. A menos que o aplicativo verifique o número de linhas da atualização, a inserção e as tentativas novamente, se ambos reportarem zero linhas, ele não saberá que a transação não teve efeito e continuará alegremente.
A única maneira de verificar as contas de linha afetadas é executá-la como duas instruções separadas, em vez de uma multi-instrução, ou usar um procedimento.
Você pode usar o SERIALIZABLE
isolamento, mas ainda precisará de um loop de repetição para lidar com falhas de serialização.
A versão do wCTE protege contra o problema de atualizações perdidas porque INSERT
depende de se UPDATE
afeta alguma linha, em vez de uma consulta separada.
O wCTE não elimina violações exclusivas
A versão gravável do CTE ainda não é um upsert confiável.
Considere duas transações que executam isso simultaneamente.
Ambos executam a cláusula VALUES.
Agora, os dois executam a UPDATE
parte. Como não há linhas correspondentes à UPDATE
cláusula s where, os dois retornam um conjunto de resultados vazio da atualização e não fazem alterações.
Agora ambos executam a INSERT
parte. Como o UPDATE
retorno de zero linhas para as duas consultas, ambas tentam INSERT
a linha.
Um consegue. Um lança uma violação única e aborta.
Isso não é motivo de preocupação com a perda de dados, desde que o aplicativo verifique os resultados de erro de suas consultas (ou seja, qualquer aplicativo decentemente escrito) e tente novamente, mas torna a solução não melhor do que as versões de duas instruções existentes. Não elimina a necessidade de um loop de repetição.
A vantagem que o wCTE oferece sobre a versão de duas instruções existente é que ele usa a saída da UPDATE
para decidir se deve INSERT
, em vez de usar uma consulta separada na tabela. Isso é parcialmente uma otimização, mas em parte protege contra um problema com a versão de duas instruções que causa atualizações perdidas; ver abaixo.
Você pode executar o wCTE SERIALIZABLE
isoladamente, mas obterá falhas de serialização em vez de violações exclusivas. Isso não mudará a necessidade de um loop de repetição.
O wCTE não parece vulnerável a atualizações perdidas
Meu comentário sugeriu que essa solução poderia resultar em atualizações perdidas, mas, ao revisar, acho que posso estar enganado.
Já faz mais de um ano, e não me lembro das circunstâncias exatas, mas acho que provavelmente perdi o fato de que índices únicos têm uma exceção parcial das regras de visibilidade de transações, a fim de permitir que uma transação de inserção espere a outra inserir ou rolar antes de prosseguir.
Ou talvez eu tenha perdido o fato de que o INSERT
wCTE depende de se as UPDATE
linhas afetaram alguma, e não da linha candidata existir na tabela.
INSERT
S conflitantes em uma espera de índice exclusiva para confirmação / reversão
Digamos que uma cópia da consulta seja executada, inserindo uma linha. A mudança ainda não foi confirmada. A nova tupla existe no heap e no índice exclusivo, mas ainda não está visível para outras transações, independentemente dos níveis de isolamento.
Agora outra cópia da consulta é executada. A linha inserida ainda não está visível, pois a primeira cópia não foi confirmada; portanto, a atualização não corresponde a nada. A consulta continuará tentando inserir, o que verá que outra transação em andamento está inserindo a mesma chave e bloqueará a espera pela confirmação ou reversão dessa transação .
Se a primeira transação for confirmada, a segunda falhará com uma violação única, conforme o descrito acima. Se a primeira transação reverter, a segunda continuará com sua inserção.
O INSERT
ser dependente do UPDATE
número de linhas protege contra atualizações perdidas
Ao contrário do caso de duas declarações, não acho que o wCTE seja vulnerável a atualizações perdidas.
Se o UPDATE
não tiver efeito, o INSERT
sempre será executado, porque é estritamente condicional se o UPDATE
item fez alguma coisa, não no estado da tabela externa. Portanto, ainda pode falhar com uma violação única, mas silenciosamente não pode ter nenhum efeito e perder a atualização completamente.