88 votes

Duplication / copie d'enregistrements dans la même table MySQL

Je cherche depuis un certain temps déjà mais je ne trouve pas de solution facile à mon problème. Je voudrais dupliquer un enregistrement dans une table, mais bien sûr, la clé primaire unique doit être mise à jour.

J'ai cette question :

INSERT INTO invoices
    SELECT * FROM invoices AS iv WHERE iv.ID=XXXXX
    ON DUPLICATE KEY UPDATE ID = (SELECT MAX(ID)+1 FROM invoices)

le problème est que cela ne fait que changer le ID de la ligne au lieu de copier la ligne. Quelqu'un sait-il comment résoudre ce problème ?

//edit : J'aimerais faire cela sans taper tous les noms de champs parce que les noms de champs peuvent changer au fil du temps.

142voto

Alex Points 516

La façon dont je m'y prends habituellement est d'utiliser une table temporaire. Ce n'est probablement pas efficace d'un point de vue informatique mais cela semble fonctionner correctement ! Ici, je duplique l'enregistrement 99 dans son intégralité, créant ainsi l'enregistrement 100.

CREATE TEMPORARY TABLE tmp SELECT * FROM invoices WHERE id = 99;

UPDATE tmp SET id=100 WHERE id = 99;

INSERT INTO invoices SELECT * FROM tmp WHERE id = 100;

J'espère que cela fonctionne bien pour vous !

1 votes

J'aime ça, une étape au milieu pour garder un œil sur les choses.

4 votes

Si vous ne copiez qu'un seul enregistrement, vous pouvez laisser tomber le where dans la mise à jour et l'insertion. Il suffirait alors d'appuyer deux fois sur la touche haut dans la console mysql, ce qui ferait apparaître l'avant-dernière opération dans l'historique, de changer l'identifiant dans update qui est pratique à la fin, d'appuyer sur entrée, d'appuyer sur haut, d'appuyer à nouveau sur entrée sans rien changer, puis de répéter la procédure pour des copies multiples. Beaucoup plus rapide.

4 votes

Que faire dans le cas où l'identifiant 100 existe déjà ? Ensuite, si vous choisissez un nouvel identifiant pour votre nouvelle ligne, et que pendant l'opération quelqu'un d'autre insère une nouvelle ligne, votre identifiant et l'identifiant nouvellement inséré seront les mêmes. C'est le goulot d'étranglement de ce code.

87voto

Tim Ruehsen Points 391

La réponse d'Alex nécessite une certaine attention (par exemple, un verrouillage ou une transaction) dans les environnements multi-clients.

En supposant que le AUTO ID est le premier de la table (un cas habituel), nous pouvons utiliser les transactions implicites implicites.

    CREATE TEMPORARY TABLE tmp SELECT \* from invoices WHERE ...;
    ALTER TABLE tmp drop ID; # drop autoincrement field
    # UPDATE tmp SET ...; # just needed to change other unique keys
    INSERT INTO invoices SELECT 0,tmp.\* FROM tmp;
    DROP TABLE tmp;

Dans la documentation de MySQL :

Utiliser AUTO_INCREMENT : Vous pouvez également attribuer explicitement NULL ou 0 à la colonne pour générer des numéros de séquence.

0 votes

L'enregistrement a donc été introduit avec un ID de 0, alors que j'ai un champ AUTO_INCREMENT. Le remplacer par null a fonctionné, je vous suggère donc de modifier le code pour utiliser null au lieu de 0. J'utilise MySQL 5.5.35 (Ubuntu).

0 votes

@TylerCollier : NULL o 0 les deux sont acceptables. Pouvez-vous afficher des captures d'écran du problème en le reproduisant et le partager ici.

0 votes

J'ai eu mal au cerveau en essayant de comprendre la sous-requête de la ligne 4. Cette explication peut peut-être vous aider : La table de destination invoices contient un AUTO_INCREMENT ID champ. La table source tmp ne le fait pas. Afin d'obtenir la corrélation 1:1 nécessaire lors de l'insertion, vous spécifiez une 0 comme premier champ de la sélection, qui est le champ AUTO_INCREMENT de la table de destination. Le site tmp.* sélectionne tous les champs de la table source tmp . Parfait ! Vous avez maintenant votre corrélation 1:1. Superbe réponse @Tim.

13voto

Ingo Points 21438

Vous SAVEZ avec certitude que la touche de duplication va se déclencher, vous pouvez donc sélectionner le MAX(ID)+1 à l'avance :

INSERT INTO invoices SELECT MAX(ID)+1, ... other fields ... FROM invoices AS iv WHERE iv.ID=XXXXX

2 votes

Oui, mais je ne veux pas taper tous les autres champs (il y en a plus ou moins 20, et ils peuvent changer dans le temps). Je ne veux pas changer le code à chaque fois que la structure de l'onglet change.

0 votes

Vous devrez le faire. Tu ferais mieux de faire un script qui génère l'instruction SQL à partir d'une liste de champs. C'est trivial.

0 votes

La seule autre alternative est d'utiliser un trigger d'insertion (si mysql le supporte).

11voto

StormoPL Points 41

Votre approche est bonne mais le problème est que vous utilisez "*" au lieu d'énumérer les noms des champs. Si vous mettez tous les noms de colonnes excepté la clé primaire, votre script fonctionnera comme un charme sur un ou plusieurs enregistrements.

INSERT INTO invoices (iv.field_name, iv.field_name,iv.field_name
) SELECT iv.field_name, iv.field_name,iv.field_name FROM invoices AS iv     
WHERE iv.ID=XXXXX

0 votes

Cependant, cela signifie que vous devrez modifier le code dès qu'un champ est ajouté ou supprimé de la table, ce que je trouve personnellement un NON-RESPONSABLE MAJEUR. (Pas toujours, mais assez souvent.)

1voto

zzapper Points 610

J'en ai eu besoin aussi ; ma solution a été d'utiliser SQLYOG (version gratuite) pour exporter l'enregistrement souhaité en tant que SQL (crée une insertion).

J'ai ensuite édité manuellement ce document pour supprimer l'identifiant, qui doit être généré automatiquement, puis j'ai copié l'insertion dans SQLYog pour l'exécuter. Cela n'a pas posé de problème. Je suppose que beaucoup d'autres interfaces graphiques de MySQL peuvent le faire aussi.

Je dispose ainsi d'un enregistrement que je peux utiliser à des fins de test sur un système réel.

Je dispose maintenant de cet insert pour le réutiliser également, car la table est réécrite quotidiennement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X