【问题标题】:TSQL: UPDATE with INSERT INTO SELECT FROMTSQL:使用 INSERT INTO SELECT FROM 进行更新
【发布时间】:2010-10-19 01:22:42
【问题描述】:

所以我有一个旧数据库,我正在迁移到一个新数据库。新的架构略有不同,但大多兼容。此外,我想从零开始重新编号所有表。

目前我一直在使用我编写的工具,手动检索旧记录,将其插入新数据库,并更新旧数据库中的 v2 ID 字段以显示其在新数据库中的相应 ID 位置。

例如,我从 MV5.Posts 中选择并插入到 MV6.Posts。在插入时,我在 MV6.Posts 中检索新行的 ID,并在旧的 MV5.Posts.MV6ID 字段中更新它。

有没有办法通过 INSERT INTO SELECT FROM 进行此更新,这样我就不必手动处理每条记录?我使用的是 SQL Server 2005,开发版。

【问题讨论】:

    标签: c# sql tsql migration bulkinsert


    【解决方案1】:

    迁移的关键是做几件事: 首先,不要在没有当前备份的情况下做任何事情。 其次,如果键将发生变化,您需要至少暂时将新旧都存储在新结构中(如果键字段永久暴露给用户,因为他们可能正在通过它进行搜索以获取旧记录)。

    接下来,您需要彻底了解与子表的关系。如果您更改关键字段,所有相关表也必须更改。这就是存储新旧密钥派上用场的地方。如果您忘记更改其中任何一个,则数据将不再正确且无用。所以这是关键的一步。

    挑选一些特别复杂数据的测试用例,确保为每个相关表包含一个或多个测试用例。将现有值存储在工作表中。

    要开始迁移,您可以使用从旧表中选择插入到新表中。根据记录的数量,您可能希望循环遍历批次(一次不是一条记录)以提高性能。如果新键是一个身份,您只需将旧键的值放在其字段中,然后让数据库创建新键。

    然后对相关表执行相同操作。然后使用表中的旧键值更新外键字段,如下所示:

    Update t2
    set fkfield = newkey
    from table2 t2
    join table1 t1 on t1.oldkey = t2.fkfield
    

    通过运行测试用例并将数据与迁移前存储的数据进行比较来测试您的迁移。彻底测试迁移数据至关重要,否则您无法确定数据是否与旧结构一致。迁移是一个非常复杂的动作;花点时间并有条不紊地彻底地做这件事是值得的。

    【讨论】:

      【解决方案2】:

      可能最简单的方法是在 MV6.Posts 上为 oldId 添加一列,然后将旧表中的所有记录插入新表中。最后,更新与新表中 oldId 匹配的旧表,如下所示:

      UPDATE mv5.posts
      SET newid = n.id
      FROM mv5.posts o, mv6.posts n 
      WHERE o.id = n.oldid
      

      如果您愿意,您可以在之后清理并删除 oldId 列。

      【讨论】:

      • 我实际上认为将 oldId 留在新表上比在旧表上保留一个 newId 更可取,这使这更加容易。
      【解决方案3】:

      有没有办法通过 INSERT INTO SELECT FROM 进行此更新,这样我就不必手动处理每条记录?

      由于您不想手动,而是自动,因此请在MV6.Posts 上创建一个触发器,以便UPDATE当您插入MV6.Posts 时,会自动在MV5.Posts 上发生。

      你的触发器可能看起来像,

      create trigger trg_MV6Posts
      on MV6.Posts
      after insert
      as
      begin
          set identity_insert MV5.Posts on
      
          update  MV5.Posts
          set ID = I.ID
          from    inserted I
      
          set identity_insert MV5.Posts off
      end
      

      【讨论】:

        【解决方案4】:

        呵呵。我记得在迁移中这样做。

        将 old_id 放入新表中使更新更容易——您可以只做一个insert into newtable select ... from oldtable,——以及随后的记录“拼接”更容易。在“缝合”中,您将在插入中更新子表的外键,通过对新父表 (insert into newchild select ... (select id from new_parent where old_id = oldchild.fk) as fk, ... from oldchild) 执行子选择,或者您将插入子表并进行单独更新以修复外键。

        一次插入会更快;在单独的步骤中执行意味着您的插入不依赖于订单,并且可以在必要时重新完成。

        迁移后,您可以删除 old_id 列,或者,如果您遇到遗留系统暴露了 id 并且用户将密钥用作数据的情况,您可以保留它们以允许使用基于old_id。

        确实,如果您正确定义了外键,则可以使用 systables/information-schema 来生成插入语句。

        【讨论】:

          【解决方案5】:

          在 MV6.Post.OldMV5Id 中创建一列

          做一个 插入 MV6.Post 从 MV5.Post 中选择 ..

          然后更新 MV5.Post.MV6ID

          【讨论】:

            【解决方案6】:

            据我所知,您能做的最好的事情就是使用output clause。假设您有 SQL 2005 或 2008。

            USE AdventureWorks;
            GO
            DECLARE @MyTableVar table( ScrapReasonID smallint,
                                       Name varchar(50),
                                       ModifiedDate datetime);
            INSERT Production.ScrapReason
                OUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDate
                    INTO @MyTableVar
            VALUES (N'Operator error', GETDATE());
            

            它仍然需要第二遍来更新原始表;但是,它可能有助于使您的逻辑更简单。您需要更新源表吗?您可以将新 ID 存储在第三个交叉引用表中。

            【讨论】:

              【解决方案7】:

              AFAIK,你不能用一条 sql 语句更新两个不同的表

              但是,您可以使用触发器来实现您想要做的事情。

              【讨论】:

                猜你喜欢
                • 2014-07-17
                • 1970-01-01
                • 1970-01-01
                • 2022-08-02
                • 1970-01-01
                • 2020-07-13
                • 2011-01-29
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多