Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Violação da restrição UNIQUE KEY em INSERT WHERE COUNT(*) =0 no SQL Server 2005


Por que isso não funciona?

Acredito que o comportamento padrão do SQL Server é liberar bloqueios compartilhados assim que eles não forem mais necessários. Sua subconsulta resultará em um bloqueio compartilhado (S) de curta duração na tabela, que será liberado assim que a subconsulta for concluída.

Neste ponto, não há nada que impeça uma transação simultânea de inserir a mesma linha que você acabou de verificar que não estava presente.

Que modificação preciso fazer para que não haja chance de uma exceção devido à violação da restrição?

Adicionando o HOLDLOCK dica para sua subconsulta instruirá o SQL Server a manter o bloqueio até que a transação seja concluída. (No seu caso, esta é uma transação implícita.) O HOLDLOCK dica é equivalente ao SERIALIZABLE dica, que em si é equivalente ao nível de isolamento de transação serializável ao qual você se refere em sua lista de "outras abordagens".

O HOLDLOCK dica por si só seria suficiente para reter o bloqueio S e impedir que uma transação simultânea insira a linha contra a qual você está se protegendo. No entanto, você provavelmente encontrará seu erro de violação de chave exclusivo substituído por deadlocks, ocorrendo na mesma frequência.

Se você está retendo apenas um bloqueio S na tabela, considere uma corrida entre duas tentativas simultâneas de inserir a mesma linha, procedendo em passo de bloqueio -- ambos conseguem adquirir um bloqueio S na tabela, mas nenhum consegue adquirir o exclusivo (X) trava necessária para executar a inserção.

Felizmente, há outro tipo de bloqueio para esse cenário exato, chamado de bloqueio Update (U). O bloqueio U é idêntico a um bloqueio S com a seguinte diferença:enquanto vários bloqueios S podem ser mantidos simultaneamente no mesmo recurso, apenas um bloqueio U pode ser mantido por vez. (Dito de outra forma, enquanto os bloqueios S são compatíveis entre si (ou seja, podem coexistir sem conflito), os bloqueios U não são compatíveis entre si, mas podem coexistir ao lado dos bloqueios S; e mais adiante no espectro, os bloqueios Exclusivos (X) não são compatível com fechaduras S ou U)

Você pode atualizar o bloqueio S implícito em sua subconsulta para um bloqueio U usando o UPDLOCK dica.

Duas tentativas simultâneas de inserir a mesma linha na tabela agora serão serializadas na instrução select inicial, pois isso adquire (e mantém) um bloqueio U, que não é compatível com outro bloqueio U da tentativa de inserção simultânea.

valores NULOS

Um problema separado pode surgir do fato de que FieldC permite valores NULL.

Se ANSI_NULLS está ativado (padrão), então a verificação de igualdade FieldC=NULL retornaria false, mesmo no caso de FieldC ser NULL (você deve usar o IS NULL operador para verificar nulo quando ANSI_NULLS está ligado). Como FieldC é anulável, sua verificação duplicada não funcionará ao inserir um valor NULL.

Para lidar corretamente com nulos, você precisará modificar sua subconsulta EXISTS para usar o IS NULL operador em vez de = quando um valor de NULL está sendo inserido. (Ou você pode alterar a tabela para não permitir NULLs em todas as colunas em questão.)

Referências dos Manuais Online do SQL Server
  • Dicas de bloqueio
  • Bloquear Matriz de Compatibilidade
  • ANSI_NULLS