• 简易的迁移


        这里的迁移指的是从一个数据库迁移到另一个,比如组织库

           一个公司可能有多个系统,各个系统又有各自的组织架构

           比如 HR 系统的用户发生信息(手机号,住址)修改时,其它的库也要跟着发生改变

           如图,要从上面的系统中添加、修改、删除用户信息,还有类似的对部门数据,职位数据进行操作,方法一样:

      

    数据在第一个系统(源表)中新增了用户,或者修改了用户信息,第二个系统也要做对应的业务操作,弯曲是两个系统关联的第三方ID。

    以往迁移会使用步骤:

          1. 通过SQL查询拿到数据。

          2. 应用C#(Java 或者 Ruby、Python脚本)读取SQL结果集并对其作比较,检查源表是做了【插入】、【修改】还是【删除】了用户信息和组织信息。

          3. 比较后对目标表作相应的【插入】、【修改】、【删除】业务操作。

    但这里我们采用:

           1. SQL查询并比较,直接分辨出源表中【插入】、【修改】、【删除】的数据。

           2. 根据上面比较的信息,使用SQL或者API接口把修过的数据更新到目标表。 

    所有代码,这里一次性找出要添加,修改过和要删除的用户

     1 ;WITH SrcModified AS (
     2       SELECT  ID 
     3             , Mobile 
     4             , HousingAddr
     5       FROM      Raw.dbo.Raw_UserInfo   -- 源表
     6         EXCEPT
     7       SELECT  ID = RawID 
     8              , Mobile 
     9              , HousingAddr
    10       FROM      Dest.dbo.Dest_UserInfo -- 目标表
    11      ), modified AS (
    12             SELECT ID FROM  srcModified
    13      ), toAdd AS ( SELECT   ID FROM  modified AS m -- 修改过的用户,并且在目标表中没有,就代表添加的用户
    14                       WHERE NOT EXISTS (
    15                                SELECT * FROM Dest.dbo.Dest_UserInfo AS dest
    16                                     WHERE  m.ID = dest.RawID -- 用第三方ID关联
    17                            ) 
    18      ) ,toModify AS ( SELECT ID
    19                         FROM modified
    20                          EXCEPT
    21                        SELECT  ID
    22                          FROM  toAdd
    23      ) ,toDel AS ( SELECT  d.ID
    24                   FROM     Dest.dbo.dest_UserInfo AS d               
    25                     WHERE NOT EXISTS ( SELECT * FROM Raw.dbo.Raw_UserInfo src
    26                                            WHERE  src.ID = d.RawID --目标表中保存有源表的ID
    27                                       )
    28      ) ,allModified AS(
    29            SELECT  ID ,1 AS opType FROM  toAdd     --所有添加用户
    30                 UNION ALL 
    31            SELECT  ID ,2 AS opType FROM  toModify  --修改过信息的用户
    32                 UNION ALL
    33            SELECT  ID ,3 AS opType FROM  toDel     --从源表中删除的用户
    34        )
    35        SELECT m.opType
    36                ,  m.ID       
    37                ,  u.Mobile
    38                ,  u.HousingAddr 
    39                 FROM allModified as m
    40            left join Raw.dbo.Raw_UserInfo as u on m.ID = u.ID

          优点:

             SQL批处理的即时结果的特性,直接可以看出插入、修改、删除了哪些数据,而不用中间语言(C#JavaPythonRuby)去比较信息,对用户调试,特别是对于几 千上万用户信息的情况尤其有利。

          缺点:

             下面标红的地方。

    在这里其实,所有工作都已经完成了,如果有兴趣,可以往下看。

    =====================================================   结束分隔线   =======================================================

    实现过程: 

       步骤1:找出所有变更过信息(手机号、住址等)的和新增的用户,这一步只要找出用户ID就行了,关于【删除】用户在【步骤4】:    

    ;WITH SrcModified AS (
          SELECT  ID 
                , Mobile
                , HousingAddr
          FROM      Raw.dbo.Raw_UserInfo -- 源表
            EXCEPT
          SELECT  ID = RawID
                 , Mobile
                 , HousingAddr
          FROM      Dest.dbo.Dest_UserInfo -- 目标表
         ), modified AS (
                SELECT ID FROM  srcModified
         )
         SELECT  ID FROM modified

    步骤2:找出新增的用户,这里也只要找出用户ID,黄色区为较前一步新增的逻辑,下同。

     1 ;WITH SrcModified AS (
     2       SELECT  ID 
     3             , Mobile 
     4             , HousingAddr
     5       FROM      Raw.dbo.Raw_UserInfo -- 源表
     6         EXCEPT
     7       SELECT  ID = RawID 
     8              , Mobile 
     9              , HousingAddr
    10       FROM      Dest.dbo.Dest_UserInfo -- 目标表
    11      ), modified AS (
    12             SELECT ID FROM  srcModified
    13      ), toAdd AS ( SELECT   ID FROM  modified AS m                       -- 修改过的用户,并且在目标表中没有,就代表添加的用户
    14                        WHERE NOT EXISTS ( SELECT * FROM Dest.dbo.Dest_UserInfo AS dest
    15                                                WHERE  m.ID = dest.RawID) -- 用第三方ID关联
    16      ) SELECT ID FROM toAdd

    步骤3:找出修改过信息(手机号、住址等),这里同样用到我们的朋友 Except ,计算公式为:【变更的】 - 【添加的】 = 修改的

     1 ;WITH SrcModified AS (
     2       SELECT  ID 
     3             , Mobile 
     4             , HousingAddr
     5       FROM      Raw.dbo.Raw_UserInfo -- 源表
     6         EXCEPT
     7       SELECT  ID = RawID 
     8              , Mobile 
     9              , HousingAddr
    10       FROM      Dest.dbo.Dest_UserInfo -- 目标表
    11      ), modified AS (
    12             SELECT ID FROM  srcModified
    13      ), toAdd AS ( SELECT   ID FROM  modified AS m          -- 修改过的用户,并且在目标表中没有,就代表添加的用户
    14                       WHERE NOT EXISTS (
    15                                SELECT * FROM Dest.dbo.Dest_UserInfo AS dest
    16                                     WHERE  m.ID = dest.RawID -- 用第三方ID关联
    17                            ) 
    18      ) ,toModify AS ( SELECT ID
    19                         FROM modified
    20                          EXCEPT
    21                        SELECT  ID
    22                          FROM  toAdd
    23              )
    24        SELECT * FROM toModify

    步骤4:指出从源表中删除的用户。目标表中保存有源表的ID,方式与上面计算方式不同。

     1 ;WITH SrcModified AS (
     2       SELECT  ID 
     3             , Mobile 
     4             , HousingAddr
     5       FROM      Raw.dbo.Raw_UserInfo -- 源表
     6         EXCEPT
     7       SELECT  ID = RawID 
     8              , Mobile 
     9              , HousingAddr
    10       FROM      Dest.dbo.Dest_UserInfo -- 目标表
    11      ), modified AS (
    12             SELECT ID FROM  srcModified
    13      ), toAdd AS ( SELECT   ID FROM  modified AS m           -- 修改过的用户,并且在目标表中没有,就代表添加的用户
    14                       WHERE NOT EXISTS (
    15                                SELECT * FROM Dest.dbo.Dest_UserInfo AS dest
    16                                     WHERE  m.ID = dest.RawID -- 用第三方ID关联
    17                            ) 
    18      ) ,toModify AS ( SELECT ID
    19                         FROM modified
    20                          EXCEPT
    21                        SELECT  ID
    22                          FROM  toAdd
    23      ) ,toDel AS ( SELECT  d.ID
    24                   FROM     Dest.dbo.dest_UserInfo AS d               
    25                     WHERE NOT EXISTS ( SELECT * FROM Raw.dbo.Raw_UserInfo src
    26                                            WHERE  src.ID = d.RawID --目标表中保存有源表的ID
    27                                       )
    28        ) SELECT * FROM toDel

    步骤5: 汇总查出所有添加,修改,删除的用户信息, 得到的执行结果 opType ( 1: 新增,2:修改,3:删除 )

     1 ;WITH SrcModified AS (
     2       SELECT  ID 
     3             , Mobile 
     4             , HousingAddr
     5       FROM      Raw.dbo.Raw_UserInfo -- 源表
     6         EXCEPT
     7       SELECT  ID = RawID 
     8              , Mobile 
     9              , HousingAddr
    10       FROM      Dest.dbo.Dest_UserInfo -- 目标表
    11      ), modified AS (
    12             SELECT ID FROM  srcModified
    13      ), toAdd AS ( SELECT   ID FROM  modified AS m -- 修改过的用户,并且在目标表中没有,就代表添加的用户
    14                       WHERE NOT EXISTS (
    15                                SELECT * FROM Dest.dbo.Dest_UserInfo AS dest
    16                                     WHERE  m.ID = dest.RawID -- 用第三方ID关联
    17                            ) 
    18      ) ,toModify AS ( SELECT ID
    19                         FROM modified
    20                          EXCEPT
    21                        SELECT  ID
    22                          FROM  toAdd
    23      ) ,toDel AS ( SELECT  d.ID
    24                   FROM     Dest.dbo.dest_UserInfo AS d               
    25                     WHERE NOT EXISTS ( SELECT * FROM Raw.dbo.Raw_UserInfo src
    26                                            WHERE  src.ID = d.RawID --目标表中保存有源表的ID
    27                                       )
    28      ) ,allModified AS(
    29            SELECT  ID ,1 AS opType FROM  toAdd     --所有添加用户
    30                 UNION ALL 
    31            SELECT  ID ,2 AS opType FROM  toModify  --修改过信息的用户
    32                 UNION ALL
    33            SELECT  ID ,3 AS opType FROM  toDel     --从源表中删除的用户
    34        )
    35        SELECT ID, opType FROM allModified

    关键步骤在这里都已经结束了,这一步拿到的数据是用户ID及发生过的操作(添加、修改、删除)信息,下一步就是关联源表,并取出所有信息

     步骤6: 关联源表,取出所有信息,这时候几乎就可以为所欲为了,通过应用(使用SQL或者其它API)这些数据对目标表进行新增,删除。且更新(这里存在唯一的遗憾,因为分不出修改的哪个字段 <手机还是住址>,所以它会修改所有比较过(Except)的字段,不过可以通过控制这些比较的字段,并在最终更新时控制更新的范围,来减少不必要的更新,不过SQL比较擅长批处理,这种活交给C#或其它动态脚本做进一步处理也是可以的。在实际应用当中,经过处理后这种更新的东西每次性更新的数据是非常少的,所以带来的副作用可以忽略)。

        以下 SQL 是最终版,这里是用户信息,其它(部门、职位)信息,使用方式一样:

     1 ;WITH SrcModified AS (
     2       SELECT  ID 
     3             , Mobile 
     4             , HousingAddr
     5       FROM      Raw.dbo.Raw_UserInfo   -- 源表
     6         EXCEPT
     7       SELECT  ID = RawID 
     8              , Mobile 
     9              , HousingAddr
    10       FROM      Dest.dbo.Dest_UserInfo -- 目标表
    11      ), modified AS (
    12             SELECT ID FROM  srcModified
    13      ), toAdd AS ( SELECT   ID FROM  modified AS m -- 修改过的用户,并且在目标表中没有,就代表添加的用户
    14                       WHERE NOT EXISTS (
    15                                SELECT * FROM Dest.dbo.Dest_UserInfo AS dest
    16                                     WHERE  m.ID = dest.RawID -- 用第三方ID关联
    17                            ) 
    18      ) ,toModify AS ( SELECT ID
    19                         FROM modified
    20                          EXCEPT
    21                        SELECT  ID
    22                          FROM  toAdd
    23      ) ,toDel AS ( SELECT  d.ID
    24                   FROM     Dest.dbo.dest_UserInfo AS d               
    25                     WHERE NOT EXISTS ( SELECT * FROM Raw.dbo.Raw_UserInfo src
    26                                            WHERE  src.ID = d.RawID --目标表中保存有源表的ID
    27                                       )
    28      ) ,allModified AS(
    29            SELECT  ID ,1 AS opType FROM  toAdd     --所有添加用户
    30                 UNION ALL 
    31            SELECT  ID ,2 AS opType FROM  toModify  --修改过信息的用户
    32                 UNION ALL
    33            SELECT  ID ,3 AS opType FROM  toDel     --从源表中删除的用户
    34        )
    35        SELECT m.opType
    36                ,  m.ID       
    37                ,  u.Mobile
    38                ,  u.HousingAddr 
    39                 FROM allModified as m
    40            left join Raw.dbo.Raw_UserInfo as u on m.ID = u.ID

    可能遇到的问题:

    1. 在做集合操作(减Except、交Intersect、并Union)时,如果两个数据库的编码不一样,一个是中文的,一个是英文,或者其它就会提示不能比较的错误,这个时候使用在语句      中使用 collate  把各个列统一成一样的,就可以操作了,例如以下两个列的手机号在两个不同编码的库里,做集合操作时可以统一成中文的

    1 select s.Mobile collate Chinese_PRC_CS_AS_KS_WS from src as s
    2    Except
    3 select d.Mobile collate Chinese_PRC_CS_AS_KS_WS from dest as d

     2. 源表和目标表很可能会在不同服务器上,这个时候做集合操作也会非常慢,可以使用 select ... into 把数据放入临时表进行比较。  

     其它小技巧:

    1.  一般我们为客户做迁移的时候,用户信息太多,而能拿到的测试的数据没有代表性,他们可能只会拿出某个部门,或者一小部分的数据给你做测试,但这样是不够的,如果客户只能提供一小部分,可以和客户商量使用 TableSample 查询部分信息,它可以随机抽取任意百分比的源信息,分部相对均匀,这样就可以保证一定的测试覆盖率。

    2. 为了测试,我们不得考虑到各种情况,一个快速且小量而又不污染源表的方法,可以使用 + union all  select 这种方式添加临时数据,这样很方便于测试,例如我想直接添加一个额外的用户,就可以: 

    select UserName from formal          -- 正式的结果
       union all
    select '额外的用户名称' as UserName    -- 临时的测试用户

           总结:  单就查询速度来说,这种方式效率并不高,因为每一个公用表达式都会扫描一次表,但这种方式比用中间语言(非SQL)把数据抽取出来,并使用各种 if ... else... 比较发生过修改过的字段信息要强得多,因为它是即时的批量查询,可以直接看到修改过的信息。

      以上所有代码测试环境为MS SQL Server,Postgres应该是可以运行的,因为MySQL不支持公用表达式,可能要做相应的修改。

  • 相关阅读:
    二叉索引树
    将rst转换为markdown格式
    LeetCode电话号码的字母组合题
    根据文件名列表批量复制文件
    VMware+Alpine代替Docker部署Gogs服务
    wsl安装Ubuntu16.04无法ssh提示Permission denied (publickey)
    json二位数组根据指定键名去重
    thinkphp6 关联模型如何查询已经软删除的数据
    DB2数据库配置步骤(图解)
    基于SpringBoot2.3.1进行AOP原理源码阅读
  • 原文地址:https://www.cnblogs.com/mikedeng/p/ez_migrate.html
Copyright © 2020-2023  润新知