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.

Keshan
fonte
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
Para obter o ID influenciado, consulte MySQL ON DUPLICATE KEY - último código de inserção?
Kris Roofe
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
Donnie
fonte
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)
Martin Schapendonk
fonte
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),
    ...
Fabiano Souza
fonte
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.

Luis Reyes
fonte
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á.

Rasel
fonte
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

Dilraj Singh
fonte
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).

DawnSong
fonte
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.

SteveCinq
fonte
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)
Rico
fonte
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 ...

Renish Gotecha
fonte
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;
Xman Classical
fonte