Lors de la mise à jour de la clé en double identique à l'insertion

158

J'ai cherché mais je n'ai pas trouvé si c'était possible.

J'ai cette requête MySQL:

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

L'identifiant de champ a un "index unique", il ne peut donc pas y en avoir deux. Maintenant, si le même identifiant est déjà présent dans la base de données, j'aimerais le mettre à jour. Mais dois-je vraiment spécifier à nouveau tous ces champs, comme:

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)

J'ai tout spécifié déjà dans l'insert ...

Une note supplémentaire, j'aimerais utiliser le travail autour pour obtenir l'ID!

id=LAST_INSERT_ID(id)

J'espère que quelqu'un pourra me dire quelle est la manière la plus efficace.

Roy
la source
9
AFAIK, la méthode de reformulation de chaque colonne, a=VALUES(a), b=VALUES(b), ...est la voie à suivre. C'est comme ça que je fais pour toutes mes INSERT ON DUPLICATE KEY UPDATEdéclarations.
Valdogg21
4
Juste pour clarifier, tout ce qui est indiqué ici est correct tant que vous comprenez que la question à laquelle il est répondu est: devez-vous reformuler chaque colonne que vous VOULEZ METTRE À JOUR? Oui, si vous souhaitez insérer ou mettre à jour 8 colonnes dans une table à 25 colonnes, vous devez indiquer les 8 colonnes deux fois - une fois dans la partie insertion et une fois dans la partie mise à jour. (Vous pouvez ignorer la clé primaire dans la partie mise à jour, mais il est plus simple de préformater une chaîne "a = 1, b = 2, c = 3" et de l'incorporer deux fois.) Cependant, vous NE devez PAS reformuler les 17 colonnes restantes tu ne veux pas changer. Si cela devient une MISE À JOUR, ils conserveront leurs valeurs existantes.
Timberline
3
J'ai ajouté ce commentaire parce que les questions et réponses m'ont laissé incertain sur les colonnes non mentionnées avec ce type d'INSERT, que j'ai ensuite testé après avoir appris exactement quoi tester. INSERT ON DUPLICATE KEY UPDATE laisse les colonnes non mentionnées inchangées sur UPDATE mais leur donne des valeurs par défaut sur INSERT. Avec REPLACE INTO, les colonnes non mentionnées obtiennent toujours des valeurs par défaut, jamais des valeurs existantes.
Timberline
1
Vérifiez stackoverflow.com/questions/2472229/… , je pense qu'il y a ce que vous cherchez
Chococroc

Réponses:

82

L' UPDATEinstruction est donnée afin que les champs plus anciens puissent être mis à jour avec une nouvelle valeur. Si vos anciennes valeurs sont les mêmes que vos nouvelles, pourquoi auriez-vous besoin de les mettre à jour dans tous les cas?

Pour par exemple. si vos colonnes aà gsont déjà définies comme 2à 8; il ne serait pas nécessaire de le remettre à jour.

Alternativement, vous pouvez utiliser:

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;

Pour obtenir le idde LAST_INSERT_ID; vous devez spécifier l'application backend que vous utilisez pour la même chose.

Pour LuaSQL, a conn:getlastautoid()récupère la valeur.

hjpotter92
la source
6
C'est juste pour l'exemple. Dans une requête réelle, il y a des différences. Ma question est simple: ai-je vraiment besoin de spécifier tous les champs (et les valeurs dans mon premier exemple) s'ils sont les mêmes que dans l'insert? Je veux juste insérer tout ou s'il y a une correspondance de valeur unique: mettre à jour tout.
Roy
1
"Une note supplémentaire, j'aimerais aussi utiliser le travail pour obtenir l'ID!"
Agamemnus
1
@Tresdin, pour autant que je sache, ce n'est que du sucre syntaxique. Personnellement, je n'ai jamais vu personne utiliser les valeurs a = VALUES (a), b = VALUES (b), etc. Peut-être pouvez-vous attribuer plusieurs valeurs à la colonne? Vous ne savez pas pourquoi vous ne concaténeriez pas simplement les données au préalable.
DuckPuncher
54
Si la table a tbldéjà la ligne (1,10), alors: INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=VALUES(a)définira a = 2 . Alors que INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=amettra un = 10
Bùi Việt Thành
2
Pourquoi tous les votes positifs? Cela ne fonctionne pas. Comme @ Bùi Việt Thành l'a souligné, n'utiliser a=aaucune mise à jour. (Les requêtes de la question d'origine ne mettent pas à jour non plus, mais une mise à jour semble être ce que le PO voulait.)
Roger Dueck
41

Il existe une extension spécifique à MySQL pour SQL qui peut être ce que vous voulez - REPLACE INTO

Cependant, cela ne fonctionne pas tout à fait de la même manière que 'ON DUPLICATE UPDATE'

  1. Il supprime l'ancienne ligne qui entre en conflit avec la nouvelle ligne, puis insère la nouvelle ligne. Tant que vous n'avez pas de clé primaire sur la table, ce serait bien, mais si vous le faites, alors si une autre table fait référence à cette clé primaire

  2. Vous ne pouvez pas référencer les valeurs des anciennes lignes, vous ne pouvez donc pas faire l'équivalent de

    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;

Je voudrais utiliser le travail pour obtenir l'ID!

Cela devrait fonctionner - last_insert_id () doit avoir la valeur correcte tant que votre clé primaire s'incrémente automatiquement.

Cependant, comme je l'ai dit, si vous utilisez réellement cette clé primaire dans d'autres tables, REPLACE INTOcela ne vous sera probablement pas acceptable, car cela supprime l'ancienne ligne qui s'est heurtée via la clé unique.

Quelqu'un d'autre a suggéré avant de pouvoir réduire la saisie en faisant:

INSERT INTO `tableName` (`a`,`b`,`c`) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE `a`=VALUES(`a`), `b`=VALUES(`b`), `c`=VALUES(`c`);
Danack
la source
Donc, je ne peux pas m'en tenir à la clé primaire avant une replace intorequête?
Roy
Non - cette ligne est supprimée et une nouvelle ligne créée, qui aura une nouvelle clé primaire.
Danack
cela ne ralentirait-il pas le processus sur de grands ensembles de données et comme l'importation de csv avec 100K ou plus de lignes?
Muhammad Omer Aslam
24

Il n'y a pas d'autre moyen, je dois tout spécifier deux fois. Premièrement pour l'insert, deuxième dans le cas de la mise à jour.

Roy
la source
9
Ceci est incorrect et ne devrait pas être la réponse acceptée. La réponse de @Danack est la bonne réponse.
Wayne Workman
2
Vous devriez relire la question @WayneWorkman, c'est la bonne réponse.
Roy
24

Voici une solution à votre problème:

J'ai essayé de résoudre un problème comme le vôtre et je veux suggérer de tester d'un aspect simple.

Suivez ces étapes: Apprenez d'une solution simple.

Étape 1: Créez un schéma de table à l' aide de cette requête 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;

Une table <code> user </code> avec des données

Étape 2: Créez un index de deux colonnes pour éviter les données en double à l'aide de la requête SQL suivante:

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

ou, créez un index de deux colonnes à partir de l'interface graphique comme suit: Créer un index à partir de l'interface graphique Sélectionnez les colonnes pour créer un index Index de la table <code> user </code>

Étape 3: Mettre à jour s'il existe, insérer si vous n'utilisez pas les requêtes suivantes:

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';

Table <code> user </code> après avoir exécuté la requête ci-dessus


la source
1
Merci pour cette procédure pas à pas - m'a fait comprendre, très apprécié, merci!
Michael Nilsson
Pour info, stocker les mots de passe en texte brut est une idée terrible, tout comme essayer d'éviter les mots de passe en double au niveau de la base de données.
OrangeDog
10

Juste au cas où vous pourriez utiliser un langage de script pour préparer vos requêtes SQL, vous pouvez réutiliser des paires champ = valeur en utilisant SETau lieu de (a,b,c) VALUES(a,b,c).

Un exemple avec PHP:

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

Exemple de tableau:

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
la source
4
Vous devriez échapper vos valeurs ou exécuter une instruction préparée avec vos valeurs.
Chinoto Vokro
1
@ChinotoVokro - Je suis d'accord. Il s'agit simplement de présenter un "comment vous pourriez" et n'utilise même aucune fonction de requête dans l'exemple.
Chris
1
C'est une bonne alternative à la spécification d'un tableau de clés et d'un tableau de valeurs. Merci.
Mark Carpenter Jr
5

Vous voudrez peut-être envisager d'utiliser la REPLACE INTOsyntaxe, mais soyez averti, lors de la duplication de la clé PRIMAIRE / UNIQUE, elle SUPPRIME la ligne et EN INSERTE une nouvelle.

Vous n'aurez pas besoin de redéfinir tous les champs. Cependant, vous devez envisager la réduction des performances possible (dépend de la conception de votre table).

Mises en garde:

  • Si vous disposez d'une clé primaire AUTO_INCREMENT, une nouvelle clé lui sera attribuée
  • Les index devront probablement être mis à jour
Matej
la source
il y a une violation de contrainte si l'index est également une clé étrangère pour une autre table, elle est déclenchée par la suppression d'une partie.
user3290180
4

Je sais qu'il est tard, mais j'espère que quelqu'un sera aidé de cette réponse

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

Vous pouvez lire le tutoriel ci-dessous ici:

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
la source
Bonne réponse. Mais l'utilisation de VALUES () pour faire référence aux nouvelles lignes est obsolète à partir de MySQL 8.0.20 . La nouvelle approche, détaillée au niveau du lien, consiste à utiliser un alias pour la ligne. Dans cet exemple, l'alias est 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 J'obtiens l'erreur: "Vous avez une erreur dans votre syntaxe SQL; vérifiez le manuel qui correspond à votre version de serveur MariaDB pour la bonne syntaxe à utiliser près de 'AS new ON DUPLICATE KEY UPDATE a = new.a'" , et ma requête fonctionnait (sauf lorsque des clés en double se sont produites, évidemment) avant que j'implémente cette clause. Des idées?
aaron
@aaron - désolé, pas de bonnes idées. MySQL 8.0.20 vient de sortir fin avril; peut-être que MariaDB n'a pas encore rattrapé son retard. Cela pourrait expliquer pourquoi cela vous donne une erreur.
user697473
@ user697473 Ouais, je viens d'essayer la méthode a = VALUES (a) dans la réponse d'origine et cela a fonctionné, merci quand même.
aaron le
-7

vous pouvez utiliser insert ignore dans ce cas, il ignorera s'il obtient des enregistrements en double INSERT IGNORE ...; - sans clé ON DUPLICATE

pitu
la source
Je voulais l'insérer lorsqu'il n'était pas présent, sinon le mettre à jour. Ne l'ignorez pas.
Roy
pourquoi vous voulez le mettre à jour si vous mettez à jour avec la valeur précédente / même, si c'est une nouvelle valeur alors c'est ok avec elle, sinon insérer ignorer travailler pour elle
pitu
2
Très probablement parce que les valeurs peuvent changer et qu'il souhaite créer un enregistrement s'il n'en existe pas, mais mettre à jour un enregistrement existant si tel est le cas (avec des modifications) sans la surcharge d'un aller-retour vers l'application, puis vers la base de données encore.
mopsyd