Na atualização de chave duplicada, igual à inserção

158

Eu procurei, mas não encontrei se é possível.

Eu tenho esta consulta MySQL:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)

O ID do campo possui um "índice exclusivo", portanto não pode haver dois deles. Agora, se o mesmo ID já estiver presente no banco de dados, eu gostaria de atualizá-lo. Mas realmente preciso especificar todos esses campos novamente, como:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=2,b=3,c=4,d=5,e=6,f=7,g=8

Ou:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=VALUES(a),b=VALUES(b),c=VALUES(c),d=VALUES(d),e=VALUES(e),f=VALUES(f),g=VALUES(g)

Eu especifiquei tudo que já está na inserção ...

Uma observação extra, eu gostaria de usar a solução alternativa para obter o ID!

id=LAST_INSERT_ID(id)

Espero que alguém possa me dizer qual é a maneira mais eficiente.

Roy
fonte
9
O AFAIK, o método de reajustar cada coluna, a=VALUES(a), b=VALUES(b), ...é o caminho que você precisa seguir. É assim que faço em todas as minhas INSERT ON DUPLICATE KEY UPDATEdeclarações.
precisa saber é o seguinte
4
Apenas para esclarecer, tudo o que foi declarado aqui está correto, desde que você entenda que a pergunta está sendo respondida: Você deve reformular todas as colunas que deseja atualizar? Sim, se você deseja inserir ou atualizar 8 colunas em uma tabela de 25 colunas, deve declarar as 8 colunas duas vezes - uma na parte de inserção e outra na parte de atualização. (Você pode pular a chave primária na parte da atualização, mas é mais fácil pré-formatar uma string "a = 1, b = 2, c = 3" e incorporá-la duas vezes.) No entanto, você NÃO precisa reexaminar as 17 colunas restantes você não quer mudar. Se se tornar uma atualização, eles manterão seus valores existentes.
Timberline
3
Eu adicionei esse comentário porque as perguntas e respostas me deixaram insegura sobre colunas não mencionadas com esse tipo de INSERT, que eu testei depois de aprender exatamente o que testar. INSERT ON DUPLICATE KEY UPDATE deixa colunas não mencionadas inalteradas em UPDATE, mas fornece valores padrão em INSERT. Com REPLACE INTO, as colunas não mencionadas sempre obtêm valores padrão, nunca valores existentes.
Timberline
1
Verifique stackoverflow.com/questions/2472229/… , acho que existe o que você está procurando #
Chococroc

Respostas:

82

A UPDATEinstrução é fornecida para que os campos mais antigos possam ser atualizados para um novo valor. Se seus valores mais antigos são iguais aos novos, por que você precisaria atualizá-lo?

Por exemplo. se as colunas apara gjá estão definidos como 2para 8; não haveria necessidade de atualizá-lo novamente.

Como alternativa, você pode usar:

INSERT INTO table (id,a,b,c,d,e,f,g)
VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY
    UPDATE a=a, b=b, c=c, d=d, e=e, f=f, g=g;

Para obter o idde LAST_INSERT_ID; você precisa especificar o aplicativo de back-end que está usando para o mesmo.

Para LuaSQL, a conn:getlastautoid()busca o valor.

hjpotter92
fonte
6
É apenas por exemplo. Em uma consulta da vida real, existem diferenças. Minha pergunta é bem simples: realmente preciso especificar todos os campos (e valores no meu primeiro exemplo) se forem iguais aos da inserção? Eu só quero inserir tudo ou se houver uma correspondência de valor exclusiva: atualizar tudo.
Roy
1
"Uma observação extra, eu gostaria de usar a solução alternativa para obter a identificação também!"
Agamemnus 17/10
1
@ Tresdin, tanto quanto posso dizer, é apenas açúcar sintático. Pessoalmente, nunca vi alguém usando a = VALUES (a), b = VALUES (b), etc. Talvez você possa atribuir vários valores à coluna? Não sei por que você não concatenaria os dados de antemão.
DuckPuncher
54
Se a tabela tbljá tiver linha (1,10), então: INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=VALUES(a)configurará a = 2 . Enquanto INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=adefinirá a = 10
Bùi Việt Thành
2
Por que todos os votos positivos? Isso não funciona. Como @ Bùi Việt Thành apontou, usando a=aatualizações nada. (As consultas na pergunta original realmente não atualizar qualquer coisa qualquer, mas uma atualização parece ser o que o OP pretendido.)
Roger Dueck
41

