web-dev-qa-db-fra.com

Dans MySQL, puis-je copier une ligne à insérer dans la même table?

insert into table select * from table where primarykey=1

Je veux juste copier une ligne à insérer dans la même table (c'est-à-dire, je veux dupliquer une ligne existante dans la table) mais je veux le faire sans avoir à lister toutes les colonnes après le "select", car cette table a trop de colonnes.

Mais quand je fais cela, j'obtiens l'erreur:

Duplicate entry 'xxx' pour la clé 1

Je peux gérer cela en créant une autre table avec les mêmes colonnes qu'un conteneur temporaire pour l'enregistrement que je veux copier:

create table oldtable_temp like oldtable;
insert into oldtable_temp select * from oldtable where key=1;
update oldtable_tem set key=2;
insert into oldtable select * from oldtable where key=2;

Existe-t-il un moyen plus simple de résoudre ce problème?

145
lina

J'ai utilisé la technique de Leonard Challis avec quelques modifications:

CREATE TEMPORARY TABLE tmptable_1 SELECT * FROM table WHERE primarykey = 1;
UPDATE tmptable_1 SET primarykey = NULL;
INSERT INTO table SELECT * FROM tmptable_1;
DROP TEMPORARY TABLE IF EXISTS tmptable_1;

En tant que table temporaire, il ne devrait jamais y avoir plus d'un enregistrement, vous n'avez donc pas à vous soucier de la clé primaire. Définir la valeur sur null permet à MySQL de choisir la valeur elle-même. Il n'y a donc aucun risque de duplication.

Si vous voulez être sur-sûr que vous n'ayez qu'une seule ligne à insérer, vous pouvez ajouter LIMIT 1 à la fin de la ligne INSERT INTO.

Notez que j'ai également ajouté la valeur de la clé primaire (1 dans ce cas) à mon nom de table temporaire.

175
Grim...

Mise à jour 07/07/2014 - La réponse basée sur ma réponse, par Grim ... , est une meilleure solution car elle s'améliore ma solution ci-dessous, donc je vous suggère de l'utiliser.

Vous pouvez le faire sans lister toutes les colonnes avec la syntaxe suivante:

CREATE TEMPORARY TABLE tmptable SELECT * FROM table WHERE primarykey = 1;
UPDATE tmptable SET primarykey = 2 WHERE primarykey = 1;
INSERT INTO table SELECT * FROM tmptable WHERE primarykey = 2;

Vous pouvez décider de changer la clé primaire d'une autre manière.

57
LeonardChallis

Je suppose que vous voulez que le nouvel enregistrement ait un nouveau primarykey? Si primarykey est AUTO_INCREMENT alors faites ceci:

INSERT INTO table (col1, col2, col3, ...)
SELECT col1, col2, col3, ... FROM table
  WHERE primarykey = 1

... où col1, col2, col3, ... représente toutes les colonnes de la table à l'exception deprimarykey.

Si ce n'est pas une colonne AUTO_INCREMENT et que vous voulez pouvoir choisir la nouvelle valeur pour primarykey, elle est similaire:

INSERT INTO table (primarykey, col2, col3, ...)
SELECT 567, col2, col3, ... FROM table
  WHERE primarykey = 1

... où 567 est la nouvelle valeur de primarykey.

37
Jordan Running

Vous l'aviez presque avec la première requête, vous avez juste besoin de spécifier les colonnes, de cette façon vous pouvez exclure votre clé primaire dans l'insertion, ce qui activera l'incrémentation que vous avez probablement sur la table pour créer automatiquement une nouvelle clé primaire pour le entrée.

Par exemple, changez ceci:

insert into table select * from table where primarykey=1

Pour ça:

INSERT INTO table (col1, col2, col3) 
SELECT col1, col2, col3 
FROM table 
WHERE primarykey = 1

N'incluez tout simplement pas la colonne primarykey dans la liste des colonnes pour les portions INSERT ou SELECT de la requête.

13
Braeden Black

Vous pouvez également essayer de vider la table, trouver la commande insert et la modifier:

mysqldump -umyuser -p mydatabase --skip-extended-insert mytable > outfile.sql

Le --skip-extended-insert vous donne une commande d'insertion par ligne. Vous pouvez ensuite trouver la ligne dans votre éditeur de texte favori, extraire la commande et modifier la clé primaire en "défaut".

8

Cette procédure suppose que:

  • vous n'avez pas _duplicate_temp_table
  • votre clé primaire est int
  • vous avez accès pour créer une table

Bien sûr, cela n’est pas parfait, mais dans certains cas (probablement la plupart du temps), cela fonctionnera.

DELIMITER $$
CREATE PROCEDURE DUPLICATE_ROW(copytable VARCHAR(255), primarykey VARCHAR(255), copyid INT, out newid INT)
BEGIN
        DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @error=1;
        SET @temptable = '_duplicate_temp_table';
        SET @sql_text = CONCAT('CREATE TABLE ', @temptable, ' LIKE ', copytable);
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @sql_text = CONCAT('INSERT INTO ', @temptable, ' SELECT * FROM ', copytable, ' where ', primarykey,'=', copyid);
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @sql_text = CONCAT('SELECT max(', primarykey, ')+1 FROM ', copytable, ' INTO @newid');
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @sql_text = CONCAT('UPDATE ', @temptable, ' SET ', primarykey, '=@newid');
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @sql_text = CONCAT('INSERT INTO ', copytable, ' SELECT * FROM ', @temptable, '');
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @sql_text = CONCAT('DROP TABLE ', @temptable);
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SELECT @newid INTO newid;
END $$
DELIMITER ;

CALL DUPLICATE_ROW('table', 'primarykey', 1, @duplicate_id);
SELECT @duplicate_id;
6
Piotr Kaczmarek

Ceci peut être réalisé avec un peu de créativité:

SET @sql = CONCAT('INSERT INTO <table> SELECT null, 
    ', (SELECT GROUP_CONCAT(COLUMN_NAME) 
    FROM information_schema.columns 
    WHERE table_schema = '<database>' 
    AND table_name = '<table>' 
    AND column_name NOT IN ('id')), ' 
from <table> WHERE id = <id>');  

PREPARE stmt1 FROM @sql;
EXECUTE stmt1;

Ainsi, la nouvelle ligne obtiendra un identifiant incrémenté automatiquement à la place de l'identifiant de la ligne sélectionnée.

4
curmil

Si le champ clé primaire de votre table est un champ à incrémentation automatique, vous pouvez utiliser une requête avec colonnes. Par exemple, votre table nommée test_tbl a 3 champs comme id, name, age. id est un champ de clé primaire et une incrémentation automatique. Vous pouvez donc utiliser la requête suivante pour dupliquer la ligne:

INSERT INTO `test_tbl` (`name`,`age`) SELECT `name`,`age` FROM `test_tbl`;

Cette requête entraîne la duplication de chaque ligne.


Si le champ clé primaire de votre table n'est pas un champ à incrémentation automatique, vous pouvez utiliser la méthode suivante:

INSERT INTO `test_tbl` (`id`,`name`,`age`)
  SELECT 20,`name`,`age` FROM `test_tbl` WHERE id = 19;

Le résultat de cette requête est une ligne en double de id=19 inséré sous la forme id=20.

4
user2595171

cloner une ligne avec des champs de mise à jour et une valeur d'incrémentation automatique

CREATE TEMPORARY TABLE `temp` SELECT * FROM `testing` WHERE id = 14;

UPDATE `temp` SET id = (SELECT id FROM testing ORDER by id DESC LIMIT 1
 )+1, user_id = 252 ,policy_no = "mysdddd12" where id = 14;

INSERT INTO `testing` SELECT * FROM `temp`;

DROP TEMPORARY TABLE IF EXISTS `temp`;
3

Certains des éléments suivants ont été glanés sur ce site. Voici ce que j'ai fait pour dupliquer un enregistrement dans une table avec un nombre quelconque de champs:

Cela suppose également que vous avez un champ d’intelligence artificielle au début de la table.

function duplicateRow( $id = 1 ){
dbLink();//my db connection
$qColumnNames = mysql_query("SHOW COLUMNS FROM table") or die("mysql error");
$numColumns = mysql_num_rows($qColumnNames);

for ($x = 0;$x < $numColumns;$x++){
$colname[] = mysql_fetch_row($qColumnNames);
}

$sql = "SELECT * FROM table WHERE tableId = '$id'";
$row = mysql_fetch_row(mysql_query($sql));
$sql = "INSERT INTO table SET ";
for($i=1;$i<count($colname)-4;$i++){//i set to 1 to preclude the id field
//we set count($colname)-4 to avoid the last 4 fields (good for our implementation)
$sql .= "`".$colname[$i][0]."`  =  '".$row[$i]. "', ";
}
$sql .= " CreateTime = NOW()";// we need the new record to have a new timestamp
mysql_query($sql);
$sql = "SELECT MAX(tableId) FROM table";
$res = mysql_query($sql);
$row = mysql_fetch_row($res);
return $row[0];//gives the new ID from auto incrementing
}
3
richardwhitney

Je voudrais utiliser ci-dessous,

insert into ORDER_ITEM select * from ORDER_ITEM where ITEM_NUMBER =123;
2
Ishan Liyanage

Je suis peut-être en retard, mais j'ai une solution similaire qui a fonctionné pour moi.

 INSERT INTO `orders` SELECT MAX(`order_id`)+1,`container_id`, `order_date`, `receive_date`, `timestamp` FROM `orders` WHERE `order_id` = 1

De cette façon, je n'ai pas besoin de créer de table temporaire, etc. Comme la ligne est copiée dans la même table, la fonction Max(PK)+1 peut être utilisée facilement.

Je suis venu chercher la solution de cette question (j'avais oublié la syntaxe) et j'ai fini par faire ma propre requête. C'est marrant comme ça marche parfois.

Cordialement

2
echo_salik

Si la clé primaire est à incrémentation automatique, spécifiez simplement chaque champ à l'exception de la clé primaire.

INSERT INTO table(field1,field2,field3) SELECT (field1,field2,field3) FROM table WHERE primarykey=1

2
Fedy Venom

J'ai mis à jour la solution de @ LeonardChallis car elle ne fonctionnait pas comme aucun autre. J'ai supprimé les clauses WHERE et SET primaryKey = 0 de la table temporaire afin que MySQL s'incrémente lui-même la primaryKey.

CREATE TEMPORARY TABLE tmptable SELECT * FROM myTable;
UPDATE tmptable SET primaryKey = 0;
INSERT INTO myTable SELECT * FROM tmptable;

Ceci est bien sûr à dupliquer tous les lignes de la table.

2
D4V1D

Cette solution a montré ci-dessus fonctionne également parfaitement pour les lignes sélectionnées. Par exemple, je crée des lignes de démonstration pour mon projet Nice2work, et cela fonctionne parfaitement.

CREATE TEMPORARY TABLE tmptable SELECT * FROM myTable WHERE id=500;
UPDATE tmptable SET id = 0;
UPDATE some fields I need to change
INSERT INTO myTable SELECT * FROM tmptable;
DROP TABLE tmptable;

//  You can use this same also directly into your code like (PHP Style)
$sql = "CREATE TEMPORARY TABLE tmptable SELECT * FROM myTable WHERE id=500;
UPDATE tmptable SET id = 0;
UPDATE some fields I need to change
INSERT INTO myTable SELECT * FROM tmptable;DROP TABLE tmptable;";
1
Ronald Stevens

J'ai utilisé la technique de Grim avec un peu de changement: Si quelqu'un cherche cette requête parce qu'il ne peut pas faire une requête simple à cause d'un problème de clé primaire:

INSERT INTO table SELECT * FROM table WHERE primakey=1;

Avec mon installation MySql 5.6.26, la clé n’est pas nullable et produit une erreur:

#1048 - Column 'primakey' cannot be null 

Ainsi, après la création de la table temporaire, je modifie la clé primaire en nullable.

CREATE TEMPORARY TABLE tmptable_1 SELECT * FROM table WHERE primarykey = 1;
ALTER TABLE tmptable_1 MODIFY primarykey int(12) null;
UPDATE tmptable_1 SET primarykey = NULL;
INSERT INTO table SELECT * FROM tmptable_1;
DROP TEMPORARY TABLE IF EXISTS tmptable_1;
1
Luca Camillo

J'ai utilisé dans ma base de données Koha pour insérer des éléments en double avec le préfixe 'C' dans la colonne code à barres:

INSERT INTO items (`biblionumber`, `biblioitemnumber`, `barcode`, `dateaccessioned` ) SELECT `biblionumber`, `biblioitemnumber`,  CONCAT('C',`barcode`), `dateaccessioned` FROM `items` WHERE barcode='14832';
1
Rajiv Mahmud

Je devais juste faire ceci et ceci était ma solution manuelle:

  1. Dans phpmyadmin, cochez la ligne que vous souhaitez copier
  2. Au bas de la liste, sous Opérations des résultats de la requête, cliquez sur 'Exporter'
  3. Sur la page suivante, cochez la case 'Enregistrer en tant que fichier' puis cliquez 'Aller'
  4. Ouvrez le fichier exporté avec un éditeur de texte, recherchez la valeur du champ principal et remplacez-le par quelque chose d'unique.
  5. De retour dans phpmyadmin cliquez sur l'onglet 'Importer', localisez le fichier à importer .sql sous parcourir, cliquez 'OK ' et la ligne en double doit être insérée.

Si vous ne savez pas ce qu'est le champ PRIMARY, regardez votre page phpmyadmin, cliquez sur l'onglet 'Structure' et au bas de la page, sous 'Indexes', il vous indiquera quel 'Champ' a un 'Keyname' valeur ' PRIMAIRE '.

C'est un peu long, mais si vous ne voulez pas vous occuper du balisage et que vous devez simplement dupliquer une seule ligne, c'est parti.

1
user27068

Pour une solution très simple, vous pouvez utiliser PHPMyAdmin pour exporter la ligne sous forme de fichier CSV, puis importer simplement le fichier CSV modifié. Modification de la colonne ID/primarykey pour n’afficher aucune valeur avant de l’importer.

SELECT * FROM table where primarykey=1

Puis au bas de la page:

enter image description here

Où est dit "Exporter" simplement exporter, puis éditez le fichier csv pour supprimer la valeur de la clé primaire, donc vide, puis importez-le dans la base de données, une nouvelle clé primaire sera attribuée à l'importation.

0
Grant

Voici une réponse que j'ai trouvée en ligne sur ce site Décrit comment faire ce qui précède 1 Vous pouvez trouver la réponse au bas de la page. En gros, vous copiez la ligne à copier dans une table temporaire conservée en mémoire. Vous changez ensuite le numéro de clé primaire en utilisant update. Vous le réinsérez ensuite dans la table cible. Vous laissez ensuite tomber la table.

Ceci est le code pour cela:

CREATE TEMPORARY TABLE rescueteam ENGINE = SELECTION DE MEMOIRE * FROMfitnessreport4 WHERE rID = 1; Ligne # 1 affectée. UPDATE rescueteam SET rID = Null WHERE rID = 1; rangée # 1 affectée.INSERT INTO fitnessreport4 SELECT * FROM rescueteam; rangée 1 affectée. DROP TABLE rescueteam # MySQL a renvoyé un jeu de résultats vide (c'est-à-dire zéro
Lignes).

J'ai créé la table temporaire rescueteam. J'ai copié la ligne de ma table originale fitnessreport4. J'ai ensuite défini la clé primaire de la ligne de la table temporaire sur null afin de pouvoir la recopier dans la table d'origine sans obtenir une erreur de clé dupliquée. J'ai essayé ce code hier soir et cela a fonctionné.

0
user2363682

Désolé pour la nécropole, mais c’est ce que j’ai trouvé avec Google. Etant donné que j’ai trouvé cela utile mais problématique, je voulais apporter une modification importante à toute personne qui déterre cela.

Tout d'abord, j'utilise SQL Server, pas MySQL, mais je pense que cela devrait fonctionner de la même manière. J'ai utilisé la solution de Leonard Challis car elle était simple et répondait au besoin. Cependant, si vous prenez simplement le PK et l'incrémentez de 1, que se passera-t-il si vous avez ajouté d'autres enregistrements depuis que la ligne en question a été ajoutée . J'ai décidé qu'il était préférable de laisser le système gérer l'auto-incrémentation de la PK, j'ai donc procédé comme suit:

SELECT * INTO #tmpTable FROM Table WHERE primarykey = 1
--Optionally you can modify one or more fields here like this: 
--UPDATE #tmpTable SET somefield = newData
ALTER TABLE #tmpTable DROP COLUMN TicketUpdateID
INSERT INTO Tickets SELECT * FROM #tmpTable
DROP TABLE #tmpTable

Je pense que cela fonctionnerait de la même manière dans MySQL, mais je ne peux pas le tester, désolé.

0
maxx233

Créer une table

    CREATE TABLE `sample_table` (
       `sample_id` INT(10) unsigned NOT NULL AUTO_INCREMENT,
       `sample_name` VARCHAR(255) NOT NULL,
       `sample_col_1` TINYINT(1) NOT NULL,
       `sample_col_2` TINYINT(2) NOT NULL,

      PRIMARY KEY (`sample_id`),
      UNIQUE KEY `sample_id` (`sample_id`)

    ) ENGINE='InnoDB' DEFAULT CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';

Insérer une ligne

INSERT INTO `sample_table`
   VALUES(NULL, 'sample name', 1, 2);

Cloner la ligne insérée ci-dessus

INSERT INTO `sample_table`
   SELECT 
    NULL AS `sample_id`, -- new AUTO_INCREMENT PRIMARY KEY from MySQL
    'new dummy entry' AS `sample_name`,  -- new UNIQUE KEY from you
    `sample_col_1`, -- col from old row
    `sample_col_2` -- col from old row
   FROM `sample_table`
   WHERE `sample_id` = 1;

Tester

SELECT * FROM `sample_table`;
0
Abdullah Aydın

Je sais que c'est une vieille question, mais voici une autre solution:

Cela duplique une ligne de la table principale, en supposant que la clé primaire s'incrémente automatiquement, et crée des copies des données des tables liées avec le nouvel identifiant de la table principale.

Autres options pour obtenir les noms de colonne:
- AFFICHER LES COLONNES DE tablename; (Nom de la colonne: champ)
- DESCRIBE tablename (Nom de la colonne: Champ)
- SELECT nom_colonne FROM information_schema.columns WHERE nom_table = 'nomtable' (nom de colonne: nom_colonne)

//First, copy main_table row
$ColumnHdr='';
$Query="SHOW COLUMNS FROM `main_table`;";
$Result=Wrappedmysql_query($Query,$link,__FILE__,__LINE__);
while($Row=mysql_fetch_array($Result))
{
    if($Row['Field']=='MainTableID')     //skip main table id in column list
        continue;
    $ColumnHdr.=",`" . $Row['Field'] . "`";
}
$Query="INSERT INTO `main_table` (" . substr($ColumnHdr,1) . ")
        (SELECT " . substr($ColumnHdr,1) . " FROM `main_table`
            WHERE `MainTableID`=" . $OldMainTableID . ");";
$Result=Wrappedmysql_query($Query,$link,__FILE__,__LINE__);
$NewMainTableID=mysql_insert_id($link);

//Change the name (assumes a 30 char field)
$Query="UPDATE `main_table` SET `Title`=CONCAT(SUBSTRING(`Title`,1,25),' Copy') WHERE `MainTableID`=" . $NewMainTableID . ";";
$Result=Wrappedmysql_query($Query,$link,__FILE__,__LINE__);

//now copy in the linked tables
$TableArr=array("main_table_link1","main_table_link2","main_table_link3");
foreach($TableArr as $TableArrK=>$TableArrV)
{
    $ColumnHdr='';
    $Query="SHOW COLUMNS FROM `" . $TableArrV . "`;";
    $Result=Wrappedmysql_query($Query,$link,__FILE__,__LINE__);
    while($Row=mysql_fetch_array($Result))
    {
        if($Row['Field']=='MainTableID')     //skip main table id in column list, re-added in query
            continue;
        if($Row['Field']=='dbID')    //skip auto-increment,primary key in linked table
            continue;
        $ColumnHdr.=",`" . $Row['Field'] . "`";
    }

    $Query="INSERT INTO `" . $TableArrV . "` (`MainTableID`," . substr($ColumnHdr,1) . ")
            (SELECT " . $NewMainTableID . "," . substr($ColumnHdr,1) . " FROM `" . $TableArrV . "`
             WHERE `MainTableID`=" . $OldMainTableID . ");";
    $Result=Wrappedmysql_query($Query,$link,__FILE__,__LINE__);
}
0
Paul

max233 était certainement sur la bonne voie, du moins pour le cas de l'auto-incrémentation. Cependant, ne faites pas ALTER TABLE. Définissez simplement le champ d’incrémentation automatique dans la table temporaire sur NULL. Cela présentera une erreur, mais l'INSERT suivant de tous les champs de la table temporaire se produira et le champ automatique NULL obtiendra une valeur unique.

0
user3067083