93

I was wondering if it is possible to move all rows of data from one table to another, that match a certain query?

For example, I need to move all table rows from Table1 to Table2 where their username = 'X' and password = 'X', so that they will no longer appear in Table1.

I'm using SQL Server 2008 Management Studio.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
doubleplusgood
  • 2,486
  • 11
  • 45
  • 64

14 Answers14

161

Should be possible using two statements within one transaction, an insert and a delete:

BEGIN TRANSACTION;
INSERT INTO Table2 (<columns>)
SELECT <columns>
FROM Table1
WHERE <condition>;

DELETE FROM Table1
WHERE <condition>;

COMMIT;

This is the simplest form. If you have to worry about new matching records being inserted into table1 between the two statements, you can add an and exists <in table2>.

Thorsten
  • 12,921
  • 17
  • 60
  • 79
  • 2
    you want to make sure both statements are done as a single transaction though. To say, turn off auto commit, and do a single commit after the delete, only if no errors occurred. You probably don't want to delete if the insert fails, or vice-versa. – Jay Oct 23 '09 at 12:55
  • true enough, but even if they are done as a single transaction, there may be issues if inserts occur during the execution of the two statements. Not too many databases are run in a way so that "reads are repeatable" within a transaction. – Thorsten Oct 23 '09 at 17:35
  • I believe you can use "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE" for the transaction to make sure you don't see the new records – Mike L Oct 23 '09 at 17:41
  • @IronGoofy, I know this thread has been sitting idle for a while and apologize for directing this specifically to you but what would the query look like of the condition to be met requires 2 tables ? ex. table.field1 has to be equal to table2.field1 , if it matches it cut/pastes the row to Table3 – Dani Jul 13 '11 at 08:33
  • 1
    @Dani: Not completely sure what you mean .. maybe it'll be easiest if you post this as a new question with a more detailed description/example? If you want to, you can add a comment to this answer so I can have a look in addition to all the other Stackoverflow users. – Thorsten Jul 14 '11 at 21:39
  • What about when you're dealing with auto-incrementing primary keys, and the keys for the data in the first table skip some numbers? – Panzercrisis Jul 30 '12 at 15:23
  • In the scenario the OP described, an auto-incrementing key does not really make sense in Table2. However, if that is a "requirement", Table2 should have column for its own id and the table1 id. That allows matching records in table2 to those in table1 if necessary. – Thorsten Aug 15 '12 at 20:10
  • 2
    I accidentally just deleted all the data I wanted to move because there was no `BEGIN TRANSACTION;` in the start of this example. Wouldn't it be a good idea to add that to the answer's example? – Deantwo Sep 16 '19 at 09:48
  • You may want to also guard against losing data inserted between the execution of the SELECT and DELETE: `DECLARE @inserted TABLE (ID int Primary Key); BEGIN TRANSACTION; INSERT INTO dbo.Table2 () OUTPUT INSERTED.ID INTO @inserted SELECT FROM dbo.Table1 WHERE ; DELETE FROM dbo.Table1 WHERE AND ID IN (SELECT ID FROM @inserted); COMMIT TRANSACTION;` – Lee Grissom Nov 20 '21 at 01:18
58

This is an ancient post, sorry, but I only came across it now and I wanted to give my solution to whoever might stumble upon this one day.

As some have mentioned, performing an INSERT and then a DELETE might lead to integrity issues, so perhaps a way to get around it, and to perform everything neatly in a single statement, is to take advantage of the [deleted] temporary table.

