Inserir em uma tabela MySQL ou atualizar se existir


875

Desejo adicionar uma linha a uma tabela de banco de dados, mas se existir uma linha com a mesma chave exclusiva, desejo atualizar a linha.

Por exemplo:

insert into table (id, name, age) values(1, "A", 19)

Digamos que a chave exclusiva seja ide, no meu banco de dados , há uma linha com id = 1. Nesse caso, quero atualizar essa linha com esses valores. Normalmente, isso gera um erro.
Se eu usar, insert IGNOREele ignorará o erro, mas ainda não será atualizado.


6
O SQL precisa de uma sintaxe oficial para esse caso de uso que não force a duplicação de valores na sintaxe e preserve a chave primária.
Pete Alvin


Advertência: a partir da versão 5.7, essa abordagem não suporta diretamente a cláusula WHERE como parte da operação INSERT / UPDATE. Além disso, um UPDATE realmente conta como duas operações separadas (DELETE e INSERT) ... caso isso importe para fins de auditoria. (Learnbit)
dreftymac

Respostas:


1600

Usar INSERT ... ON DUPLICATE KEY UPDATE

INQUERIR:

INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE    
name="A", age=19

86
+1 Pelo que eu descobri, esse método é menos problemático para chaves de incremento automático e outras colisões de chaves exclusivas do que REPLACE INTO, e é mais eficiente.
Andrew Ensley

46
Eu sei que todos vocês aludem a isso, mas quero ser explícito para os outros. Se o ID que você inserir NÃO for a PRIMARY KEY ou UNIQUE, isso não funcionará. Inicialmente, isso não funcionou para mim porque meu ID não era único.
Keven

7
Gostaria de saber por que affected row countresulta 2quando atualizar com êxito (em chave duplicada) linha única? Alguém mais teve esse resultado? (Os dados são atualizados corretamente: ou seja, apenas 1 linha é atualizada)
Dimitry K

15
Isso é um pouco tarde, mas de qualquer maneira: é afirmado no manual que as atualizações ON DUPLICATE KEY UPDATEaumentam as linhas afetadas em 2. Ele informa 0 se nada é realmente atualizado (o mesmo que o normal UPDATE).
Vatev 31/03

61
Observe também que você pode usar VALUES (nome) para fazer referência ao valor que você tenta inserir, por exemplo, INSERT INTO na tabela (id, nome, idade) VALUES (1, "A", 19) NA ATUALIZAÇÃO DUPLICATIVA DE CHAVES name = VALUES (name) , idade = VALUES (idade)
Sam curioso 1/16

246

Confira REPLACE

http://dev.mysql.com/doc/refman/5.0/en/replace.html

REPLACE into table (id, name, age) values(1, "A", 19)

11
@Piontek Porque este é mais curto e fácil de entender e ninguém explicou por que "inserir em duplicado" é melhor.
Mr_Chimp

77
altera os IDs do registro e, portanto, pode destruir referências estrangeiras.
boh 13/09/13

102
O outro problema com REPLACE INTO é que você deve especificar valores para TODOS os campos ... caso contrário, os campos serão perdidos ou substituídos pelos valores padrão. REPLACE INTO exclui essencialmente a linha, se existir, e insere a nova linha. No exemplo, se você 'REPLACE INTO table (id, age) valores (1, 19), o campo de nome se tornará nulo.
Dale

33
Na verdade, é DELETE a linha inteira e executa o novo INSERT.
Mjb

8
@mjb Portanto, você precisa ter privilégios DELETE, o que é melhor não ter se não precisar deles. O usuário do banco de dados do meu ambiente possui apenas permissões INSERT e UPDATE, portanto, REPLACE não funcionará.
Ndm13 5/06

54

Ao usar a inserção em lote, use a seguinte sintaxe:

INSERT INTO TABLE (id, name, age) VALUES (1, "A", 19), (2, "B", 17), (3, "C", 22)
ON DUPLICATE KEY UPDATE
    name = VALUES (name),
    ...

Muito útil se você quiser manipular o novo valor
Hunger

Se VALUES(name)não funcionar, você pode tentarname = 'name_value',...;
Son Pham

26

Tente isto:

INSERT INTO table (id, name, age) VALUES (1, 'A', 19) ON DUPLICATE KEY UPDATE id = id + 1;

Espero que isto ajude.


6
na verdade, não preciso adicionar os novos valores a outra linha com um novo ID. Em vez disso, quero substituir os valores existentes de id = 1 por esses valores. (como eu entendo isso incrementos do id e adicionar os dados)
Keshan

49
Eu não acho que ele queira aumentar a identificação em um em duplicatas.
Donnie

7
"transgredir" não é a palavra que você está procurando :) Infelizmente, agora eu já vi "transgredir", eu já não pode visualizar a palavra real ..
mwfearnley

1
Você estava procurando "travessias" ou "capas"?
Chris Dev

4
@mwfearnley A palavra que você estava tentando visualizar é "transcende". Só levou um ano e meio :-)
Michael Krebs

20

Tente o seguinte:

INSERT INTO table (id,name,age) VALUES('1','Mohammad','21') ON DUPLICATE KEY UPDATE name='Mohammad',age='21'

Nota:
Aqui, se id for a chave primária, após a primeira inserção, id='1'sempre que a tentativa de inserção id='1'atualizar o nome e a idade, o nome anterior mudará.


