Como inserir ou atualizar usando uma única consulta?

25

Eu tenho um teste de tabela com colunas id qual chave primária e auto incrementado e nome. Quero inserir um novo registro, se somente não houver registros.Por exemplo

input é id = 30122 e name = john

se houver registros com o ID 30122, atualizei a coluna de nome para john; se não houver registros, insira um novo registro.

Eu posso fazer usando 2 consultas como

select * from test where id=30122

se tem alguns registros então eu posso usar update test set name='john' where id=3012

ou se não tiver registros, eu posso usar

insert into test(name) values('john')

Mas eu queria usar a consulta única?

Alguém pode dizer se é possível?

SpringLearner
fonte
1
But I wanted to use single query?Por quê?
Aaron Bertrand
@AaronBertrand My back-end é desenvolvido usando java.So Se eu usar 2 quries então eu tenho que bater o DB 2 times.So se ele pode ser feito usando uma única consulta, em seguida, por que usar 2 consultas
SpringLearner
1
Java não suporta um procedimento armazenado ou um único lote com duas instruções que requerem apenas uma ocorrência no banco de dados?
Aaron Bertrand
@AaronBertrand você poderia dar um exemplo de como você lidaria com isso com o sql server 2008 ou posterior?
eaglei22
1
@ eaglei22 Eu usaria o segundo exemplo na resposta do vijayp abaixo. Eu ainda não escolheria MERGEnenhuma versão, nem o SQL Server 2019. Alguns antecedentes sobre isso aqui .
Aaron Bertrand

Respostas:

41

Você pode tentar isso

IF EXISTS(select * from test where id=30122)
   update test set name='john' where id=3012
ELSE
   insert into test(name) values('john');

Outra abordagem para obter melhor desempenho é

update test set name='john' where id=3012
IF @@ROWCOUNT=0
   insert into test(name) values('john');

e também leia esses maus hábitos para chutar no prefixo do esquema

vijayp
fonte
4
O primeiro exemplo é um desperdício e muitas vezes pode levar a conflitos - eu não sugeriria nada.
Aaron Bertrand
@AaronBertrand gostaria de elaborar? Obrigado
Hexo
5
@SlapY Claro, no primeiro exemplo, você está dizendo: "Ei, SQL Server, há uma linha com esse ID?" O SQL Server sai para encontrar a linha, talvez usando uma verificação, e depois volta com a resposta. "Por que, sim, usuário, eu tenho uma linha com esse ID!" Então você diz: "Ok, SQL Server, encontre a linha novamente , mas desta vez, atualize-a!" Você vê como realizar duas vezes a busca ou digitalização é um desperdício? Você pode imaginar o que acontece se outro usuário fizer ao SQL Server a mesma pergunta sobre a existência de uma linha, antes de passar a fazer algo sobre isso?
Aaron Bertrand
Obrigado, não vejo por que o primeiro ameaça entrar em conflito, enquanto o segundo não. Ambos consistem em várias instruções que podem ser interceptadas se não executadas com bloqueio completo. Estou errado?
Hexo 21/08/19
2
@ 0x25b3 Não é que um esteja ameaçado por impasses e o outro não, é que o primeiro exemplo é muito mais propenso a eles. Você deve estar envolvido em uma transação completa e adequada em ambos os casos, mas as pessoas não, então ...
Aaron Bertrand
17

Supondo que o SQL Server 2008 ou posterior, você poderia usar MERGE:

Tabela

CREATE TABLE dbo.Test
(
    id integer NOT NULL,
    name varchar(30) NULL,

    CONSTRAINT PK_dbo_Test__id
        PRIMARY KEY CLUSTERED (id)
);

Inquerir

MERGE dbo.Test WITH (SERIALIZABLE) AS T
USING (VALUES (3012, 'john')) AS U (id, name)
    ON U.id = T.id
WHEN MATCHED THEN 
    UPDATE SET T.name = U.name
WHEN NOT MATCHED THEN
    INSERT (id, name) 
    VALUES (U.id, U.name);

A SERIALIZABLEdica é necessária para a operação correta em alta simultaneidade .

Você pode encontrar uma comparação dos métodos comuns de Michael J. Swart aqui:

Mythbusting: soluções simultâneas de atualização / inserção

Evaldas Buinauskas
fonte
8
A mesclagem tem alguns problemas .
VonPryz
o elo destruidor de mitos é excelente. Agradável!
JonnyRaa 20/11