【问题标题】:Swap values for two rows in the same table in SQL Server在 SQL Server 的同一个表中交换两行的值
【发布时间】:2011-02-15 01:12:04
【问题描述】:

我想交换表中两行的值。我有两行的行 ID。有什么查询可以做到这一点吗?这是一个例子。在查询之前我有这个:

第 1 行:1,2,3 第 2 行:5,6,7

交换后我想要这个:

第 1 行:5,6,7 第 2 行:1,2,3

【问题讨论】:

  • 我第一次阅读您的问题时,我以为您想复制一行,但现在我开始认为您想更新一行中的值以等于另一行......但我没有得到你问题的“反之亦然”部分。你能举一些例子来帮助澄清你的问题吗?
  • 反之亦然,这意味着我还需要使用来自行更新示例的数据更新我从中获取数据的行:row1:1,2,3 row2:5,6,7 应对后我想要 row1 : 5,6,7 row2 : 1,2,3 我希望你明白了。
  • @AmRoSH:我已经重写了你的问题,以便更容易理解。我希望我做得正确,否则请重新编辑。并且您应该尝试在将来清楚地写出您的问题 - 它会更快地为您提供更高质量的答案。

标签: sql sql-server


【解决方案1】:

如果您想将两个已知 ID 的值从一行交换到另一行,请尝试以下操作:

--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 这个查询是否适用于多行意味着如果这些 ID 是外键并且我需要交换它们的行。
  • 如果要交换的列是外键或只是普通数据,这应该可以工作。
  • @JanakThakkar,我尽量不更新主键。正确处理所有外键真的很痛苦。如果您没有约束问题(FK、PK),那么您可以更新 PK,这取决于您需要/想要的。
  • 这是在 2010 年写的,我认为在更现代的 SQL Server 版本中,使用表变量而不是临时表会运行得更好。
【解决方案2】:

简单的更新工作:

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 

结果:行值被交换。

【讨论】:

    【解决方案3】:

    如果您只需要交换几行,那么是的,您可以像其他答案一样使用量身定制的 case 语句和 join 语句来强制它。但是如果你需要对很多行进行操作,那就很麻烦了。

    简单、可扩展的解决方案

    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
    

    备注

    • 您可以进行两行交换、多行交换和复制。它很灵活。
    • 根据需要指定尽可能多的目标/源行对。只会更新目标行。
    • 指定要复制的列。只会更新这些列。
    • 没有要清理的临时表。
    • 很容易重复使用,因为行 ID 列在一个明显的位置

    【讨论】:

      【解决方案4】:
      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')
      

      您不一定需要使用表的 ID 列,我相信您可以使用适当的连接逻辑按任何列进行搜索。将表连接到自身是诀窍。

      【讨论】:

      • 如果我只使用您的查询更新一列,是否可以交换整个原始数据。 ?因为我只使用您的查询更新一列并且它交换了整行。
      【解决方案5】:

      我最近遇到了类似的问题,我有一个用于订购输出的列,并希望允许移动订单。我一直在寻找答案并遇到了这个问题。这没有充分回答我的特定查询,但也许我的解决方案会对其他人有所帮助。

      我的数据库看起来像这样

      表:Order_Table

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

      我希望能够在 php 中使用 pdo 交换它们。 最终我找到了一种使用 SQL 查询的方法

      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)
      

      【讨论】:

        【解决方案6】:

        您需要按“WHERE”条件选择所有记录, 然后通过“CASE”条件更新“SET”。

        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);
        

        【讨论】:

          【解决方案7】:

          上面的例子都不是实用的......它应该看起来像下面的更新部分:

          /*******************************************************************************/
          /*  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
          

          【讨论】:

            【解决方案8】:

            如果它只是一个必须在两行之间交换的值:

            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)
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-03-29
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多