Comment copier une ligne et l'insérer dans la même table avec un champ d'auto-incrémentation dans MySQL?

233

Dans MySQL, j'essaie de copier une ligne avec un auto column ID=1 - incrémentation et d' insérer les données dans la même table qu'une nouvelle ligne avec column ID=2.

Comment faire cela en une seule requête?

Navdroid
la source

Réponses:

351

Utilisation INSERT ... SELECT:

insert into your_table (c1, c2, ...)
select c1, c2, ...
from your_table
where id = 1

c1, c2, ...sont toutes les colonnes sauf id. Si vous souhaitez insérer explicitement avec un idde 2, incluez-le dans votre liste de colonnes INSERT et votre SELECT:

insert into your_table (id, c1, c2, ...)
select 2, c1, c2, ...
from your_table
where id = 1

Vous devrez idbien sûr vous occuper d'un éventuel doublon de 2 dans le second cas.

mu est trop court
la source
1
Vous pouvez alors obtenir par programmation les noms des colonnes en utilisant INFORMATION_SCHEMA ... Je me demande si vous pourriez le faire comme une sous-requête et des fonctions de chaîne? Hmmm ...
Yzmir Ramirez
1
@Yzmir: Vous finiriez par devoir utiliser du SQL dynamique et cela nécessiterait généralement la construction d'une procédure stockée. Cela semble plus problématique que cela ne vaut quand vous devriez quand même avoir une liste de noms de colonnes à portée de main.
mu est trop court le
6
D'accord ... d'après mon expérience, une fois que vous avez suivi la procédure stockée, vous ne revenez pas en arrière - vous êtes marié à cette base de données maintenant et ne fait qu'ajouter aux coûts chaque fois que vous voulez changer.
Yzmir Ramirez
11
@YzmirRamirez, vous donnez l'impression que le mariage est intrinsèquement une mauvaise chose. :)
Prof.Falken
1
Comment ajouter une valeur personnalisée dans la colonne avec tous les autres champs copiés?
Manish Kumar
49

OMI, le meilleur semble utiliser les instructions sql uniquement pour copier cette ligne, tout en référençant uniquement les colonnes que vous devez et souhaitez modifier.

CREATE TEMPORARY TABLE temp_table ENGINE=MEMORY

SELECT * FROM your_table WHERE id=1;
UPDATE temp_table SET id=NULL; /* Update other values at will. */

INSERT INTO your_table SELECT * FROM temp_table;
DROP TABLE temp_table;

Voir aussi av8n.com - Comment cloner un enregistrement SQL

Avantages:

  • Les instructions SQL 2 mentionnent uniquement les champs qui doivent être modifiés pendant le processus de clonage. Ils ne connaissent pas - ou ne se soucient pas - d'autres domaines. Les autres champs vont juste pour le trajet, inchangés. Cela rend les instructions SQL plus faciles à écrire, plus faciles à lire, plus faciles à maintenir et plus extensibles.
  • Seules les instructions MySQL ordinaires sont utilisées. Aucun autre outil ou langage de programmation n'est requis.
  • Un enregistrement entièrement correct est inséré your_tabledans une opération atomique.
parvus
la source
3
Il semble, que cette belle astuce (je l'ai aimé, mais cela n'a pas fonctionné pour moi) ne fonctionne pas sur les tables qui contiennent des colonnes TEXT / VARCHAR. Je l'ai essayé et j'ai obtenu: (1163): Le type de table utilisé ne prend pas en charge les colonnes BLOB / TEXT. Bien sûr, cela limite beaucoup l'utilisation sur MySQL! Il se peut qu'à l'avenir ces limites soient levées ou sur d'autres systèmes db cela fonctionne, mais pour l'instant c'est vraiment limité.
Juergen
J'aime vraiment l'utilisation intelligente de la table temporaire. Tellement utile pour les tableaux avec une tonne de colonnes!
Lasma
1
Ça a l'air bien, mais quand j'exécute la première requête dans MySQL, j'obtiens Error Code: 1113. A table must have at least 1 column.
physique du
3
Meilleure réponse pour beaucoup de colonnes. Mais SET id=NULLpeut provoquer une erreur Column 'id' cannot be null. Devrait être remplacé parUPDATE temp_table SET id = (SELECT MAX(id) + 1 as id FROM your_table);
Modder
1
@physicalattraction vous devez vous assurer que les deux premières lignes sont une seule instruction.
Samuurai
16

Disons que la table est user(id, user_name, user_email).

Vous pouvez utiliser cette requête:

INSERT INTO user (SELECT NULL,user_name, user_email FROM user WHERE id = 1)
Vijay
la source
29
Vous devez toujours spécifier les noms des colonnes lorsque vous utilisez INSERT, sinon vous obtiendrez des bogues étranges et intéressants lorsque votre schéma changera.
mu est trop court le
2
Étrange et intéressant, en effet. : D
sparkyShorts
Ne fonctionne pas avec sqlite. Result: near "SELECT": syntax error
kyb
10

Cela a aidé et prend en charge les colonnes BLOB / TEXT.

CREATE TEMPORARY TABLE temp_table
AS
SELECT * FROM source_table WHERE id=2;
UPDATE temp_table SET id=NULL WHERE id=2;
INSERT INTO source_table SELECT * FROM temp_table;
DROP TEMPORARY TABLE temp_table;
USE source_table;
Petr Hladík
la source
En quoi c'est mieux que d'autres réponses ici?
Smar
3
Je pense que c'est plutôt bien si vous avez une table avec 100 champs. Sauf qu'il pourrait y avoir une contrainte non nulle sur l'id qui le fait échouer
Loïc Faure-Lacroix
Code d'erreur: 1136. Le nombre de colonnes ne correspond pas au nombre de valeurs à la ligne 1
Oleksii Kyslytsyn
C'est bien mieux si vous avez une table avec des centaines de colonnes.
Tyler S. Loeper
N'est-ce pas ce que parvus a dit, des années plus tôt?
ToolmakerSteve
7

Pour une solution rapide et propre qui ne vous oblige pas à nommer des colonnes, vous pouvez utiliser une instruction préparée comme décrit ici: https://stackoverflow.com/a/23964285/292677

Si vous avez besoin d'une solution complexe pour pouvoir le faire souvent, vous pouvez utiliser cette procédure:

DELIMITER $$

CREATE PROCEDURE `duplicateRows`(_schemaName text, _tableName text, _whereClause text, _omitColumns text)
SQL SECURITY INVOKER
BEGIN
  SELECT IF(TRIM(_omitColumns) <> '', CONCAT('id', ',', TRIM(_omitColumns)), 'id') INTO @omitColumns;

  SELECT GROUP_CONCAT(COLUMN_NAME) FROM information_schema.columns 
  WHERE table_schema = _schemaName AND table_name = _tableName AND FIND_IN_SET(COLUMN_NAME,@omitColumns) = 0 ORDER BY ORDINAL_POSITION INTO @columns;

  SET @sql = CONCAT('INSERT INTO ', _tableName, '(', @columns, ')',
  'SELECT ', @columns, 
  ' FROM ', _schemaName, '.', _tableName, ' ',  _whereClause);

  PREPARE stmt1 FROM @sql;
  EXECUTE stmt1;
END

Vous pouvez l'exécuter avec:

CALL duplicateRows('database', 'table', 'WHERE condition = optional', 'omit_columns_optional');

Exemples

duplicateRows('acl', 'users', 'WHERE id = 200'); -- will duplicate the row for the user with id 200
duplicateRows('acl', 'users', 'WHERE id = 200', 'created_ts'); -- same as above but will not copy the created_ts column value    
duplicateRows('acl', 'users', 'WHERE id = 200', 'created_ts,updated_ts'); -- same as above but also omits the updated_ts column
duplicateRows('acl', 'users'); -- will duplicate all records in the table

AVIS DE NON-RESPONSABILITÉ: Cette solution est uniquement pour quelqu'un qui va souvent dupliquer des lignes dans de nombreuses tables, souvent. Cela pourrait être dangereux entre les mains d'un utilisateur voyou.

curmil
la source
3

Vous pouvez également passer «0» comme valeur pour la colonne à incrémenter automatiquement, la valeur correcte sera utilisée lors de la création de l'enregistrement. C'est tellement plus facile que les tables temporaires.

Source: Copie de lignes dans MySQL (voir le deuxième commentaire, par TRiG, ​​à la première solution, par Lore)

Mark Pruce
la source
1
Cette méthode fonctionne avec les nouvelles versions de MySQL lorsque NULL n'est pas acceptable.
err
3

Beaucoup de bonnes réponses ici. Voici un exemple de la procédure stockée que j'ai écrite pour accomplir cette tâche pour une application Web que je développe:

-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON

-- Create Temporary Table
SELECT * INTO #tempTable FROM <YourTable> WHERE Id = Id

--To trigger the auto increment
UPDATE #tempTable SET Id = NULL 

--Update new data row in #tempTable here!

--Insert duplicate row with modified data back into your table
INSERT INTO <YourTable> SELECT * FROM #tempTable

-- Drop Temporary Table
DROP TABLE #tempTable
RBILLC
la source
dans la version actuelle de MariaDB / MySQL, le paramètre id = null donne # 1048 - La colonne 'id' ne peut pas être nulle, le paramètre id = 0 fonctionne;
shelbypereira
2
insert into MyTable(field1, field2, id_backup)
    select field1, field2, uniqueId from MyTable where uniqueId = @Id;
IR.Programmer
la source
3
Comment est-ce mieux que d'autres réponses ici?
Smar
1
@smar imo c'est plus propre à comprendre
Satbir Kira
2

Si vous pouvez utiliser MySQL Workbench, vous pouvez le faire en cliquant avec le bouton droit sur la ligne et en sélectionnant «Copier la ligne», puis en cliquant avec le bouton droit sur la ligne vide et en sélectionnant «Coller la ligne», puis en changeant l'ID, puis en cliquant sur «Appliquer».

Copiez la ligne:

entrez la description de l'image ici

Collez la ligne copiée dans la ligne vierge:

entrez la description de l'image ici

Modifiez l'ID:

entrez la description de l'image ici

Appliquer:

entrez la description de l'image ici

Nathan Wailes
la source
0

Je cherchais la même fonctionnalité mais je n'utilise pas MySQL. Je voulais copier TOUS les champs sauf bien sûr la clé primaire (id). Il s'agissait d'une requête unique, à ne pas utiliser dans un script ou un code.

J'ai trouvé mon chemin avec PL / SQL mais je suis sûr que n'importe quel autre IDE SQL ferait l'affaire. J'ai fait un basique

SELECT * 
FROM mytable 
WHERE id=42;

Ensuite, exportez-le dans un fichier SQL où je pourrais trouver le

INSERT INTO table (col1, col2, col3, ... , col42) 
VALUES (1, 2, 3, ..., 42);

Je l'ai juste édité et utilisé:

INSERT INTO table (col1, col2, col3, ... , col42) 
VALUES (mysequence.nextval, 2, 3, ..., 42);
SabineA
la source
0

J'ai tendance à utiliser une variation de ce qui est trop court:

INSERT INTO something_log
SELECT NULL, s.*
FROM something AS s
WHERE s.id = 1;

Tant que les tables ont des champs identiques (à l'exception de l'incrémentation automatique sur la table de journal), cela fonctionne bien.

Puisque j'utilise des procédures stockées autant que possible (pour faciliter la vie à d'autres programmeurs qui ne sont pas trop familiers avec les bases de données), cela résout le problème d'avoir à revenir en arrière et à mettre à jour les procédures chaque fois que vous ajoutez un nouveau champ à une table.

Il garantit également que si vous ajoutez de nouveaux champs à une table, ils commenceront à apparaître immédiatement dans la table de journal sans avoir à mettre à jour vos requêtes de base de données (à moins bien sûr que certains en définissent explicitement un champ)

Avertissement: vous voudrez vous assurer d'ajouter tous les nouveaux champs aux deux tables en même temps afin que l'ordre des champs reste le même ... sinon vous commencerez à obtenir des bogues impairs. Si vous êtes le seul à écrire des interfaces de base de données ET que vous êtes très prudent, cela fonctionne bien. Sinon, tenez-vous à nommer tous vos champs.

Remarque: Après réflexion, à moins que vous ne travailliez sur un projet solo, vous en serez sûr que d'autres n'y travailleront pas pour répertorier explicitement tous les noms de champs et mettre à jour vos instructions de journal à mesure que votre schéma change. Ce raccourci ne vaut probablement pas le mal de tête à long terme qu'il peut causer ... surtout sur un système de production.

Corsaire
la source
0
INSÉRER DANS `dbMyDataBase`.`tblMyTable` 
(
    `IdAutoincrement`, 
    `Colonne2`, 
    `Colonne3`, 
    `Colonne4` 
) 

SÉLECTIONNER 
    NUL,  
    `Colonne2`, 
    `Colonne3`, 
    'CustomValue' AS Column4 
FROM `dbMyDataBase`.`tblMyTable` 
OERE `tblMyTable`.`Column2` = 'UniqueValueOfTheKey' 
; 
/ * mySQL 5.6 * /
Wojciech Skibiński
la source
3
Essayez d'ajouter des explications plus détaillées, ou comment cela se développe sur les autres réponses
Azsgy
-1

Videz la ligne que vous souhaitez sql puis utilisez le SQL généré, moins la colonne ID pour la réimporter.

Claire
la source