Existe uma extensão específica do MySQL para SQL que pode ser o que você deseja - REPLACE INTO

No entanto, ele não funciona da mesma forma que 'ON DUPLICATE UPDATE'

  1. Exclui a linha antiga que entra em conflito com a nova linha e insere a nova linha. Contanto que você não tenha uma chave primária na tabela, tudo bem, mas se você tiver, então se alguma outra tabela fizer referência a essa chave primária

  2. Você não pode fazer referência aos valores nas linhas antigas, portanto não pode fazer o equivalente a

    INSERT INTO mytable (id, a, b, c) values ( 1, 2, 3, 4) 
    ON DUPLICATE KEY UPDATE
    id=1, a=2, b=3, c=c + 1;

Gostaria de usar a solução alternativa para obter o ID!

Isso deve funcionar - last_insert_id () deve ter o valor correto, desde que sua chave primária seja incrementada automaticamente.

No entanto, como eu disse, se você realmente usar essa chave primária em outras tabelas, REPLACE INTOprovavelmente não será aceitável, pois exclui a linha antiga que se chocou por meio da chave exclusiva.

Alguém sugeriu antes que você possa reduzir a digitação fazendo:

INSERT INTO `tableName` (`a`,`b`,`c`) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE `a`=VALUES(`a`), `b`=VALUES(`b`), `c`=VALUES(`c`);
Danack
fonte
Então, não posso ficar com a chave primária antes de uma replace intoconsulta?
Roy
Não - essa linha é excluída e uma nova linha criada, que terá uma nova chave primária.
Danack 17/01
isso não atrasaria o processo em grandes conjuntos de dados e como importar csv's com 100K ou mais linhas?
Muhammad Omer Aslam
24

Não há outra maneira, eu tenho que especificar tudo duas vezes. Primeiro para a inserção, segundo no caso de atualização.

Roy
fonte
9
Isso está incorreto e não deve ser a resposta aceita. A resposta do @Danack é a resposta correta.
Wayne Workman
2
Você deve reler a pergunta @WayneWorkman, esta é a resposta correta.
217 Roy Roy
24

Aqui está uma solução para o seu problema:

Eu tentei resolver um problema como o seu e quero sugerir que teste de um aspecto simples.

Siga estas etapas: Aprenda com uma solução simples.

