Comment copier un enregistrement dans une table SQL mais échanger l'identifiant unique de la nouvelle ligne?

123

Cette question se rapproche de ce dont j'ai besoin, mais mon scénario est légèrement différent. La table source et la table de destination sont identiques et la clé primaire est un uniqueidentifier (guid). Quand j'essaye ceci:

insert into MyTable
    select * from MyTable where uniqueId = @Id;

J'obtiens évidemment une violation de contrainte de clé primaire, car j'essaie de copier la clé primaire. En fait, je ne veux pas du tout copier la clé primaire. Je veux plutôt en créer un nouveau. De plus, je voudrais copier sélectivement certains champs et laisser les autres nuls. Pour rendre les choses plus complexes, je dois prendre la clé primaire de l'enregistrement d'origine et l'insérer dans un autre champ de la copie (champ PreviousId).

Je suis sûr qu'il existe une solution simple à cela, je ne connais tout simplement pas assez TSQL pour savoir ce que c'est.

Kilhoffer
la source

Réponses:

186

Essaye ça:


insert into MyTable(field1, field2, id_backup)
    select field1, field2, uniqueId from MyTable where uniqueId = @Id;

Tous les champs non spécifiés doivent recevoir leur valeur par défaut (qui est généralement NULL lorsqu'elle n'est pas définie).

AaronSieb
la source
2
Impressionnant. A travaillé comme un charme. C'était tellement évident une fois que vous l'avez écrit. Merci!!
Kilhoffer le
1
Cela fonctionnera si vous avez peu de colonnes. Si vous avez par exemple 20 ou 30 colonnes, vous pouvez toujours utiliser cette méthode, mais ce serait intimidant. En outre, chaque fois que vous ajoutez une colonne et que vous souhaitez copier également ces données, vous devez ajouter la colonne à ce script. Le mieux est de générer des insertions dynamiquement à l'aide de tables sys.
Jeyara
93

Ok, je sais que c'est un vieux problème mais je poste quand même ma réponse.

J'aime cette solution. Je n'ai qu'à spécifier la (les) colonne (s) d'identité.

SELECT * INTO TempTable FROM MyTable_T WHERE id = 1;
ALTER TABLE TempTable DROP COLUMN id;
INSERT INTO MyTable_T SELECT * FROM TempTable;
DROP TABLE TempTable;

La colonne "id" est la colonne d'identité et c'est la seule colonne que je dois spécifier. C'est mieux que l'inverse de toute façon. :-)

J'utilise SQL Server. Vous pouvez utiliser " CREATE TABLE" et "UPDATE TABLE " aux rangées 1 et 2. Hmm, j'ai vu que je n'ai pas vraiment donné la réponse qu'il voulait. Il voulait également copier l'identifiant dans une autre colonne. Mais cette solution est intéressante pour faire une copie avec un nouvel identifiant automatique.

J'édite ma solution avec les idéas de Michael Dibbets.

use MyDatabase; 
SELECT * INTO #TempTable FROM [MyTable] WHERE [IndexField] = :id;
ALTER TABLE #TempTable DROP COLUMN [IndexField]; 
INSERT INTO [MyTable] SELECT * FROM #TempTable; 
DROP TABLE #TempTable;

Vous pouvez supprimer plusieurs colonnes en les séparant par un ",". Le: id doit être remplacé par l'id de la ligne que vous souhaitez copier. MyDatabase, MyTable et IndexField doivent être remplacés par vos noms (bien sûr).

Jonas
la source
1
C'est délicat, comment pouvez-vous être sûr que vous copiez le bon identifiant dans la bonne ligne lors de l'insertion dans la ligne 3?
Erno
La colonne id est une colonne d'identité (dans ma table) donc je ne me soucie pas de la valeur. Je veux juste un nouveau record contenant les mêmes valeurs que "l'ancien".
Jonas
9
Car TempTable, ne serait-il pas préférable d'utiliser #TempTable, donc c'est une vraie table temporaire?
Jess
3
J'utilise ceci dans le format suivant De use MyDatabase; SELECT * INTO #TempTable FROM [TABLE] WHERE [indexfield] = :id; ALTER TABLE #TempTable DROP COLUMN [indexfield]; INSERT INTO [TABLE] SELECT * FROM #TempTable; DROP TABLE #TempTable;cette façon, je sais qu'il sera nettoyé dès que possible sans aucun retard de l'écriture d'un fichier intermittent sur le disque.
Tschallacka
3
Vous pouvez également utiliser $identitysi vous ne voulez pas coder en dur le nom de votre colonne d'identité
Factor Mystic
12