DELETE FROM [source]
OUTPUT [deleted].<column_list>
INTO [destination] (<column_list>)
that0th3rGuy
  • 1,356
  • 1
  • 15
  • 19
  • 3
    With a `DELETE` statement, all records are first written to the the `[deleted]` temporary table, before the `DELETE` clause resolves, from where they can be processed - in this case inserted into another table - after which the statement resolves. If the processing section of the statement fails, the entire statement is terminated; not only the `INTO` clause. Besides, `BEGIN TRY...BEGIN CATCH` and `ROLLBACK TRANSACTION` are great preventative statements. – that0th3rGuy Jul 16 '14 at 10:14
  • 8
    This will run into problems if destination is involved in foreign key relations. You will get the error: The target table '' of the OUTPUT INTO clause cannot be on either side of a (primary key, foreign key) relationship. Found reference constraint ''. – Niels Harremoes Nov 26 '14 at 08:34
  • Can you explain how the OUTPUT keyword works here? It seems like it is trying to put the deleted record in the [deleted] table, but the [deleted] table doesn't exist (and never does). – GreySage Jun 19 '18 at 22:13
  • 1
    @GreySage The `OUTPUT` keyword is simply used to return a tabled dataset to the subsequent statement, but is only valid in some cases, like `UPDATE` (`[inserted]`) and `DELETE` if I remember correctly. The `[deleted]` table is an implicit, temporary table -- not exactly sure where it lives or which process manages it -- scoped to the current statement execution. So, just as you can do something like `SELECT [source].[column] INTO [destination]` -- where `SELECT` returns a dataset and `INTO` receives it -- you can use `OUTPUT` to return the deleted dataset to the `INTO` which receives it. – that0th3rGuy Jun 20 '18 at 08:18
  • 1
    The `OUTPUT` statement is non-standard SQL, FWIW (e.g. doesn't work in `sqlite3`). – ijoseph Jul 02 '18 at 00:44
  • This will cause problems in a trigger as well, since the `deleted` table contains the records deleted in the transaction that caused the trigger. – Tobias Feil Mar 04 '19 at 10:25
  • the deleted table refers only to each single transaction? Or it's a temporary table valid for the instance and so shared with all transactions? My question is: what happens with two deletes at the same time with different tables? – Alessandro C Jan 09 '20 at 07:50
  • Is this whole statement transactional? – Sergey Shafiev Feb 17 '23 at 07:50
24

All these answers run the same query for the INSERT and DELETE. As mentioned previously, this risks the DELETE picking up records inserted between statements and could be slow if the query is complex (although clever engines "should" make the second call fast).

The correct way (assuming the INSERT is into a fresh table) is to do the DELETE against table1 using the key field of table2.

The delete should be:

DELETE FROM tbl_OldTableName WHERE id in (SELECT id FROM tbl_NewTableName)

Excuse my syntax, I'm jumping between engines but you get the idea.

kamranicus
  • 4,207
  • 2
  • 39
  • 57
Ken Sands
  • 241
  • 2
  • 2
11

A cleaner representation of what some other answers have hinted at:

DELETE sourceTable
OUTPUT DELETED.*
INTO destTable (Comma, separated, list, of, columns)
WHERE <conditions (if any)>
GreySage
  • 1,153
  • 19
  • 39
  • Unfortunately this will not work if the target (insert) table contains a foreign key constraint. In that case, the only solution is to use distinct insert and delete statements in a single transaction. – Carlo Bos Nov 10 '21 at 14:37
10

Yes it is. First INSERT + SELECT and then DELETE orginals.

INSERT INTO Table2 (UserName,Password)
SELECT UserName,Password FROM Table1 WHERE UserName='X' AND Password='X'

then delete orginals

DELETE FROM Table1 WHERE UserName='X' AND Password='X'

you may want to preserve UserID or someother primary key, then you can use IDENTITY INSERT to preserve the key.

see more on SET IDENTITY_INSERT on MSDN

hjpotter92
  • 78,589
  • 36
  • 144
  • 183
pirho
  • 2,972
  • 4
  • 23
  • 17
6

Use this single sql statement which is safe no need of commit/rollback with multiple statements.

INSERT Table2 (
      username,password
) SELECT username,password
      FROM    (
           DELETE Table1
           OUTPUT
                   DELETED.username,
                   DELETED.password
           WHERE username = 'X' and password = 'X'
      ) AS RowsToMove ;

Works on SQL server make appropriate changes for MySql

Dheerendra Kulkarni
  • 2,728
  • 1
  • 16
  • 18
  • 2
    Unfortunately this will not work if the target (insert) table contains a foreign key constraint. In that case, the only solution is to use distinct insert and delete statements in a single transaction. – Carlo Bos Nov 10 '21 at 14:37
5

You should be able to with a subquery in the INSERT statement.

INSERT INTO table1(column1, column2) SELECT column1, column2 FROM table2 WHERE ...;

followed by deleting from table1.

Remember to run it as a single transaction so that if anything goes wrong you can roll the entire operation back.

workmad3
  • 25,101
  • 4
  • 35
  • 56
4

Try this

INSERT INTO TABLE2 (Cols...) SELECT Cols... FROM TABLE1 WHERE Criteria

Then

DELETE FROM TABLE1 WHERE Criteria
Adriaan Stander
  • 162,879
  • 31
  • 289
  • 284
2

You could try this:

SELECT * INTO tbl_NewTableName 
FROM tbl_OldTableName
WHERE Condition1=@Condition1Value

Then run a simple delete:

DELETE FROM tbl_OldTableName
WHERE Condition1=@Condition1Value
royse41
  • 2,310
  • 4
  • 22
  • 29
2

You may use "Logical Partitioning" to switch data between tables:

By updating the Partition Column, data will be automatically moved to the other table:

here is the sample:

CREATE TABLE TBL_Part1
(id  INT NOT NULL,
 val VARCHAR(10) NULL,
 PartitionColumn  VARCHAR(10) CONSTRAINT CK_Part1 CHECK(PartitionColumn = 'TBL_Part1'),
 CONSTRAINT TBL_Part1_PK PRIMARY KEY(PartitionColumn, id)
);

CREATE TABLE TBL_Part2
(id  INT NOT NULL,
 val VARCHAR(10) NULL,
 PartitionColumn  VARCHAR(10) CONSTRAINT CK_Part2 CHECK(PartitionColumn = 'TBL_Part2'),
 CONSTRAINT TBL_Part2_PK  PRIMARY KEY(PartitionColumn, id)
);

GO

CREATE VIEW TBL(id, val, PartitionColumn)
WITH SCHEMABINDING
AS
     SELECT id, val, PartitionColumn FROM dbo.TBL_Part1
     UNION ALL  
     SELECT id, val, PartitionColumn FROM dbo.TBL_Part2;

GO

--Insert sample to TBL ( will be inserted to Part1 )
INSERT INTO TBL
VALUES(1, 'rec1', 'TBL_Part1');

INSERT INTO TBL
VALUES(2, 'rec2', 'TBL_Part1');

GO

--Query sub table to verify
SELECT * FROM TBL_Part1

GO
--move the data to table TBL_Part2 by Logical Partition switching technique
UPDATE TBL
  SET
      PartitionColumn = 'TBL_Part2';

GO

--Query sub table to verify
SELECT * FROM TBL_Part2
abdkok
  • 306
  • 2
  • 6
1

Here is how do it with single statement

WITH deleted_rows AS (
DELETE FROM source_table WHERE id = 1
RETURNING *
) 
INSERT INTO destination_table 
SELECT * FROM deleted_rows;

EXAMPLE:

    postgres=# select * from test1 ;
 id |  name
----+--------
  1 | yogesh
  2 | Raunak
  3 | Varun
(3 rows)


postgres=# select * from test2;
 id | name
----+------
(0 rows)


postgres=# WITH deleted_rows AS (
postgres(# DELETE FROM test1 WHERE id = 1
postgres(# RETURNING *
postgres(# )
postgres-# INSERT INTO test2
postgres-# SELECT * FROM deleted_rows;
INSERT 0 1


postgres=# select * from test2;
 id |  name
----+--------
  1 | yogesh
(1 row)

postgres=# select * from test1;
 id |  name
----+--------
  2 | Raunak
  3 | Varun
pgyogesh
  • 342
  • 2
  • 13
  • 1
    This uses incorrect syntax (near the as and the returning), and just doesn't work the intended way. – GreySage Jun 19 '18 at 22:08
0

If the two tables use the same ID or have a common UNIQUE key:

1) Insert the selected record in table 2

INSERT INTO table2 SELECT * FROM table1 WHERE (conditions)

2) delete the selected record from table1 if presents in table2

DELETE FROM table1 as A, table2 as B WHERE (A.conditions) AND  (A.ID = B.ID)
Sabyasachi Mishra
  • 1,677
  • 2
  • 31
  • 49
user1847437
  • 213
  • 1
  • 3
  • 12
0

It will create a table and copy all the data from old table to new table

SELECT * INTO event_log_temp FROM event_log

And you can clear the old table data.

DELETE FROM event_log

0

For some scenarios, it might be the easiest to script out Table1, rename the existing Table1 to Table2 and run the script to recreate Table1.

  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 11 '22 at 19:55