Etapa 1: Crie um esquema de tabela usando esta Consulta SQL:

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) NOT NULL,
  `password` varchar(32) NOT NULL,
  `status` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `no_duplicate` (`username`,`password`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;

Uma tabela <code> user </code> com dados

Etapa 2: Crie um índice de duas colunas para evitar dados duplicados usando a seguinte Consulta SQL:

ALTER TABLE `user` ADD INDEX no_duplicate (`username`, `password`);

ou Crie um índice de duas colunas da GUI da seguinte maneira: Criar índice a partir da GUI Selecione colunas para criar índice Índices da tabela <code> user </code>

Etapa 3: atualize, se existir, insira, se não estiver usando as seguintes consultas:

INSERT INTO `user`(`username`, `password`) VALUES ('ersks','Nepal') ON DUPLICATE KEY UPDATE `username`='master',`password`='Nepal';

INSERT INTO `user`(`username`, `password`) VALUES ('master','Nepal') ON DUPLICATE KEY UPDATE `username`='ersks',`password`='Nepal';

Tabela <code> user </code> após executar a consulta acima


fonte
1
Obrigado por este passo a passo - me fez entender, muito apreciado, obrigado!
Michael Nilsson
Para sua informação, armazenar senhas em texto sem formatação é uma péssima idéia, pois está tentando evitar senhas duplicadas no nível do banco de dados.
precisa saber é o seguinte
10

Caso você seja capaz de utilizar uma linguagem de script para preparar suas consultas SQL, você pode reutilizar pares campo = valor usando em SETvez de (a,b,c) VALUES(a,b,c).

Um exemplo com PHP:

$pairs = "a=$a,b=$b,c=$c";
$query = "INSERT INTO $table SET $pairs ON DUPLICATE KEY UPDATE $pairs";

Tabela de exemplo:

CREATE TABLE IF NOT EXISTS `tester` (
  `a` int(11) NOT NULL,
  `b` varchar(50) NOT NULL,
  `c` text NOT NULL,
  UNIQUE KEY `a` (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Chris
fonte
4
Você deve estar escapando de seus valores ou executando uma instrução preparada com seus valores.
Chinoto Vokro
1
@ChinotoVokro - eu concordo. Isso está apenas apresentando um "como você poderia" e nem está usando nenhuma função de consulta no exemplo.
Chris
1
Essa é uma boa alternativa para especificar uma matriz de chaves e uma matriz de valores. Obrigado.
Mark Carpenter Jr
5

Você pode considerar o uso de REPLACE INTOsintaxe, mas, ao ser avisado, ao duplicar a chave PRIMARY / UNIQUE, DELETE a linha e INSIRA uma nova.

Você não precisará especificar novamente todos os campos. No entanto, você deve considerar a possível redução de desempenho (depende do design da sua tabela).

Ressalvas:

  • Se você tiver a chave primária AUTO_INCREMENT, ela receberá uma nova
  • Os índices provavelmente precisarão ser atualizados
Matej
fonte
há uma violação de restrição se o índice também for uma chave estrangeira para outra tabela, é acionado pela exclusão de parte.
user3290180
4

Eu sei que é tarde, mas espero que alguém seja ajudado com esta resposta

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

Você pode ler o tutorial abaixo aqui:

https://mariadb.com/kb/en/library/insert-on-duplicate-key-update/

http://www.mysqltutorial.org/mysql-insert-or-update-on-duplicate-key-update/

Galang Piliang
fonte
Boa resposta. Mas o uso de VALUES () para se referir às novas linhas está obsoleto no MySQL 8.0.20 . A nova abordagem, detalhada no link, é usar um alias para a linha. Neste exemplo, o alias é new:INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new ON DUPLICATE KEY UPDATE c = new.a+new.b;
user697473
@ user697473 Estou recebendo o erro: "Você tem um erro na sintaxe SQL; verifique o manual que corresponde à sua versão do servidor MariaDB para obter a sintaxe correta perto de 'AS new ON DUPLICATE KEY UPDATE a = new.a'" , e minha consulta estava funcionando (exceto quando chaves duplicadas ocorreram, obviamente) antes de implementar esta cláusula. Alguma ideia?
aaron
@ Aaron - desculpe, sem grandes idéias. O MySQL 8.0.20 foi lançado no final de abril; talvez o MariaDB ainda não o tenha alcançado. Isso pode explicar por que está causando um erro.
user697473 18/06
@ user697473 Sim, na verdade, eu apenas tentei o método a = VALUES (a) na resposta original e funcionou, de qualquer forma, obrigado.
aaron
-7

você pode usar insert ignore nesse caso, ele ignorará se receber registros duplicados INSERT IGNORE ...; - sem CHAVE DUPLICADA

pitu
fonte
Eu queria inseri-lo quando não estiver presente, ou atualizá-lo. Não ignore isso.
214 Roy Roy
por que você deseja atualizá-lo se você estiver atualizando com o mesmo valor anterior /, se o seu novo valor, em seguida, o seu ok com ele, se não inserir ignorar o trabalho para ele
pitu
2
Provavelmente porque os valores podem mudar, e ele deseja criar um registro, se não existir, mas atualizar um registro existente, se existir (com alterações) sem a sobrecarga de uma viagem de ida e volta de volta ao aplicativo e depois ao banco de dados novamente.
Mpsyd 17/08/2015