代码之家  ›  专栏  ›  技术社区  ›  Henk Holterman

将多个数据库迁移并合并到一个数据库中

  •  -2
  • Henk Holterman  · 技术社区  · 16 年前

    SELECT OrderId, OrderDate FROM Sales.dbo.Orders
    

    SELECT OrderId, OrderDate FROM Sales.Orders
    

    6 回复  |  直到 16 年前
        1
  •  6
  •   Brent Ozar    16 年前

    假设:

    • 您希望所有对象最终都出现在数据库A中的SQL 2005中(我们称之为目标)
    • 您希望最终摆脱数据库B和C(旧的源代码)
    • 您没有一个全面的测试环境,可以每天自动恢复生产数据库,并一次又一次地编写脚本,直到正确为止。(这是最好的方法,我也采取了这种方法,但这是劳动密集型的。)

    以下是我学到的深刻教训:

    不要在同一天进行合并和SQL 2005更改。

    即使它们没有在你的实时制作应用程序中被查询,也要立即构建它们。这样,您就可以在其中填充虚假测试数据,以便提前测试您的应用程序。是的,这意味着将实时数据和测试数据混合在一起,但坦率地说,你已经在没有网络的情况下工作了。不过,要小心标识字段,因为您最终可能会在目标和源数据库中遇到具有相同标识号但数据不同的冲突记录。

    提前在Target中创建视图。 您提到您已经有了可以进行跨数据库查询的视图。现在将这些从Source复制到Target,并告诉任何其他开发人员(报告人员、高级用户)开始引用Target视图。这不会加快你自己的工作,但会加快他们的工作。如果你能验证他们只击中了目标(即使目标视图仍然指向源代码中的表),那么这将使迁移当天的故障排除更容易。然后,您可以提前开始拒绝对源代码视图的权限。

    提前同步表。 列出所有需要从源中移出的表,并分析每个表的更新方式。如果它只被插入(不更新或删除),如日志表,那么编写一个T-SQL脚本以开始在Target中保持同步。在服务器上的低活动时段(如每晚)通过SQL代理作业运行该脚本。这样,当上线当天,你就不必推送那么多记录,这意味着你的上线窗口会更小,你的目标交易日志也会更小。不断更新或删除的表并不容易,是否同步也取决于您。我们为任何超过一百万行的桌子做了这件事。

    检查源数据库之间的记录冲突。 听起来这个并不特别适用于你,但我在这里注意到了这一点,以防其他人进行合并,他们正在阅读它以获取提示。如果您有多个源数据库,请转储对象列表。如果有两个同名对象,请检查它们的模式。我曾处理过这样的实例,其中每个数据库中都有一个State或Region表,它们应该是相同的,但它们的主键有标识字段。每个子表(如链接到Region表的Customers)都通过主键(标识字段)引用父表(Region),而主键在数据库之间不匹配。在这种情况下,明智的做法是在迁移日之前提前一个停机窗口,用手动更新脚本清理这些记录。

    1. 修改Region表,添加一个与它将成为什么相匹配的NewID字段和一个显示它曾经是什么的OldID字段

    将迁移分解成碎片。 列出所有数据库中存储的每个进程。如果其中任何一个可以在不移动数据的情况下移动,请先这样做。例如,如果你有Source.dbo.usp_RunReport,并且它只引用目标数据库中的表,那么在第一阶段就这样做。如果你有只在应用程序内部使用的小型系统查找表,客户或报告看不到,那么也把它放在第一阶段。听起来它太小了,不值得费心,但这个想法是为了减少移民日的恐慌。你想得越少,就越能排除故障。我们提前移动了每个静态查找表(州、地区、日历等)。第一阶段所需的工作量——只是移动那些小的静态表——让管理层了解移动其余部分的工作量有多大,这为我们争取了资源和时间。

    为Target预生长数据文件。

    在迁移日,一次运行一个表或更小的表。

    更新完成后,更改源数据库的安全性。 将每个非SA登录名放入源数据库中的dbdenydatareader和dbdenydata writer角色中。这样,如果他们在连接字符串中硬编码了数据库名称,他们仍然可以登录,但他们将无法做任何事情。这也使您的故障排除更容易:如果应用程序或查询遇到问题,您可以考虑将他们的登录从拒绝角色中删除,看看它是否有效——如果有效,它就完蛋了。这样做的风险是,他们可能会运行一个使用源数据库数据更新目标数据库的事务(从源获取客户,在目标中更新他们),这可能会导致问题。

    源数据库的其他选项包括:

    • 删除所有登录名,并使用新登录名访问现有数据库,以防万一。然后,如果某人的只读报告完全被破坏,你可以通过向他们发出新的登录名并告诉他们它引用了错误的数据库来让它暂时工作。

    更新完成后,重建索引和;Target的统计数据。 如果你只是在做连续插入,这没什么大不了的,但如果你要合并多个数据库(比如两个已分为不同地区的销售数据库),那么你需要清理一下。

    依我之见,除非你能证明从多个模式中获益是合理的,否则使用一个模式。 最后一个只是我的两分钱,但听起来你从3个数据库(每个数据库1个模式)到1个有3个模式的数据库,要做很多工作。如果你对3模式的事情不太确定,你可以考虑使用1模式,否则你以后会陷入另一场混乱的返工。如果你有特定的安全需求,3种模式确实有意义,但除此之外,只要确保你得到了你想要的回报。现在正是采用一种模式的好时机。

        2
  •  2
  •   Sam    16 年前

    您可以尝试使用Redgate SQL Compare和Data Compare。它们具有模式映射功能,可以让您将dbo模式映射到另一个中的销售模式,然后移动表和过程。这将使您不必弄乱SQL导出向导。不过,你仍然需要重构其他对象。

    我喜欢这两个工具。


    编辑:


    编辑: 此外,他们还提供SQL Refactor,它可以进行“智能”重命名。分数!

        3
  •  1
  •   Kristen    16 年前

    你能有一个名为SALES的虚拟数据库,它有一个叫做[Orders]的视图吗:

    CREATE VIEW Sales.dbo.Orders
    AS
    SELECT OrderId, OrderDate, ...
    FROM CombinedDatabase.Sales.Orders
    

    然后

    选择。..来自Sales.dbo。订单

    不过,如果不进行进一步的修改,您将无法插入/更新该表。

    如果你能有这样的VIEW日志,说明它们被使用了,这将使你能够修复调用它们的代码!!但我想不出办法做到这一点;但是,您可以依次禁用每个,运行一些测试,修复损坏的部分,然后继续下一个。..并因此通过重构消除它们,但在此过程中有一个基本可用的应用程序。

    我使用SED来处理这类事情,但我们所有的表和所有的列都有唯一的名称,我们在应用程序中使用与数据库列名匹配的变量名——所以我很有信心在我们的应用程序中将xxx_yyy_ID更改为aaa_bbb_ID会很好地工作,而且不会产生意外的副作用。

        4
  •  1
  •   Tim Cooper    13 年前

    好吧,我对你的问题的基本理解是这样的:

    1. 他们有不同的桌子和;程序名称(Manu或Inventory中不存在Sales中的表/程序名称)
    2. 您希望将所有三个数据库中的所有表/过程都放在一个数据库中(即SaleManInv)
    3. 每个数据库中的某些存储过程显式引用其他数据库中的表(即Sales.dbo.lolookupItem()显式引用Inventory.dbo。项目表)

    导出和导入表似乎不会有问题,我会为procs做些什么:

    1. 将一个proc从SQL Server 2000数据库导出到SQL Server 2005数据库,以确定是否需要删除交叉引用的“.dbo.”部分。
    2. 使用带有“在文件中搜索和替换”的文本编辑器(我使用PSPAD),将所有“Sales.dbo.”替换为“SaleManInv.dbo.“,然后将所有“Iventory.dbo。

        5
  •  1
  •   Andrew Barber Eric Lafortune    12 年前

    存储过程的诀窍是按照顺序或系统约束将其编写出来,以确保内部引用其他存储过程/函数的存储过程或函数是最后创建的。

    如果我觉得有一个工具可以以自动化的方式处理这项任务,我会立即购买它。

        6
  •  0
  •   Flinkman    16 年前

    那么你可以回答这个问题。然后在一两年内,如果这些问题没有出现。您可以删除该列。合并数据会删除数据的来源。