翻译|使用教程|编辑:杨鹏连|2020-10-27 11:53:52.983|阅读 225 次
概述:Phil Factor描述了自定义部署前和部署后脚本的工作方式,使用SQL Compare或SQL Change Automation进行基于状态的数据库部署时,以及如何使用它们(例如,将版本号添加到目标数据库)指定其数据库设置,或将数据填充到某些表中。
# 慧都年终大促·界面/图表报表/文档/IDE等千款热门软控件火热促销中 >>
SQL Compare是一款比较和同步SQL Server数据库结构的工具。现有超过150,000的数据库管理员、开发人员和测试人员在使用它。当测试本地数据库,暂存或激活远程服务器的数据库时,SQL Compare将分配数据库的过程自动化。
当使用所谓的“基于状态”的数据库部署方法时,您需要设计一些其他的自定义脚本来解决一些棘手的部署问题。例如,如果您使用SQL Compare或SQL Change Automation(SCA)来将包含对象级脚本的脚本文件夹与保存数据的目标数据库同步,则可能需要一些自定义脚本来控制当前和当前数据库之间的数据移动。
在执行之前,SQL Compare和SCA都将这些脚本放置在其生成的同步脚本的开头或结尾。
自定义部署脚本有哪些优点?
通常,当您需要引入代码来处理从一个数据库版本到另一个版本的尴尬更改时,将使用自定义部署脚本。例如,如果要更改已存储有数据的数据库版本中的表,则偶尔需要从SQL Compare中“夺取”来确保旧数据全部移至改进版本中的正确位置。设计。表重命名或表拆分经常会发生这种情况。有时,包含非原子数据的单个列(例如,以逗号分隔的列表,XML或JSON)将需要分配到标准化列中。
您可能还会遇到依赖关系,更改数据库属性,添加计划的任务,添加数据库版本号等问题。在其他时候,您可能还需要更改数据库级别的设置,管理角色成员身份,检查是否存在正确的静态数据,或者创建或更改SQL Server代理作业。所有这些事项都可以通过自定义部署脚本处理
从脚本目录部署
SQL Compare比较两个数据库,即源数据库和目标数据库,并自动生成一个同步脚本,该脚本将修改目标架构,使其与源架构匹配。当您使用脚本目录作为源时,SQL Compare允许您将额外的SQL脚本附加到它生成的同步脚本的开头或结尾。
如果源是从版本控制系统获取的脚本目录,则SQL Compare允许部署前和部署后脚本,但每个脚本只能有一个。UI和命令行的SQL Compare允许使用13.4.7版本的版本,或SQL Change Automation从3.0.4版本的版本。
这些脚本必须包含在名为Custom Scripts的目录中,该目录位于Post-Deployment或Pre-Deployment的子目录中。
每个脚本必须遵循某些简单的约定。该脚本不会因错误而中止执行,因此它必须捕获每个错误并报告它,并且如果是“致命”错误,还必须将其设置NOEXEC为ON。如果在部署后脚本之前发生错误,NOEXEC将为ON。因此,它将不会执行。切勿NOEXEC OFF在部署后脚本中使用该命令,因为同步脚本会使用该NOEXEC ON开关在发生任何错误后中止处理。如果您在发生错误后无意间重新启用了执行,则会发生坏事
脚本的每个部分PRINT在开始时都应该有一条语句来描述其功能,这样,如果出现错误,则很明显是造成回滚的原因。
每个脚本都必须是可重新运行且幂等的。它必须能够在所有预期的目标环境中运行,并且必须支持整个数据库环境中数据库排序规则的任何可能差异。
部署前和部署后脚本如何工作
这些自定义脚本的内容不是构建的一部分,因为它们直到比较完成后才执行,因此它们对SQL Compare自动生成的同步脚本没有影响。在生成同步脚本之后但在执行之前,使用预部署脚本来更改目标数据库。执行同步脚本后,将使用部署后脚本来更改目标。
例如,如果您在预部署子目录中放置了一个包含创建表的自定义脚本,则该表将不包含在比较中。SQL Compare运行比较,生成同步脚本,在目标上执行部署前脚本,创建新表,然后执行同步脚本。另一方面,如果将相同的脚本放在“自定义脚本”目录的“部署后”子目录中,则SQL Compare将在运行其同步脚本后创建新表。
SQL Compare中没有其他方法可以添加迁移逻辑。如果需要在完成比较之前更改目标,则必须在运行SQL Compare之前在单独的脚本中进行操作。
例如,如果您需要从目标数据库中的表中复制数据(将对其进行更改)并将其保存到临时表中,则部署前脚本会很有用。然后可以在同步脚本中更改表,最后,可以在部署后脚本中将数据重新插入到新表中。您可能还需要使用部署后脚本来确保表中存在某些引用或静态数据。
SQL Compare在比较中不考虑服务器范围的对象,因此,如果需要在源和目标之间同步代理作业,则可以在部署前或部署后脚本中进行同步。其他任务(例如,检查数据库设置是否正确)必须使用部署前脚本,因为它们可以轻松更改后续脚本的执行方式。例如,如果排序规则不区分大小写,而数据库区分大小写,则同步将无法进行。
部署后脚本可用于应用创建数据库版本的特定变体所需的更改。例如,如果您根据立法区域拥有不同的工资单数据库变体,则可以根据所需的立法区域进行切换。
您可能需要在主干中保留数据库同一版本的多个变体,使用条件开关来生成正确的变体(例如,任何会计程序包可能每个税区都有变体)。尽管可以有条件地运行一个CREATE或多个ALTER脚本,但这会使源代码控制变得过于复杂,并使从脚本目录进行的同步成为雷区。
我建议,最佳做法是在版本中包含所有代码,并使用功能切换或功能开关(例如表中的值或扩展属性)来实现正确的逻辑。仅可以通过简单的逻辑(例如,检查目标数据库的名称并相应地进行切换)在部署后脚本中设置此属性。保留在功能或扩展属性中的“软”数据库开关或切换开关允许使用同一部署测试所有变体。
使用部署前和部署后脚本时,需要考虑一些限制。
不支持使用SQLCMD语法和变量,除非您在SQL Compare之外,使用SQLCMD或在SQLCMD模式下使用SSMS分别执行同步脚本。
如果您选择修改现有对象作为这些自定义脚本的一部分,则需要确保SQL Compare引擎不理会它们。您将需要在主脚本目录中而不是“自定义脚本”目录中具有新版本的对象源代码,因此您将需要告诉SQL Compare也不要使用SQL Compare过滤器或/Exclude开关来创建或更改它们。以防止在比较中包含这些对象。
如果SQL Compare在源和目标之间进行比较并发现它们相同,则它将不会运行后脚本和前脚本,因为将没有附加它们的同步脚本。
除非对这些脚本进行了彻底的测试,否则只有在执行部署脚本后才会发现错误,然后它们才会破坏构建,从而导致问题。在使用之前,请花一些时间对其进行彻底测试。
源代码控制和自定义脚本
部署后和部署前脚本几乎应始终保留在源代码管理中。无论它们创建或更改的对象如何,状态都必须处于源代码管理中。通常,对于表拆分之类的更改,您只需添加一个SQL比较过滤器或/exclude切换为告诉SQL Compare在比较中不包括受影响的对象,因为它是在随附的部署后脚本中完成的。这意味着源控件可以具有用于更改对象的SQL DDL代码,而它们的存在不会干扰复杂的数据拆分。如前所述,在允许同步脚本进行除对象以外的其余所有更改之前,完全有可能使用预部署脚本将要更改的表中的现有数据从目标表中保存到目标数据库中。您指定要排除在外;然后匹配的部署后脚本可以读取临时表并将数据放置在正确的位置。
两种类型的自定义脚本(部署前和部署后)都应特定于版本。但是,由于它们是幂等的,因此即使它们意外重新运行,它们也通常不会伤害任何东西。脚本文件夹将仅反映一个版本,因此应易于为该版本提供正确的自定义脚本。
不一定要保证数据库部署的目标。如果您不维护实时数据库的版本号,那么您将需要确保自定义脚本将与“狂野的”所有可能的目标版本一起使用。
例子
这些不应该按原样运行;例如,您需要填写数据库的名称和版本号,或者根据需要调整信息。数据库设置示例可能会对运行它的不幸数据库产生微妙而广泛的影响。它们也不代表如何执行这些任务的一般建议,因为您的情况可能会有所不同。例如,“存有数据的表”示例将很好,直到您有成千上万的行,此时您将有一个更好的生活BULK INSERT。这些示例仅是为了说明,以向您展示可能性
在表中存储数据
想象一下,您需要构建一个过时的PUBS数据库版本,包括所有数据。该脚本必须位于“部署后”目录中。这是一个确保dbo.publishers表仅包含原始数据的示例。
PRINT 'Ensuring that the original PUBS test data is there' BEGIN TRY MERGE INTO dbo.publishers AS target USING (VALUES ( '0736', 'New Moon Books', 'Boston', 'MA', 'USA' ), ( '0877', 'Binnet & Hardley', 'Washington', 'DC', 'USA' ), ( '1389', 'Algodata Infosystems', 'Berkeley', 'CA', 'USA' ), ( '1622', 'Five Lakes Publishing', 'Chicago', 'IL', 'USA' ), ( '1756', 'Ramona Publishers', 'Dallas', 'TX', 'USA' ), ( '9901', 'GGG&G', 'Mnchen', NULL, 'Germany' ), ( '9952', 'Scootney Books', 'New York', 'NY', 'USA' ), ( '9999', 'Lucerne Publishing', 'Paris', NULL, 'France' ) )source(pub_id,pub_name,city,[state],country) ON target.pub_id = source.pub_id WHEN NOT MATCHED BY TARGET THEN INSERT ( pub_id,pub_name,city,[state],country ) VALUES ( pub_id,pub_name,city,[state],country ) WHEN NOT MATCHED BY SOURCE THEN DELETE; END try BEGIN CATCH DECLARE @msg nvarchar(max)=Error_Message(), @severity int = ERROR_SEVERITY(), @State int = ERROR_State() RAISERROR(@msg, @severity, @state); SET NOEXEC ON END CATCH数据库设置
这些必须在部署前脚本中执行。为了使数据库按预期运行,需要某些数据库属性设置。通常最好在部署时检查这些设置,因为它们有时会产生细微的错误。排序规则,恢复模型和兼容性级别是显而易见的,但是还需要检查其他几项,例如自动更新统计信息和自动创建统计信息。当然,您需要确定数据库的正确设置。这些只是DBA可能建议的设置示例。
/* To run this Requires having ALTER permission on the target database. */ PRINT 'Ensuring that the settings are right for this particular database. DECLARE @AutoCreateStatistics INT,@AutoUpdateStatistics INT, @ReadCommittedSnapshot INT,@AutoUpdateStatisticsAsynchronously INT, @recovery_model_desc nvarchar(120),@compatibility_level int SELECT @AutoCreateStatistics=is_auto_create_stats_on, @AutoUpdateStatistics=is_auto_update_stats_on, @AutoUpdateStatisticsAsynchronously=is_auto_update_stats_async_on, @ReadCommittedSnapshot=is_read_committed_snapshot_on, @recovery_model_desc=recovery_model_desc, @compatibility_level=[compatibility_level] FROM sys.databases WHERE name='pubs' IF @AutoCreateStatistics=0 ALTER DATABASE current SET AUTO_CREATE_STATISTICS ON DECLARE @AnyErrors INT =@@error IF @AutoUpdateStatistics=0 ALTER DATABASE current SET AUTO_UPDATE_STATISTICS ON SELECT @AnyErrors=@AnyErrors+@@Error IF @AutoUpdateStatisticsAsynchronously=1 ALTER DATABASE current SET AUTO_UPDATE_STATISTICS_ASYNC OFF SELECT @AnyErrors=@AnyErrors+@@Error if @ReadCommittedSnapshot=0 ALTER DATABASE current SET READ_COMMITTED_SNAPSHOT ON WITH rollback immediate SELECT @AnyErrors=@AnyErrors+@@Error if @recovery_model_desc<>'SIMPLE' ALTER DATABASE CURRENT SET RECOVERY SIMPLE SELECT @AnyErrors=@AnyErrors+@@Error /* normally you'd want it at your current product version but you might, as in this case, need something different check product version with SELECT SERVERPROPERTY('ProductVersion'); */ IF @compatibility_level<>100 --Warning this is specially for old PUBS! ALTER DATABASE PUBS --a bad idea anywhere else SET COMPATIBILITY_LEVEL = 100 SELECT @AnyErrors=@AnyErrors+@@Error --See //docs.microsoft.com/en-us/sql/t-sql/statements/alter-database-transact-sql?view=sql-server-ver15 before doing this IF NOT EXISTS ( SELECT 1 FROM sys.databases WHERE name= Db_Name() AND collation_name='SQL_Latin1_General_CP1_CI_AI') ALTER DATABASE CURRENT COLLATE SQL_Latin1_General_CP1_CI_AI SELECT @AnyErrors=@AnyErrors+@@Error IF @AnyErrors>0 SET NOEXEC ON添加版本信息
这可以作为部署前脚本或部署后脚本来完成。此示例脚本仅将版本号和说明作为JSON字符串插入。本文直接将数据与SQL Server数据库对象相关联,其中包含该代码的扩展版本,该版本可以跟踪版本以及何时应用版本。
PRINT N'Adding a version number' GO DECLARE @DatabaseInfo NVARCHAR(3750) SELECT @DatabaseInfo = ( SELECT Db_Name() AS "Name", '2.4.01 (Change This to your version)' AS "Version", 'You will need to edit this string' AS "Description", GetDate() AS "Modified", SUser_Name() AS "by" FOR JSON PATH ); BEGIN TRY IF not EXISTS (SELECT name, value FROM fn_listextendedproperty( N'Database_Info',default, default, default, default, default, default) ) EXEC sys.sp_addextendedproperty @name=N'Database_Info', @value=@DatabaseInfo ELSE EXEC sys.sp_Updateextendedproperty @name=N'Database_Info', @value=@DatabaseInfo END TRY BEGIN CATCH DECLARE @msg nvarchar(max)=Error_Message(), @severity int = ERROR_SEVERITY(), @State int = ERROR_State() RAISERROR(@msg, @severity, @state); SET NOEXEC ON END CATCH结论
相关产品推荐:
SQL Prompt:SQL语法提示工具
SQL Toolbelt:Red Gate产品套包
SQL Monitor:SQL Server监控工具
本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@cahobeh.cn
文章转载自: