153

This question comes close to what I need, but my scenario is slightly different. The source table and destination table are the same and the primary key is a uniqueidentifier (guid). When I try this:

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

I obviously get a primary key constraint violation, since I'm attempting to copy over the primary key. Actually, I don't want to copy over the primary key at all. Rather, I want to create a new one. Additionally, I would like to selectively copy over certain fields, and leave the others null. To make matters more complex, I need to take the primary key of the original record, and insert it into another field in the copy (PreviousId field).

I'm sure there is an easy solution to this, I just don't know enough TSQL to know what it is.

Community
  • 1
  • 1
Kilhoffer
  • 32,375
  • 22
  • 97
  • 124

11 Answers11

221

Try this:

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

Any fields not specified should receive their default value (which is usually NULL when not defined).

TylerH
  • 20,799
  • 66
  • 75
  • 101
AaronSieb
  • 8,106
  • 8
  • 39
  • 58
123

I like this solution. I only have to specify the identity column(s).

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;

The "id"-column is the identity column and that's the only column I have to specify. It's better than the other way around anyway. :-)

I use SQL Server. You may want to use "CREATE TABLE" and "UPDATE TABLE" at row 1 and 2. Hmm, I saw that I did not really give the answer that he wanted. He wanted to copy the id to another column also. But this solution is nice for making a copy with a new auto-id.

I edit my solution with the idéas from 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;

You can drop more than one column by separating them with a ",". The :id should be replaced with the id of the row you want to copy. MyDatabase, MyTable and IndexField should be replaced with your names (of course).

TylerH
  • 20,799
  • 66
  • 75
  • 101
Jonas
  • 1,356
  • 1
  • 8
  • 11
  • 1
    This is tricky, how can you be sure you are copying the correct id to the correct row during the INSERT INTO in line 3? – Emond Feb 22 '13 at 13:47
  • The id-column is an identity column (in my table) so I don't care about the value. I just want a new record holding the same values as the "old" one. – Jonas Feb 22 '13 at 14:26
  • id = 1 on the first row is just an example. – Jonas Apr 24 '13 at 09:49
  • THis works great! I was not using an SQL account that had enough security so I had to use a schema and give select permission to the schema in order to execute the select * from TempTable – Jaydel Gluckie Aug 12 '14 at 22:42
  • 9
    For `TempTable`, wouldn't it be better to use `#TempTable`, so it is a true temporary table? – Jess Sep 09 '14 at 15:44
  • 3
    I am using this in the following format `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;` This way I know it will be cleaned up asap without any delays from writing an intermittend file to disc. – Tschallacka Mar 26 '15 at 13:21
  • This fails in SQL Server 2014 (and probably earlier too) with the error "An explicit value for the identity column in table 'dbo.' can only be specified when a column list is used and IDENTITY_INSERT is ON."
    – Ian Dec 16 '15 at 03:06
  • Yes Ian, that's correct! (I'm sorry for the late reply. I didn't see this until now.) That's why I'm dropping the identity column. In my case, I'm not interested in the identity column. I would get some kind of "duplicate key" error if I would copy the identity column also. What do you want to do? – Jonas Jan 11 '16 at 14:04
  • 3
    You could also use `$identity` if you didn't want to hard code your identity column name – Factor Mystic Jun 22 '16 at 17:17
  • 1
    It's an old one, but I appreciate the answer, only needed to modify the `INSERT` part a bit: `INSERT INTO service_plan SELECT Newid(), * FROM TempTable;` – Sura Chaitanya Jun 19 '18 at 15:52
19

I'm guessing you're trying to avoid writing out all the column names. If you're using SQL Management Studio you can easily right click on the table and Script As Insert.. then you can mess around with that output to create your query.

Matt Hinze
  • 13,577
  • 3
  • 35
  • 40
12

Specify all fields but your ID field.

INSERT INTO MyTable (FIELD2, FIELD3, ..., FIELD529, PreviousId)
SELECT FIELD2, NULL, ..., FIELD529, FIELD1
FROM MyTable
WHERE FIELD1 = @Id;
Scott Bevington
  • 1,201
  • 10
  • 12
10

I have the same issue where I want a single script to work with a table that has columns added periodically by other developers. Not only that, but I am supporting many different versions of our database as customers may not all be up-to-date with the current version.

I took the solution by Jonas and modified it slightly. This allows me to make a copy of the row and then change the primary key before adding it back into the original source table. This is also really handy for working with tables that do not allow NULL values in columns and you don't want to have to specify each column name in the INSERT.

This code copies the row for 'ABC' to 'XYZ'

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

Once you have finished the drop the temp table.

DROP TABLE #TempRow;
Tony Tullemans
  • 158
  • 2
  • 4
6

I know my answer is late to the party. But the way i solved is bit different than all the answers.

I had a situation, i need to clone a row in a table except few columns. Those few will have new values. This process should support automatically for future changes to the table. This implies, clone the record without specifying any column names.

My approach is to,

  1. Query Sys.Columns to get the full list of columns for the table and include the names of columns to skip in where clause.
  2. Convert that in to CSV as column names.
  3. Build Select ... Insert into script based on this.


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
  • 2,198
  • 1
  • 23
  • 26
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
  • 58,241
  • 9
  • 71
  • 99
1

My table has 100 fields, and I needed a query to just work. Now I can switch out any number of fields with some basic conditional logic and not worry about its ordinal position.

  1. Replace the below table name with your table name

    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. Replace the original identity field name with your PK field name

    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. Replace the table names again (both target table name and source table name); edit your where conditions

    SQL = "INSERT INTO TARGET-TABLE-NAME (" & columnListTARGET & ") SELECT " & columnListSOURCE & " FROM SOURCE-TABLE-NAME WHERE (FIELDNAME = FIELDVALUE)" 
    Conn.Execute(SQL)
    
Jules
  • 14,200
  • 13
  • 56
  • 101
Rit Man
  • 51
  • 2
1

If "key" is your PK field and it's autonumeric.

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

it will generate a new record, copying field1 and field2 from the original record

Eduardo Campañó
  • 6,778
  • 4
  • 27
  • 24
0

You can do like this:

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

There instead of 112 you should put a number of the maximum id in table DENI/FRIEN01P.

Chains
  • 12,541
  • 8
  • 45
  • 62
0

Here is how I did it using ASP classic and couldn't quite get it to work with the answers above and I wanted to be able to copy a product in our system to a new product_id and needed it to be able to work even when we add in more columns to the table.

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
  • 362
  • 3
  • 15
  • I dont think you need to issue 5 separate SQL commands to complete this. And also, who do you determine in 2nd command, that product id should be 34567? – Jeyara Mar 11 '19 at 23:33
  • 34567 is just an example. You can set it to a variable or whatever you want. If you have a better way of doing this in ASP Classic, let me know. :) – Daniel Nordh Mar 13 '19 at 07:32
  • you could write a stored proc to run all these in single line. Also, usually PK used to be an auto increment value. So assigning to yourself is not a good idea. – Jeyara Mar 15 '19 at 00:30
  • How would you write said procedure? Please enlighten me. PK? If you mean article ID's, in our system we use article ID's based on which store we sell them in. For example our toy store all have article number starting with 30 and our sports store starts with 60. Plus we save spaces so that if we get a new colour in, for example, we can have an article ID next to the others of the same kind. As said, this was my solution that works in our system. How you set variables is not that important and most programmers would understand what works for them and their system. – Daniel Nordh Mar 15 '19 at 07:44
  • PK stands for primary key. In your case its '12345'. See https://stackoverflow.com/questions/21561657/classic-asp-ado-execute-stored-procedure-passing-in-parameters for how to use stored proc in Classic ASP if you dont know how to. To do this, move all those in to one stored proc and pass your '12345' as parameter. The way you do would work but parameterized Scripts are the recommended these days. – Jeyara Mar 17 '19 at 23:17
  • Need to read up on it, because at first glance I don't understand how it would help make my code more efficient. I'm still an ASP classic novice, and I'm mostly satisfied that my code works great for us for the few times we need to copy an article. :) And just to clarify once more. In our actual code, I'm fetching the variables '12345' and '34567' from somewhere else. They are not fixed values. Since the question is about SQL, I'm only showing that core part of the code. – Daniel Nordh Mar 18 '19 at 07:38