Je suppose que vous essayez d'éviter d'écrire tous les noms de colonnes. Si vous utilisez SQL Management Studio, vous pouvez facilement cliquer avec le bouton droit de la souris sur la table et Script As Insert .. alors vous pouvez jouer avec cette sortie pour créer votre requête.

Matt Hinze
la source
10

Spécifiez tous les champs sauf votre champ ID.

INSERT INTO MyTable (FIELD2, FIELD3, ..., FIELD529, PreviousId)
SELECT FIELD2, NULL, ..., FIELD529, FIELD1
FROM MyTable
WHERE FIELD1 = @Id;
Scott Bevington
la source
6

J'ai le même problème où je veux qu'un seul script fonctionne avec une table qui a des colonnes ajoutées périodiquement par d'autres développeurs. Non seulement cela, mais je prends en charge de nombreuses versions différentes de notre base de données car les clients ne sont peut-être pas tous à jour avec la version actuelle.

J'ai pris la solution de Jonas et l'ai légèrement modifiée. Cela me permet de faire une copie de la ligne, puis de modifier la clé primaire avant de l'ajouter à nouveau dans la table source d'origine. C'est également très pratique pour travailler avec des tables qui n'autorisent pas les valeurs NULL dans les colonnes et vous ne voulez pas avoir à spécifier chaque nom de colonne dans INSERT.

Ce code copie la ligne de «ABC» vers «XYZ»

SELECT * INTO #TempRow FROM SourceTable WHERE KeyColumn = 'ABC';
UPDATE #TempRow SET KeyColumn = 'XYZ';
INSERT INTO SourceTable SELECT * FROM #TempRow;
DELETE #TempRow;

Une fois que vous avez terminé, déposez la table temporaire.

DROP TABLE #TempRow;
TonyT
la source
4

Je sais que ma réponse est en retard à la fête. Mais la façon dont j'ai résolu est un peu différente de toutes les réponses.

J'ai eu une situation, j'ai besoin de cloner une ligne dans un tableau sauf quelques colonnes. Ces quelques-uns auront de nouvelles valeurs. Ce processus doit prendre en charge automatiquement les modifications futures de la table. Cela implique de cloner l'enregistrement sans spécifier de nom de colonne.

Mon approche est de,

  1. Interrogez Sys.Columns pour obtenir la liste complète des colonnes de la table et inclure les noms des colonnes à ignorer dans la clause where.
  2. Convertissez-le en CSV en tant que noms de colonne.
  3. Build Select ... Insérer dans le script basé sur ceci.


declare @columnsToCopyValues varchar(max), @query varchar(max)
SET @columnsToCopyValues = ''

--Get all the columns execpt Identity columns and Other columns to be excluded. Say IndentityColumn, Column1, Column2 Select @columnsToCopyValues = @columnsToCopyValues + [name] + ', ' from sys.columns c where c.object_id = OBJECT_ID('YourTableName') and name not in ('IndentityColumn','Column1','Column2') Select @columnsToCopyValues = SUBSTRING(@columnsToCopyValues, 0, LEN(@columnsToCopyValues)) print @columnsToCopyValues

