彩票走势图

SQL Compare使用教程:数据库开发阶段(四)

翻译|使用教程|编辑:杨鹏连|2020-07-15 09:35:27.863|阅读 266 次

概述:本文介绍了所有这些任务,并演示了使用SQL Compare可以实现的功能。

# 慧都年终大促·界面/图表报表/文档/IDE等千款热门软控件火热促销中 >>

SQL Compare是一款比较和同步SQL Server数据库结构的工具。现有超过150,000的数据库管理员、开发人员和测试人员在使用它。当测试本地数据库,暂存或激活远程服务器的数据库时,SQL Compare将分配数据库的过程自动化。

点击下载SQL Compare试用版

第三版

这次,我们决定更改出版物,我们不仅仅只涉及一个主题,还允许应用多个子主题。我们这样做是为了说明迁移步骤,该步骤将需要在迁移脚本中添加一些其他迁移代码。

使用我们刚刚保存的v2.1.7构建脚本,我们type从titles表中删除该列并创建两个新表。其中一个是称为的标签列表,TagName另一个是称为的标签列表,TagTitle用于将一个或多个标签与标题相关联,但是每个标题只有一个主标签。同样,我使用构建脚本来执行此操作,因为更改此表并创建另外两个表会产生影响。

CREATE TABLE [dbo].[titles](
    [title_id] [dbo].[tid] NOT NULL,
    [title] [nvarchar](120) NOT NULL,
    [pub_id] [char](10) NULL,
    [price] [money] NULL,
    [advance] [money] NULL,
    [royalty] [int] NULL,
    [ytd_sales] [int] NULL,
    [notes] [nvarchar](max) NULL,
    [pubdate] [datetime] NOT NULL,
 CONSTRAINT [UPKCL_titleidind] PRIMARY KEY CLUSTERED 
(
    [title_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
CREATE TABLE TagName (TagName_ID INT IDENTITY(1, 1) PRIMARY KEY, Tag VARCHAR(20) NOT NULL UNIQUE);
go
CREATE TABLE TagTitle
  (
  TagTitle_ID INT IDENTITY(1, 1),
  title_id dbo.tid NOT NULL REFERENCES titles (title_id),
  Is_Primary BIT NOT NULL DEFAULT 0,
  TagName_ID INT NOT NULL REFERENCES TagName (TagName_ID),
  CONSTRAINT PK_TagNameTitle PRIMARY KEY CLUSTERED (title_id ASC, TagName_ID) ON [PRIMARY]
  );

当您为新版本(2.1.8)运行完整的构建脚本时,您会看到构建错误,因为有几个依赖的报告存储过程reptq2和reptq3,它们使用type需要更改的旧列。

消息207,级别16,状态1,过程reptq2,第4行[批处理开始行459]
无效的列名“类型”。
消息207,级别16,状态1,过程reptq3,第10行[批处理开始行475]
无效的列名“类型”。

不过,您将拥有重构的表,因此可以为它们设计新的代码而不会出现太多问题(我不会在这里显示它,但是您很快就会在迁移脚本中看到它)。您还必须修复DEFAULT旧type列的约束,因此它引用新Tag列。

当然,要测试这个新版本,我们现在需要用当前版本(2.1.7)中的数据填充它,但是这次我们需要制定数据迁移脚本以将数据移到旧type列中到新表中,并填充其他新列。

解决了数据迁移的所有问题并运行了所有测试后,我们将使用v2.1.8标记新的开发版本,并使用带有该版本的SQL Compare作为源代码和源代码的内容目录作为目标,以便更新对象脚本并保存“ 2.1.7 to 2.1.8”迁移脚本,这时会发出警告。

SQL Compare使用教程:数据库开发阶段(四)

下一步是编辑迁移脚本。这比我们以前的简单版本陷阱要多。

SQL Compare使用教程:数据库开发阶段(四)

幸运的是,我们了解迁移问题,因为我们必须填充v2.1.8构建来测试那些存储过程。

我们需要打开刚刚保存的迁移脚本并对其进行编辑。我们创建一个临时表,它是title的一个版本。我们使用它来将数据添加到两个新表中。方便地,更改后的存储过程reptq2可以用作方便的单元测试(有关完成的脚本,请参见migration_2-1-7_to_2-1-8.sql)。如果这两个过程给出的结果与以前的版本相同,那么我们很可能会早点回家。如果要继续学习,则需要使用SQL Compare生成脚本,然后在脚本中添加“插入代码”注释标记的部分。

/*
Run this script on :
Script created by SQL Compare version 13.4.5.6953 from Red Gate Software Ltd at 12/05/2020 09:35:47
*/
SET NUMERIC_ROUNDABORT OFF
GO
SET ANSI_PADDING, ANSI_WARNINGS, CONCAT_NULL_YIELDS_NULL, ARITHABORT, QUOTED_IDENTIFIER, ANSI_NULLS ON
GO
SET XACT_ABORT ON
GO
SET TRANSACTION ISOLATION LEVEL Serializable
GO
BEGIN TRANSACTION
GO
IF @@ERROR <> 0 SET NOEXEC ON
GO
--inserted code
Declare @version varchar(25);
SELECT @version= Coalesce(Json_Value(
  ( SELECT Convert(NVARCHAR(3760), value) 
      FROM sys.extended_properties AS EP
      WHERE major_id = 0 AND minor_id = 0 
        AND name = 'Database_Info'),'$[0].Version'),'that was not recorded');
IF @version <> '2.1.7'
  BEGIN
  RAISERROR ('The Target was at version %s, not the correct version (2.1.7)',16,1,@version)
  SET NOEXEC ON
  END
go
PRINT N'Saving TITLES table to temporary table'
SELECT titles.title_id, titles.title, titles.type, titles.pub_id, titles.price,
  titles.advance, titles.royalty, titles.ytd_sales, titles.notes,
  titles.pubdate
  INTO #titles
  FROM [dbo].[titles];
IF @@ERROR <> 0 SET NOEXEC ON
GO
--end of inserted code
PRINT N'Dropping constraints from [dbo].[titles]'
GO
ALTER TABLE [dbo].[titles] DROP CONSTRAINT [DF__titles__type__07F6335A]
GO
IF @@ERROR <> 0 SET NOEXEC ON
GO
PRINT N'Altering [dbo].[titles]'
GO
IF @@ERROR <> 0 SET NOEXEC ON
GO
ALTER TABLE [dbo].[titles] DROP
COLUMN [type]
GO
IF @@ERROR <> 0 SET NOEXEC ON
GO
PRINT N'Creating [dbo].[TagName]'
GO
CREATE TABLE [dbo].[TagName]
(
[TagName_ID] [int] NOT NULL IDENTITY(1, 1),
[Tag] [varchar] (20) COLLATE Latin1_General_CI_AS NOT NULL
)
GO
IF @@ERROR <> 0 SET NOEXEC ON
GO
PRINT N'Creating primary key [PK__TagName__3109E9F88C8DE0AD] on [dbo].[TagName]'
GO
ALTER TABLE [dbo].[TagName] ADD PRIMARY KEY CLUSTERED  ([TagName_ID])
GO
IF @@ERROR <> 0 SET NOEXEC ON
GO
PRINT N'Adding constraints to [dbo].[TagName]'
GO
ALTER TABLE [dbo].[TagName] ADD UNIQUE NONCLUSTERED  ([Tag])
GO
IF @@ERROR <> 0 SET NOEXEC ON
GO
PRINT N'Creating [dbo].[TagTitle]'
GO
CREATE TABLE [dbo].[TagTitle]
(
[TagTitle_ID] [int] NOT NULL IDENTITY(1, 1),
[title_id] [dbo].[tid] NOT NULL,
[Is_Primary] [bit] NOT NULL DEFAULT ((0)),
[TagName_ID] [int] NOT NULL
)
GO
IF @@ERROR <> 0 SET NOEXEC ON
GO
PRINT N'Creating primary key [PK_TagNameTitle] on [dbo].[TagTitle]'
GO
ALTER TABLE [dbo].[TagTitle] ADD CONSTRAINT [PK_TagNameTitle] PRIMARY KEY CLUSTERED  ([title_id], [TagName_ID])
GO
IF @@ERROR <> 0 SET NOEXEC ON
GO
PRINT N'Altering [dbo].[reptq2]'
GO
ALTER PROCEDURE [dbo].[reptq2] AS
select 
    case when grouping(TN.tag) = 1 then 'ALL' else TN.tag end as type, 
    case when grouping(pub_id) = 1 then 'ALL' else pub_id end as pub_id, 
    avg(ytd_sales) as avg_ytd_sales
 FROM titles INNER JOIN tagtitle
ON TagTitle.title_id = titles.title_id
INNER JOIN dbo.TagName AS TN 
ON TN.TagName_ID = TagTitle.TagName_ID
where pub_id is NOT NULL AND is_primary=1
group by pub_id, TN.tag with rollup
GO
IF @@ERROR <> 0 SET NOEXEC ON
GO
PRINT N'Altering [dbo].[reptq3]'
GO
ALTER PROCEDURE [dbo].[reptq3] @lolimit money, @hilimit money,
@type char(12)
AS
select 
    case when grouping(pub_id) = 1 then 'ALL' else pub_id end as pub_id, 
    case when grouping(TN.tag) = 1 then 'ALL' else TN.tag end as type, 
    count(titles.title_id) as cnt
from titles INNER JOIN tagtitle
ON TagTitle.title_id = titles.title_id
INNER JOIN dbo.TagName AS TN 
ON TN.TagName_ID = TagTitle.TagName_ID
where price >@lolimit AND is_primary=1 AND price <@hilimit AND TN.tag = @type OR TN.tag LIKE '%cook%'
group by pub_id, TN.tag with rollup
GO
IF @@ERROR <> 0 SET NOEXEC ON
GO
PRINT N'Adding foreign keys to [dbo].[TagTitle]'
GO
ALTER TABLE [dbo].[TagTitle] ADD FOREIGN KEY ([TagName_ID]) REFERENCES [dbo].[TagName] ([TagName_ID])
GO
ALTER TABLE [dbo].[TagTitle] ADD FOREIGN KEY ([title_id]) REFERENCES [dbo].[titles] ([title_id])
GO
IF @@ERROR <> 0 SET NOEXEC ON
GO
PRINT N'Altering extended properties'
GO
BEGIN TRY
    EXEC sp_updateextendedproperty N'Database_Info', N'[{"Name":"Pubs","Version":"2.1.8","Description":"The Pubs (publishing) Database supports a fictitious bookshop.","Modified":"2020-05-06T13:57:56.217","by":"PhilFactor"}]', NULL, NULL, NULL, NULL, NULL, NULL
END TRY
BEGIN CATCH
    DECLARE @msg nvarchar(max);
    DECLARE @severity int;
    DECLARE @state int;
    SELECT @msg = ERROR_MESSAGE(), @severity = ERROR_SEVERITY(), @state = ERROR_STATE();
    RAISERROR(@msg, @severity, @state);
    SET NOEXEC ON
END CATCH
GO
--inserted code
INSERT INTO TagName (Tag) SELECT DISTINCT type FROM #titles;
IF @@ERROR <> 0 SET NOEXEC ON
INSERT INTO TagTitle (title_id,Is_Primary,TagName_ID)
  SELECT title_id, 1, TagName_ID FROM #titles 
    INNER JOIN TagName ON #titles.type = TagName.Tag;
IF @@ERROR <> 0 SET NOEXEC ON
DROP  TABLE #titles
go
--end of inserted code
COMMIT TRANSACTION
GO
IF @@ERROR <> 0 SET NOEXEC ON
GO
DECLARE @Success AS BIT
SET @Success = 1
SET NOEXEC OFF
IF (@Success = 1) PRINT 'The database update succeeded'
ELSE BEGIN
    IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION
    PRINT 'The database update failed'
END
GO

为了测试此迁移脚本是否有效,我们可能需要反复将dev版本还原到2.1.7,并用当前版本中的数据填充它,然后重复我们的单元测试。您可以通过在事务中进行操作并回滚来避免使用这种简单的迁移进行重复生成,但这会使调试迁移脚本更加困难。重复执行直到迁移脚本可靠运行为止。

一旦完成,我们就可以在当前版本上运行它,并且可以很好地更新Pubs数据库,并且所有数据都应该在新表结构中保留。它拒绝执行两次,这是正确的

SQL Compare使用教程:数据库开发阶段(四)

它还拒绝与其他数据库一起使用,这很令人欣慰。
将所有版本脚本放在一起

我们已经经历了三个版本。第一个是最小的更改,只是为了演示如何进行版本检查。第二个将所有这些varchar数据类型更改为更正确的NVARCHAR数据类型。最后一个使书籍分类系统更加有用,并更新了存储过程以使用新系统。最后一个需要数据迁移。现在,我们可以进行全部测试。我们想知道是否可以迁移从Internet下载的原始版本的数据库,并将其升级到与源目录中的原始版本相同的版本。

由于原始备份已从互联网上消失了很长时间,因此我在Github存储库的备份目录中添加了一个副本。现在,我们以正确的顺序将四个脚本应用于该脚本...

  1. AddInitialVersion.sql –仅将版本添加到原始Pubs
  2. migration_2-1-5_to_2-1-6.sql –从2.1.5迁移到2.1.6:仅更新PubInfo表的数据类型。
  3. migration_2-1-6_to_2-1-7.sql –从2.1.6迁移到2.1.7:将所有varchar类型的数据类型更新为NVarchar。
  4. migration_2-1-7_to_2-1-8.sql –从2.1.7迁移到2.1.8:拆分“标题”表以允许分配多种图书类型。演示数据迁移。
希望一切都很好,这已经说明了如何使用两种替代方法来获得数据库的版本。可以通过在空数据库上运行构建脚本,然后添加数据来直接完成此操作,也可以从现有版本中通过一系列迁移脚本来完成此操作,并从SQL Compare自动生成的脚本中进行了一些修改。请记住,实际工作是使用无数据脚本完成的,而无需考虑迁移的后果。这项工作全部由SQL Compare完成。

出现的一个问题是没有显式名称声明的约束的问题。这对于临时表和表变量来说很好,但是对于基表来说不是一个好习惯,因为它会使任何比较工作变得更加困难。当您使用一系列现有的迁移脚本来创建具有完整现有数据的版本时,它也会产生连锁反应。如果您是从使用懒惰的约束定义的构建脚本创建Pubs的初始副本的,那么这些迁移脚本在应用于数据库时有时会失败,因为这些约束是在元数据中内部指定的,例如'PK__TagName__3109E9F88C8DE0AD'。该随机数将不会重复。这就是为什么我提供Pubs作为备份而不是发布的构建脚本的原因。

结论

在数据库开发过程中,优秀的开发人员应自由使用最佳工具来完成当前任务。您可能需要使用导出新表设计的ER图表工具。在项目的某个时刻,您可能想要使用传统的构建脚本,表构建器工具,甚至是文本编辑器以及SSMS。如果您可以生成对象级脚本和迁移脚本的可交付成果,那么可以。

为了可靠地部署数据库更改,我们需要在版本控制中同时使用每个版本的对象级源和在版本之间移动的迁移脚本。当某个版本成为发行候选版本时,将创建迁移脚本,该脚本将使用先前发行版中的数据库。SQL Compare可以提供此脚本的“第一手资料”,只要SQL Compare无法满足所有要求,便可以与自定义迁移代码结合使用。当然,您可以按顺序运行各个版本到版本的脚本,但是其中可能要管理的脚本太多,并且您可能会因无法在集成测试中幸存下来的设计思想而来来往往。每个版本一个迁移脚本更易于管理。

最后,自动化很重要。由于在单元测试,集成测试,自动化构建和“办公室工作”(检查日志,团队协作,文档,问题管理和报告)的速度和数量方面的期望越来越高,因此自动化成为实现更加愉快和富有成效的工作生活。在即将发表的文章中,我将展示开发阶段的各种任务,如本文所述,将其插入自动化的SQL Change Automation过程中。

相关产品推荐:

SQL Prompt:SQL语法提示工具

SQL Toolbelt:Red Gate产品套包

SQL Monitor:SQL Server监控工具


想要购买SQL Compare正版授权,或了解更多产品信息请点击


标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@pclwef.cn

文章转载自:

为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP