演练:比较数据库的架构和数据库项目的架构

本主题适用于:

Visual Studio 旗舰版

Visual Studio 高级专业版

Visual Studio 专业版 

Visual Studio 学习版

主题适用 主题适用 主题不适用 主题不适用

在本演练中,通过使用 Visual Studio 将数据库项目的架构与数据库的架构进行比较。根据您的团队使用数据库项目和数据库的方式,您可能需要按一个方向或按其他方向复制架构更改。 团队在数据库开发的整个生命周期内工作的过程中,您可能会遇到以下典型方案:

  • **项目是源,数据库是目标。**可以使用数据库项目来开发或维护数据库。 在项目的架构中进行更改后,可以将这些更改复制到位于临时服务器上的数据库中。 随后,团队可以将该数据库部署到成品服务器上。

    在本演练中执行的比较操作从架构差异生成数据定义语言 (DDL) 脚本。 然后可以使用此脚本将所有或部分数据库项目应用于数据库。 有关更多信息,请参见将更改从项目传播到数据库。

  • **数据库是源,项目是目标。**可能会在成品数据库的架构中发现错误,或者架构可能过时并需要更新。 此发现可能需要您的团队将紧急更新应用到数据库。 若要使项目与数据库保持同步,您可以将更新导入到数据库项目中。 有关更多信息,请参见如何:将更新从数据库导入数据库项目

本演练阐释了以下任务:

安装阶段

  1. 创建一个数据库项目。 新项目最初为空。

  2. 从脚本中导入数据库架构。

  3. 配置和生成数据库项目并将其部署到数据库服务器。 数据库和项目具有相同的架构。

生产阶段

  1. 向数据库项目中添加表。 添加一个名为 InternationalShippers 表,其中包含三列。

  2. 比较这两种架构。 在进行比较时,可以将数据库项目指定为源,将数据库指定为目标。 因此,结果是将 InternationalShippers 表作为一个新表显示在数据库项目中。

  3. 将更改从项目传播到数据库。 可以将新的 InternationalShippers 表从数据库项目传播到部署的数据库。 如果按照此过程操作,则将覆盖示例数据库的部分内容。

    提示

    也可以将更改从数据库传播到数据库项目。 有关更多信息,请参见如何:将更新从数据库导入数据库项目

系统必备

若要完成本演练,您需要下面的应用程序和权限:

  • SQL Server 2008

  • 可在目标数据库服务器上创建和更新数据库的权限

  • Visual Studio 高级专业版或 Visual Studio 旗舰版

安装阶段

创建数据库项目

创建数据库项目

  1. 在**“文件”菜单上指向“新建”,再单击“项目”**。

    将打开**“新建项目”**对话框。

  2. 在**“已安装的模板”下,展开“数据库”节点,然后单击“SQL Server”**。

  3. 在模板列表中单击**“SQL Server 2008 数据库项目”**。

  4. 在**“名称”中键入“CompareProject”,然后单击“确定”**。

    将打开空的 CompareProject 项目,并将其显示在**“解决方案资源管理器”**中。

从脚本中导入数据库架构

创建可从中导入架构的脚本

  1. 在**“文件”菜单上指向“新建”,然后单击“文件”**。

    此时将打开**“新建文件”**对话框。

  2. 在**“类别”列表中,如果尚未突出显示“常规”**,请单击它。

  3. 在**“模板”列表中,单击“Sql 文件”,然后单击“打开”**。

    Transact-SQL 编辑器打开。

  4. 复制下面的代码并将其粘贴到 Transact-SQL 编辑器中。

    PRINT N'Creating Sales...';
    GO
    CREATE SCHEMA [Sales]
        AUTHORIZATION [dbo];
    GO
    PRINT N'Creating Sales.Customer...';
    GO
    CREATE TABLE [Sales].[Customer] (
        [CustomerID]   INT IDENTITY (1, 1) NOT NULL,
        [CustomerName] NVARCHAR (40) NOT NULL,
        [YTDOrders] INT NOT NULL,
        [YTDSales] INT NOT NULL
    );
    GO
    PRINT N'Creating Sales.Orders...';
    GO
    CREATE TABLE [Sales].[Orders] (
        [CustomerID] INT NOT NULL,
        [OrderID] INT IDENTITY (1, 1) NOT NULL,
        [OrderDate] DATETIME NOT NULL,
        [FilledDate] DATETIME NULL,
        [Status] CHAR (1) NOT NULL,
        [Amount] INT NOT NULL
    );
    GO
    PRINT N'Creating Sales.Def_Customer_YTDOrders...';
    GO
    ALTER TABLE [Sales].[Customer]
        ADD CONSTRAINT [Def_Customer_YTDOrders] DEFAULT 0 FOR [YTDOrders];
    GO
    PRINT N'Creating Sales.Def_Customer_YTDSales...';
    GO
    ALTER TABLE [Sales].[Customer]
        ADD CONSTRAINT [Def_Customer_YTDSales] DEFAULT 0 FOR [YTDSales];
    GO
    PRINT N'Creating Sales.Def_Orders_OrderDate...';
    GO
    ALTER TABLE [Sales].[Orders]
        ADD CONSTRAINT [Def_Orders_OrderDate] DEFAULT GetDate() FOR [OrderDate];
    GO
    PRINT N'Creating Sales.Def_Orders_Status...';
    GO
    ALTER TABLE [Sales].[Orders]
        ADD CONSTRAINT [Def_Orders_Status] DEFAULT 'O' FOR [Status];
    GO
    PRINT N'Creating Sales.PK_Customer_CustID...';
    GO
    ALTER TABLE [Sales].[Customer]
        ADD CONSTRAINT [PK_Customer_CustID] PRIMARY KEY CLUSTERED ([CustomerID] ASC) WITH (ALLOW_PAGE_LOCKS = ON, ALLOW_ROW_LOCKS = ON, PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF, STATISTICS_NORECOMPUTE = OFF);
    GO
    PRINT N'Creating Sales.PK_Orders_OrderID...';
    GO
    ALTER TABLE [Sales].[Orders]
        ADD CONSTRAINT [PK_Orders_OrderID] PRIMARY KEY CLUSTERED ([OrderID] ASC) WITH (ALLOW_PAGE_LOCKS = ON, ALLOW_ROW_LOCKS = ON, PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF, STATISTICS_NORECOMPUTE = OFF);
    GO
    PRINT N'Creating Sales.FK_Orders_Customer_CustID...';
    GO
    ALTER TABLE [Sales].[Orders]
        ADD CONSTRAINT [FK_Orders_Customer_CustID] FOREIGN KEY ([CustomerID]) REFERENCES [Sales].[Customer] ([CustomerID]) ON DELETE NO ACTION ON UPDATE NO ACTION;
    GO
    PRINT N'Creating Sales.CK_Orders_FilledDate...';
    GO
    ALTER TABLE [Sales].[Orders]
        ADD CONSTRAINT [CK_Orders_FilledDate] CHECK ((FilledDate >= OrderDate) AND (FilledDate < '01/01/2010'));
    GO
    PRINT N'Creating Sales.CK_Orders_OrderDate...';
    GO
    ALTER TABLE [Sales].[Orders]
        ADD CONSTRAINT [CK_Orders_OrderDate] CHECK ((OrderDate > '01/01/2005') and (OrderDate < '01/01/2020'));
    GO
    PRINT N'Creating Sales.uspCancelOrder...';
    GO
    CREATE PROCEDURE [Sales].[uspCancelOrder]
    @OrderID INT
    AS
    BEGIN
    DECLARE @Delta INT, @CustomerID INT
    BEGIN TRANSACTION
        SELECT @Delta = [Amount], @CustomerID = [CustomerID]
         FROM [Sales].[Orders] WHERE [OrderID] = @OrderID;
    
    UPDATE [Sales].[Orders]
       SET [Status] = 'X'
    WHERE [OrderID] = @OrderID;
    
    UPDATE [Sales].[Customer]
       SET
       YTDOrders = YTDOrders - @Delta
        WHERE [CustomerID] = @CustomerID
    COMMIT TRANSACTION
    END
    GO
    PRINT N'Creating Sales.uspFillOrder...';
    GO
    CREATE PROCEDURE [Sales].[uspFillOrder]
    @OrderID INT, @FilledDate DATETIME
    AS
    BEGIN
    DECLARE @Delta INT, @CustomerID INT
    BEGIN TRANSACTION
        SELECT @Delta = [Amount], @CustomerID = [CustomerID]
         FROM [Sales].[Orders] WHERE [OrderID] = @OrderID;
    
    UPDATE [Sales].[Orders]
       SET [Status] = 'F',
           [FilledDate] = @FilledDate
    WHERE [OrderID] = @OrderID;
    
    UPDATE [Sales].[Customer]
       SET
       YTDSales = YTDSales - @Delta
        WHERE [CustomerID] = @CustomerID
    COMMIT TRANSACTION
    END
    GO
    PRINT N'Creating Sales.uspNewCustomer...';
    GO
    CREATE PROCEDURE [Sales].[uspNewCustomer]
    @CustomerName NVARCHAR (40)
    AS
    BEGIN
    INSERT INTO [Sales].[Customer] (CustomerName) VALUES (@CustomerName);
    SELECT SCOPE_IDENTITY()
    END
    GO
    PRINT N'Creating Sales.uspPlaceNewOrder...';
    GO
    CREATE PROCEDURE [Sales].[uspPlaceNewOrder]
    @CustomerID INT, @Amount INT, @OrderDate DATETIME, @Status CHAR (1)='O'
    AS
    BEGIN
    DECLARE @RC INT
    BEGIN TRANSACTION
    INSERT INTO [Sales].[Orders] (CustomerID, OrderDate, FilledDate, Status, Amount) 
         VALUES (@CustomerID, @OrderDate, NULL, @Status, @Amount)
    SELECT @RC = SCOPE_IDENTITY();
    UPDATE [Sales].[Customer]
       SET
       YTDOrders = YTDOrders + @Amount
        WHERE [CustomerID] = @CustomerID
    COMMIT TRANSACTION
    RETURN @RC
    END
    GO
    
  5. 在**“文件”菜单上,单击“将 SqlQuery_1.sql 另存为”**。

    **“另存文件为”**对话框打开。

  6. 在**“对象名”**中,键入 SampleImportScript.sql。

    可以将文件保存到计算机中的任何位置。 记下该位置,因为在下面的步骤中必须使用它。

  7. 单击**“保存”**。

  8. 在**“文件”菜单上,单击“关闭解决方案”**。

    接下来,创建一个数据库项目,并从已创建的脚本导入架构。

从脚本中导入数据库

  1. 在**“项目”菜单上,单击“导入数据库脚本”**。

  2. 阅读“欢迎”页之后,单击**“下一步”**。

  3. 单击**“浏览”**,浏览至保存 SampleImportScript.sql 文件的位置。

  4. 双击 SampleImportScript.sql 文件,然后单击**“完成”**。

    将导入脚本,在该脚本中定义的对象添加到数据库项目中。

  5. 查看摘要,然后单击**“完成”**。

配置、生成和部署数据库项目

这些过程将创建一个在独立开发环境(或沙盒)中具有导入的架构的数据库,您可以在该环境中开发和测试数据库。

配置和生成数据库项目

  1. 在**“解决方案资源管理器”**中,单击 CompareProject 项目。

  2. 在**“项目”菜单上,单击“CompareProject 属性”**。

    此时将显示 CompareProject 项目的属性。

  3. 单击**“部署”**选项卡。

  4. 在**“部署操作”列表中,单击“创建部署脚本(.sql)并部署到数据库”**。

  5. 在**“目标数据库设置”中,单击“编辑”**。

  6. 在**“连接属性”对话框中,设置要使用的数据库的连接属性,然后单击“确定”**。

    **“目标连接”**框将显示正确的连接字符串。

    警告

    应在测试服务器、开发服务器或本地计算机上创建数据库。 不应使用生产服务器。

  7. 在**“目标数据库名称”**框中键入“CompareProjectDB”。

  8. 在**“文件”菜单上,单击“全部保存”**。

  9. 在**“生成”菜单上,单击“生成解决方案”**。

    即会基于刚刚设置的项目属性生成部署脚本。 生成状态显示在**“输出”窗口中,并且最后一行显示为“生成: 成功或最新 1 个”**。

部署数据库项目

  1. 在**“解决方案资源管理器”**中,单击 CompareProject 项目。

  2. 在**“生成”菜单上,单击“部署 CompareProject”**。

    或者,也可以在**“解决方案资源管理器”中右击项目,然后单击“部署”**。

    警告

    应对测试服务器、开发服务器或本地计算机运行此部署。 不应使用生产服务器。

    数据库项目部署到新数据库。 部署状态显示在**“输出”窗口中,并且最后一行显示为“部署已成功”**。

生产阶段

向数据库项目中添加表

向项目中添加表

  1. 在**“视图”菜单上,单击“数据库架构视图”**。

    **“架构视图”**将打开并显示 CompareProject 项目的架构。

  2. 在**“架构视图”**中,展开“CompareProject”节点,再展开“架构”节点。

  3. 右击**“Sales”,指向“添加”,然后单击“表”**。

    **“添加新项 - CompareProject”**对话框将打开。

  4. 在**“模板”下,单击“表”**。

  5. 在**“名称”中键入“InternationalShippers”,然后单击“添加”**。

    一个名为 InternationalShippers 的表将会添加到 CompareProject 项目中。 相应的表定义将显示在 Transact-SQL 编辑器中。

  6. 将 InternationalShippers.table.sql 中的 SQL 脚本更改为以下语句:

    CREATE TABLE [Sales].[InternationalShippers]
    (
    [ShipperID] [int] NOT NULL IDENTITY(1,1),
    [CompanyName] [nvarchar] (40) NOT NULL,
    [Region] [nvarchar] (40) NOT NULL,
    [Phone] [nvarchar] (24) NULL
    ) ON [PRIMARY]
    
  7. 单击**“保存 InternationalShippers.table.sql”**。

  8. 在**“架构视图”中右击 InternationalShippers 表,然后单击“在解决方案资源管理器中查看文件”**。

    InternationalShippers.sql 文件将在**“解决方案资源管理器”**中突出显示。

  9. 按 F4。

    “属性”窗口将出现并显示 InternationalShippers.table.sql 文件的属性。 Build Action 属性设置为“生成”,用于指示该文件包含数据库对象的定义,应对该文件进行分析和验证。

比较这两种架构

比较这两种架构

  1. 在**“数据”菜单上,指向“架构比较”,然后单击“新建架构比较”**。

  2. 将打开**“新建架构比较”**对话框。

  3. 在**“目标架构”中单击“数据库”,并指定与本演练前面部署的数据库的连接,然后单击“确定”**。

    该数据库的架构将与已更改项目的架构进行比较,结果显示在**“架构比较”**窗口中。 在 CompareProject 列中将显示 [Sales].[InternationalShippers] 表。 表的状态为“新建”,其更新操作为“创建”。 如果您现在传播更改,则将在目标数据库中创建该表。 有关更多信息,请参见以下过程。

检查并忽略预期的差异

  1. 在比较结果的列表中,滚动到**“SQL 文件”**节点。

    对于目标数据库,列表中将显示状态为**“缺少”**的两行:一行代表文件组文件,一行代表日志文件。

  2. 单击每行的**“更新操作”列以将操作更改为“跳过”**。

    当更新数据库架构时,您通常不希望更改与目标数据库关联的文件组文件或日志文件。 通过将操作更改为“跳过”,目标数据库将继续使用其当前文件。

    接下来,您可以选择更新目标数据库以匹配源数据库。

将更改从项目传播到数据库

将更改传播到目标数据库

  • 在**“架构比较”窗口中,单击“写入更新”**。

    提示

    如果项目包含一个或多个错误,则可能会禁用“写入更新”按钮。 如果发生这种情况,则“架构比较”状态栏中将显示一条消息。

    将执行**“架构比较”**窗口中列出的操作,包括针对 InternationalShippers 表的“创建”操作。 此同步可以更改数据库的架构,使其与数据库项目的架构匹配。 在更新操作完成后,将重新比较架构,并刷新结果。

    在更新目标架构的过程中,通过打开**“数据”菜单,指向“架构比较”并单击“停止写入目标”**,可以取消更新操作。

    警告

    由于某些架构更改无法在一个事务范围内执行,因此如果取消更新操作,可能会丢失数据。 例如,目标数据库中的表在准备重新创建过程中可能会被删除。 如果此时取消更新,则可能会丢失该表。 如果发生此类数据丢失,您应重新比较架构。

请参见

任务

如何:比较数据库架构

如何:比较并同步两个数据库的数据

概念

比较和同步数据库架构

将一个或多个表中的数据与引用数据库中的数据进行比较和同步