Select @query = CONCAT('insert into YourTableName (',@columnsToCopyValues,', Column1, Column2) select ', @columnsToCopyValues, ',''Value1'',''Value2'',', ' from YourTableName where IndentityColumn =''' , @searchVariable,'''')

print @query exec (@query)

Jeyara
la source
2
insert into MyTable (uniqueId, column1, column2, referencedUniqueId)
select NewGuid(), // don't know this syntax, sorry
  column1,
  column2,
  uniqueId,
from MyTable where uniqueId = @Id
Jeffrey L Whitledge
la source
1

Si "clé" est votre champ PK et qu'il est autonumérique.

insert into MyTable (field1, field2, field3, parentkey)
select field1, field2, null, key from MyTable where uniqueId = @Id

il générera un nouvel enregistrement, en copiant champ1 et champ2 à partir de l'enregistrement d'origine

Eduardo Campañó
la source
1

Ma table contient 100 champs et j'avais besoin d'une requête pour fonctionner. Maintenant, je peux changer n'importe quel nombre de champs avec une logique conditionnelle de base et ne pas me soucier de sa position ordinale.

  1. Remplacez le nom de la table ci-dessous par le nom de votre table

    SQLcolums = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE (TABLE_NAME = 'TABLE-NAME')"
    
    Set GetColumns = Conn.Execute(SQLcolums)
    Do WHILE not GetColumns.eof
    
    colName = GetColumns("COLUMN_NAME")
  2. Remplacez le nom du champ d'identité d'origine par votre nom de champ PK

    IF colName = "ORIGINAL-IDENTITY-FIELD-NAME" THEN ' ASSUMING THAT YOUR PRIMARY KEY IS THE FIRST FIELD DONT WORRY ABOUT COMMAS AND SPACES
        columnListSOURCE = colName 
        columnListTARGET = "[PreviousId field name]"
    ELSE
        columnListSOURCE = columnListSOURCE & colName
        columnListTARGET = columnListTARGET & colName
    END IF
    
    GetColumns.movenext
    
    loop
    
    GetColumns.close    
  3. Remplacez à nouveau les noms de table (nom de la table cible et nom de la table source); modifier vos whereconditions

    SQL = "INSERT INTO TARGET-TABLE-NAME (" & columnListTARGET & ") SELECT " & columnListSOURCE & " FROM SOURCE-TABLE-NAME WHERE (FIELDNAME = FIELDVALUE)" 
    Conn.Execute(SQL)
Rit Man
la source
0

Vous pouvez faire comme ceci:

INSERT INTO DENI/FRIEN01P 
SELECT 
   RCRDID+112,
   PROFESION,
   NAME,
   SURNAME,
   AGE, 
   RCRDTYP, 
   RCRDLCU, 
   RCRDLCT, 
   RCRDLCD 
FROM 
   FRIEN01P      

Là, au lieu de 112, vous devez mettre un numéro de l'identifiant maximum dans la table DENI / FRIEN01P.

Denis Kutlubaev
la source
0

Voici comment je l'ai fait en utilisant ASP classic et je n'ai pas tout à fait réussi à le faire fonctionner avec les réponses ci-dessus et je voulais pouvoir copier un produit de notre système vers un nouveau product_id et j'en avais besoin pour pouvoir fonctionner même lorsque nous ajoutez plus de colonnes au tableau.

Cn.Execute("CREATE TEMPORARY TABLE temprow AS SELECT * FROM product WHERE product_id = '12345'")
Cn.Execute("UPDATE temprow SET product_id = '34567'")
Cn.Execute("INSERT INTO product SELECT * FROM temprow")
Cn.Execute("DELETE temprow")
Cn.Execute("DROP TABLE temprow")
Daniel Nordh
la source
Je ne pense pas que vous deviez émettre 5 commandes SQL distinctes pour terminer cela. Et aussi, qui déterminez-vous dans la 2ème commande, que l'ID de produit devrait être 34567?
Jeyara
34567 n'est qu'un exemple. Vous pouvez le définir sur une variable ou ce que vous voulez. Si vous avez une meilleure façon de faire cela dans ASP Classic, faites-le moi savoir. :)
Daniel Nordh
vous pouvez écrire un processus stocké pour exécuter tout cela sur une seule ligne. En outre, PK était généralement une valeur d'incrémentation automatique. Donc, vous assigner n'est pas une bonne idée.
Jeyara
Comment écririez-vous cette procédure? Veuillez m'éclairer. PK? Si vous voulez dire des identifiants d'article, dans notre système, nous utilisons des identifiants d'article en fonction du magasin dans lequel nous les vendons. Par exemple, notre magasin de jouets a tous un numéro d'article commençant par 30 et notre magasin de sport commence par 60. De plus, nous économisons des espaces de sorte que si nous obtenir une nouvelle couleur, par exemple, nous pouvons avoir un ID d'article à côté des autres du même type. Comme dit, c'était ma solution qui fonctionne dans notre système. La façon dont vous définissez les variables n'est pas si importante et la plupart des programmeurs comprendraient ce qui fonctionne pour eux et leur système.
Daniel Nordh
PK signifie clé primaire. Dans votre cas, c'est «12345». Voir stackoverflow.com/questions/21561657/ ... pour savoir comment utiliser le processus stocké dans ASP classique si vous ne savez pas comment le faire. Pour ce faire, déplacez tous ceux-ci dans un processus stocké et passez votre '12345' comme paramètre. La façon dont vous le faites fonctionnerait, mais les scripts paramétrés sont recommandés de nos jours.
Jeyara