Eu quero trabalhar sem usar o id . Você tentou sem chave primária?
Ersks 6/07

@ersks veja a pergunta. user perguntado sobre quando há uma chave única
Rasel

Entendi, mas estou tentando resolver meu problema em que esses únicos valores são conhecidos. Então, em tal situação, escrevi o comentário acima, esperando a solução correta.
Ersks

15
INSERT IGNORE INTO table (id, name, age) VALUES (1, "A", 19);

INSERT INTO TABLE (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE NAME = "A", AGE = 19;

REPLACE INTO table (id, name, age) VALUES(1, "A", 19);

Todas essas soluções funcionarão em relação à sua pergunta.

Se você quiser saber detalhes sobre essas declarações, visite este link


REPLACEnão está documentado no documento vinculado.
Raio Baxter

consultas sql cheias de erros de digitação, seus exemplos não funcionarão. É INSERT INTO TABLE (não INTO TABLE) e ON DUPLICATE KEY UPDATE (não ON DUPLICATE UPDATE SET). Não tenho certeza que está upvoting isso
Robert Sinclair

1
Desculpe pelo erro de digitação. Obrigado por me corrigir
Dilraj Singh

11

Ao usar o SQLite:

REPLACE into table (id, name, age) values(1, "A", 19)

Desde que idseja a chave primária. Ou então, apenas insere outra linha. Consulte INSERIR (SQLite).


O que replace intofaz é exatamente "inserir ou atualizar quando existente". @Owl
DawnSong

+1 (1 de 3) Achei que isso funcionava para a minha situação - eu precisava substituir uma linha existente por uma chave exclusiva ou, se não houver, adicionar a linha. Soluções mais simples aqui.
Therobyouknow 30/01

(2 de 3) Minha consulta:CREATE TRIGGER worklog_update AFTER INSERT ON worklog FOR EACH ROW REPLACE INTO support_time_monthly_hours ( ProjectID, monthTotalTime, Year, Month ) SELECT jiraissue.PROJECT, SUM(worklog.timeworked), YEAR(CURRENT_DATE()), MONTH(CURRENT_DATE()) FROM worklog, jiraissue WHERE worklog.issueid = jiraissue.ID AND jiraissue.PROJECT = (SELECT PROJECT FROM jiraissue WHERE NEW.issueid = jiraissue.ID ) AND worklog.startdate BETWEEN DATE_FORMAT(NOW() ,'%Y-%m-01 00:00:00') AND NOW();
therobyouknow

(3 de 3) Pergunta / resposta relacionada stackoverflow.com/questions/41767517/…
therobyouknow

2
O problema com isso no mysql é que replace removerá outros valores caso eles não sejam fornecidos. No entanto solução @ fabiano-souza é mais apropriado
Quamber Ali

11

Só porque eu estava aqui procurando esta solução, mas atualizando de outra tabela estruturada de forma idêntica (no meu caso, teste o banco de dados para o banco de dados ativo):

INSERT  live-db.table1
SELECT  *
FROM    test-db.table1 t
ON DUPLICATE KEY UPDATE
        ColToUpdate1 = t.ColToUpdate1,
        ColToUpdate2 = t.ColToUpdate2,
        ...

Como mencionado em outro lugar, somente as colunas que você deseja atualizar precisam ser incluídas depois ON DUPLICATE KEY UPDATE.

Não é necessário listar as colunas no INSERTou SELECT, embora eu concorde que provavelmente seja uma prática melhor.


4

Caso deseje criar um non-primarycampo como critério / condição ON DUPLICATE, é possível criar uma UNIQUE INDEXchave nessa tabela para acionar o DUPLICATE.

ALTER TABLE `table` ADD UNIQUE `unique_index`(`name`);

Caso deseje combinar dois campos para torná-lo único na tabela, você pode conseguir isso adicionando mais no último parâmetro.

ALTER TABLE `table` ADD UNIQUE `unique_index`(`name`, `age`);

Note, apenas certifique-se de apagar primeiro todos os dados que tem o mesmo namee agevalor através das outras linhas.

DELETE table FROM table AS a, table AS b WHERE a.id < b.id 
AND a.name <=> b.name AND a.age <=> b.age;

Depois disso, ele deve acionar o ON DUPLICATEevento.

INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE    
name = VALUES(name), age = VALUES(age)

2

No meu caso, criei as consultas abaixo, mas na primeira consulta se id1 já existe e a idade já existe, depois disso, se você criar a primeira consulta sem ageque o valor de ageserá none

REPLACE into table SET `id` = 1, `name` = 'A', `age` = 19

para evitar o problema acima, crie uma consulta como abaixo

INSERT INTO table SET `id` = '1', `name` = 'A', `age` = 19 ON DUPLICATE KEY UPDATE `id` = "1", `name` = "A",`age` = 19

pode ajudá-lo ...


2
Alguém sabe por que devemos atribuir os valores duas vezes? Por que o MySQL não nos permite terminar a consulta em ON DUPLICATE KEY UPDATE sem duplicar todas as instruções de atribuição? Algumas tabelas de banco de dados têm muitas colunas, e isso parece redundante / gratuito. Entendo por que temos a opção de tarefas alternativas, mas por que não temos a opção de omiti-las também? Apenas curioso se alguém souber.
Blue Water

2

No caso, você deseja manter o campo antigo (por exemplo: nome). A consulta será:

INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE    
name=name, age=19;
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.