2009-04-22 11 views
104

J'ai la requête suivante:MySQL ON DUPLICATE KEY - dernier identifiant d'insertion?

INSERT INTO table (a) VALUES (0) 
    ON DUPLICATE KEY UPDATE a=1 

Je veux que l'ID soit de l'insertion ou la mise à jour. Habituellement, j'exécute une deuxième requête afin d'obtenir ceci car je crois que insert_id() ne renvoie que l'identifiant 'inséré' et non l'identifiant mis à jour.

Existe-t-il un moyen de INSERT/UPDATE et récupérer l'ID de la ligne sans exécuter deux requêtes?

+3

Plutôt que de supposer, pourquoi ne pas le tester vous-même? Le SQL dans l'édition ci-dessus fonctionne, et grâce à mon test est plus rapide que l'échec d'une insertion, en utilisant INSERT IGNORE, ou en sélectionnant pour voir s'il y a un doublon en premier. –

+1

AVERTISSEMENT: La solution proposée fonctionne, mais la valeur auto_increment continue à augmenter, même s'il n'y a pas d'insertion. Si la clé dupliquée se produit souvent, vous pouvez exécuter 'alter table nom_table AUTO_INCREMENT = 0;' après la requête ci-dessus, afin d'éviter de grandes lacunes dans vos valeurs d'identifiant. –

Répondre

125

Vérifiez cette page sur: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
au bas de la page, ils expliquent comment vous pouvez faire LAST_INSERT_ID significative pour les mises à jour en passant une expression th à la fonction MySQL.

Dans l'exemple de la documentation MySQL:

Si une table contient une colonne AUTO_INCREMENT et INSERT ... UPDATE insère une ligne, la fonction LAST_INSERT_ID() renvoie la valeur AUTO_INCREMENT. Si l'instruction met à jour une ligne à la place, LAST_INSERT_ID() n'a pas de sens. Cependant, vous pouvez contourner ce problème en utilisant LAST_INSERT_ID (expr). Supposons que id est la colonne AUTO_INCREMENT. Pour LAST_INSERT_ID() significatif pour les mises à jour, les lignes d'insertion comme suit:

INSERT INTO table (a,b,c) VALUES (1,2,3) 
    ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), c=3; 
+2

D'une façon ou d'une autre, j'ai raté ça en regardant cette page. La partie de mise à jour apparaît ainsi: UPDATE id = LAST_INSERT_ID (id) Et cela fonctionne très bien. Merci! – thekevinscott

+1

Content de pouvoir aider! – fredrik

+6

On dit que la fonction php mysql_insert_id() renvoie la valeur correcte dans les deux cas: http://www.php.net/manual/fr/function.mysql-insert-id.php#59718. – jayarjo

2

Vous pouvez regarder REPLACE, qui est essentiellement une suppression/insertion si l'enregistrement existe. Mais cela modifierait le champ d'incrémentation automatique s'il est présent, ce qui pourrait rompre les relations avec d'autres données.

+1

Ah ouais - Je cherche quelque chose qui ne se débarrasse pas des ID précédents – thekevinscott

+0

Cela peut être dangereux aussi parce que cela peut aussi entraîner la suppression d'autres données connexes (par des contraintes). – Serge

0

Il convient de noter, et cela pourrait être évident (mais je vais le dire quand même pour plus de clarté ici), qui REPLACE séduiront la ligne correspondante existante avant d'insérer vos nouvelles données. ON DUPLICATE KEY UPDATE ne mettra à jour que les colonnes que vous spécifiez et préserve la ligne.

De l'manual:

REPLACE fonctionne exactement comme INSERT, sauf que si une ancienne ligne de la table a la même valeur que d'une nouvelle ligne pour une clé primaire ou un index unique, old La ligne est supprimée avant que la nouvelle ligne soit insérée.

25

Pour être exact, si cela est la requête originale:

INSERT INTO table (a) VALUES (0) 
ON DUPLICATE KEY UPDATE a=1 

et 'id' est la clé primaire auto-incrément que ce serait la solution de travail:

INSERT INTO table (a) VALUES (0) 
    ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), a=1 

Est-ce là: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html

Si une table contient un AUTO_INCREMENT colonne et INSERT ... UPDATE insère une ligne, la fonction LAST_INSERT_ID() renvoie la valeur AUTO_INCREMENT . Si l'instruction met à jour une ligne à la place, LAST_INSERT_ID() n'a pas de sens. Toutefois, vous pouvez contourner ce en utilisant LAST_INSERT_ID (expr). Supposons que id est la colonne AUTO_INCREMENT .

+3

Oui, voir la réponse acceptée pour la même chose que vous avez dit. Pas besoin de faire revivre les messages de 3 ans. Merci pour votre effort de toute façon. – fancyPants

+1

@tombom la seule raison pour laquelle j'ai posté cette réponse est parce que la réponse acceptée n'est pas correcte - cela ne fonctionnera pas s'il n'y a rien à mettre à jour. –

+0

+1 bien que je pense que la réponse acceptée mentionne que :) – fancyPants

0

Les solutions existantes fonctionnent si vous utilisez l'auto-incrémentation. J'ai une situation où l'utilisateur peut définir un préfixe et il devrait redémarrer la séquence à 3000. En raison de ce préfixe varié, je ne peux pas utiliser autoincrement, ce qui rend last_insert_id vide pour les insertions. Je l'ai résolu ce qui suit:

INSERT INTO seq_table (prefix, id) VALUES ('$user_prefix', LAST_INSERT_ID(3000)) ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id + 1); 
SELECT LAST_INSERT_ID(); 

Si le préfixe existe, il incrémente et remplir LAST_INSERT_ID. Si le préfixe n'existe pas, il insèrera le préfixe avec la valeur 3000 et remplira last_insert_id avec 3000.