Muitas pessoas irão sugerir que você use
MERGE
, mas eu o alerto contra isso. Por padrão, ele não protege você de condições de concorrência e corrida mais do que várias instruções, mas apresenta outros perigos:- Tenha cuidado com a instrução MERGE do SQL Server
- O que evitar se você quiser usar MERGE
- Padrões e antipadrões UPSERT do SQL Server
Mesmo com essa sintaxe "mais simples" disponível, ainda prefiro essa abordagem (tratamento de erros omitido por brevidade):
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE dbo.table SET ... WHERE PK = @PK;
IF @@ROWCOUNT = 0
BEGIN
INSERT dbo.table(PK, ...) SELECT @PK, ...;
END
COMMIT TRANSACTION;
Mais informações sobre este
UPSERT
abordagem aqui:- Pare de usar este antipadrão UPSERT
Muita gente vai sugerir desta forma:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
IF EXISTS (SELECT 1 FROM dbo.table WHERE PK = @PK)
BEGIN
UPDATE ...
END
ELSE
BEGIN
INSERT ...
END
COMMIT TRANSACTION;
Mas tudo isso garante que você precise ler a tabela duas vezes para localizar as linhas a serem atualizadas. Na primeira amostra, você só precisará localizar a(s) linha(s) uma vez. (Em ambos os casos, se nenhuma linha for encontrada na leitura inicial, ocorrerá uma inserção.)
Outros irão sugerir desta forma:
BEGIN TRY
INSERT ...
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 2627
UPDATE ...
END CATCH
No entanto, isso é problemático se por nenhum outro motivo além de permitir que o SQL Server capture exceções que você poderia ter evitado em primeiro lugar for muito mais caro, exceto no raro cenário em que quase todas as inserções falham. Provo isso aqui:
- Verificando possíveis violações de restrições antes de inserir TRY/CATCH
- Impacto no desempenho de diferentes técnicas de tratamento de erros
Não tenho certeza do que você acha que ganha por ter uma única declaração; Acho que você não ganha nada.
MERGE
é uma única instrução, mas ainda precisa executar várias operações de qualquer maneira - mesmo que faça você pensar que não.