19

I want to swap the values from two rows in a table. I have the rows IDs of the two rows. Is there any query to do that? Here is an example. Before the query I have this:

row1 : 1,2,3
row2 : 5,6,7

After the swap I want this:

row1 : 5,6,7
row2 : 1,2,3
MarredCheese
  • 17,541
  • 8
  • 92
  • 91
Amr Elnashar
  • 1,739
  • 12
  • 33
  • 53
  • The first time I read your question I thought you wanted to make a copy of a row, but now I'm starting to think that you want to update the values in one row to equal the values in another row... but I don't get the 'vice versa' part of your question. Can you give some examples to help clarify your question? – Mark Byers May 03 '10 at 13:56
  • Vice versa here means that i need also to update the row that i took data from it with the data from row updated example : row1 : 1,2,3 row2 : 5,6,7 after coping i want row1 : 5,6,7 row2 : 1,2,3 I hope you got it. – Amr Elnashar May 03 '10 at 14:08
  • @AmRoSH: I've rewritten your question so that it is easier to understand. I hope that I have done it correctly, otherwise please edit it again. And you should try to write you question clearly in future - it will get you better quality answers more quickly. – Mark Byers May 03 '10 at 20:14

9 Answers9

16

If you want to swap values from one row to the other for two known IDs try something like this:

--need to store the original values
SELECT
    *,CASE WHEN id=123 then 987 ELSE 123 END AS JoinId
    INTO #Temp
    FROM YourTable
    WHERE ID in (123,987)

--swap values
UPDATE y
    SET col1=t.col1
        ,col2=t.col2
    FROM YourTable        y
        INNER JOIN #Temp  t ON y.id =t.JoinId
    WHERE ID in (123,987)
KM.
  • 101,727
  • 34
  • 178
  • 212
  • Thanks KM is this query works with multiple rows means if those IDs are forign keys and i need to swap their rows. – Amr Elnashar May 03 '10 at 15:27
  • this should work if the columns to swap are foreign keys or just plain data. – KM. May 03 '10 at 17:12
  • @JanakThakkar, I try to never update primary keys. it is a real pain to properly handle all foreign keys. If you don' have constraint issues (FK, PK) then you can update a PK, it depends on what you need/want. – KM. Nov 30 '15 at 14:23
  • As this was written back in 2010, I think in more modern versions of SQL Server this would run better using a table-variable rather than a temporary-table. – Dai Jan 08 '20 at 16:36
  • I had to add a `DROP TABLE IF EXISTS #Temp` at the end to ensure repeatability. – Scott Severance Apr 05 '23 at 19:49
7

If you just need to swap a couple of rows, then you can use tailor-made case statements and joins like in the other answers. If you need to operate on many rows, that's going to be a pain though. In that case, I suggest using a mapping table.

A Simple, Scalable Solution

WITH map AS (
    SELECT *
    FROM (VALUES
        (1, 2),  -- Here's an example of swapping two rows:
        (2, 1),  -- 1 <- 2,  2 <- 1

        (3, 4),  -- Here's an example of rotating three rows:
        (4, 5),  -- 3 <- 4,  4 <- 5,  5 <- 3
        (5, 3),

        (6, 7)   -- Here's an example of just copying one row to another: 3 <- 5
    ) AS a (destID, srcID)
)
UPDATE destination
SET
    ColumnA = source.ColumnA,
    ColumnB = source.ColumnB,
    ColumnC = source.ColumnC
FROM
    SomeTable AS destination
    JOIN map ON map.destID = destination.ID
    JOIN SomeTable AS source ON source.ID = map.srcID

Notes

  • You can do two-row swaps, many-row swaps, and copies. It's flexible.
  • Specify as many destination/source row pairs as needed. Only destination rows will be updated.
  • Specify the columns you want to be copied over. Only those columns will be updated.
  • There's no temporary table to clean up.
  • It's easy to reuse since the row IDs are listed in a single, obvious place.
MarredCheese
  • 17,541
  • 8
  • 92
  • 91
5

Simple update works:

UPDATE myTable
SET 
col1 = CASE WHEN col1 = 1 THEN 5 ELSE 1 END,
col2 = CASE WHEN col2 = 2 THEN 6 ELSE 2 END,
col3 = CASE WHEN col3 = 3 THEN 7 ELSE 3 END 

Result: row values are swapped.

Israel Margulies
  • 8,656
  • 2
  • 30
  • 26
3
UPDATE t1
SET
t1.col1 = t2.col1
,t1.col2 = t2.col2
,t1.col3 = t2.col3
,t1.col4 = t2.col4
--and so forth...
FROM YourTable AS t1
INNER JOIN YourTable AS t2
    ON      (t1.ID = '1'
            AND t2.ID = '2')
        OR
            (t1.ID = '2'
            AND t2.ID = '1')

You don't necessarily need to use the ID column of your table, I believe you could search by any column, with the proper joining logic. Joining the table to itself is the trick.

Brian
  • 31
  • 1
  • is that possible to swap whole raw if i update one column only using your query. ? Because I just update one column only using your query and its swapped the whole row. – FullStack Jun 30 '17 at 05:07
1

I had a similar issue recently I had a column for ordering the output and wanted to allow moving the order around. I was looking for the answer and ran across this question. This didn't sufficiently answer my particular query but maybe my solution will help others.

I had my database look like so

Table:Order_Table

Index_Column,Order_Column,Text
1           ,1           ,"Second Test text"
2           ,2           ,"First Test text"

I wanted to be able to swap them around using pdo in php. Ultimately I found a way to do it with one SQL query

UPDATE `Order_Table` AS o 
INNER JOIN (SELECT `Index_Column`, `Order_Column` FROM `Order_Table` 
WHERE `Index_Column` IN (:Index1,:Index2)) 
AS t ON o.`Index_Column` <> t.`Index_Column` 
SET o.`Order_Column` = t.`Order_Column` 
WHERE o.`Index_Column` IN (:Index1,:Index2)
penguinjeff
  • 306
  • 3
  • 8
1

You need to select all records by "WHERE" condition, Then "SET" update by "CASE" condition.

UPDATE tbl_Temp SET 
fk_userType = CASE fk_userType WHEN 1 THEN 2 WHEN 2 THEN 1 END,
fk_userRole = CASE fk_userRole WHEN 1 THEN 2 WHEN 2 THEN 1 END
WHERE (fk_userType = 1 AND fk_userRole = 1) OR (fk_userType = 2 AND fk_userRole = 2);
Nikunj Patel
  • 395
  • 1
  • 5
  • 19
1

None of the above examples is practical ... It should look like the following update section:

/*******************************************************************************/
/*  DATA TABLE IS PREPARING                                                    */
/*******************************************************************************/
IF EXISTS (SELECT TOP 1 * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'TEST' AND TABLE_SCHEMA = 'dbo')
DROP TABLE [dbo].[TEST];

CREATE TABLE [dbo].[TEST](
  [ID]       int           IDENTITY(1,1) NOT NULL,
  [Name]     varchar(50)   NULL,
  [Surname]  varchar(50)   NULL,
  [AGE]      int           NULL,
  CONSTRAINT [PK_TEST] PRIMARY KEY CLUSTERED 
    ( [ID] ASC ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
/*******************************************************************************/
/*  INSERTING TEST VALUES                                                      */
/*******************************************************************************/
INSERT INTO dbo.TEST (Name, Surname, AGE)
SELECT 'Sevim'        , 'PARLAYAN' , 36   UNION ALL
SELECT 'Uğur'         , 'PARLAYAN' , 41   UNION ALL
SELECT 'Berkan Cahit' , 'PARLAYAN' , 17   UNION ALL
SELECT 'Miray Çağla'  , 'PARLAYAN' , 6    ;

SELECT * FROM dbo.TEST ORDER BY ID;


-- At this point maybe the trigger can be disabled...

/*******************************************************************************/
/*  I'm swapping Uğur and Sevim rows (So, rows into 1 and 2 do swapping )...   */
/*******************************************************************************/
UPDATE  TT
SET     TT.Name     = ZZZ.Name
     ,  TT.Surname  = ZZZ.Surname
     ,  TT.AGE      = ZZZ.AGE
FROM     dbo.TEST as TT
JOIN     (
           SELECT TOP 1 * FROM dbo.TEST WHERE ID = 2 /* Big key value first     */  UNION ALL
           SELECT TOP 1 * FROM dbo.TEST WHERE ID = 1 /* Then small key value... */
         ) as ZZZ on ZZZ.ID in (1, 2)
WHERE   TT.ID in (1, 2) ;

-- At this point maybe the trigger can be activated...

SELECT * FROM dbo.TEST ORDER BY ID
0

If it's just one value that has to be swapped between two rows :

UPDATE task t1 SET ordre = (SELECT ordre FROM task t2 WHERE t1.id <> t2.id AND id IN (26,25)) WHERE id IN (26,25)
Ben
  • 1,548
  • 1
  • 11
  • 12
0

UPDATE Salary SET sex = CASE WHEN sex ='m' THEN 'f' ELSE 'm' END