配置源代码管理和管理引用数据

已完成

没有源代码管理的 SQL 数据库项目只是某人计算机上的文件文件夹。 一旦该人员的笔记本电脑出现故障,或者两名开发人员编辑同一个存储过程,你就会回到原点。 将项目放入 Git 可为你提供更改历史记录、通过分支和拉取请求实现协作,以及每个 CI/CD 管道所需的基础。

但是,仅源代码管理并不能解决所有问题。 数据库可能具有与架构并存的查阅表、状态代码和默认配置。 还需要针对该数据制定策略。

为 SQL 数据库项目设置源代码管理

由于 SQL 数据库项目只是 .sql 文件和 .sqlproj 文件,因此它自然适合 Git。 每个数据库对象都位于自己的文件中,因此 Git 跟踪对象级别的更改。 修改 Customers 表并更新存储过程的提交,会在差异中准确显示这两个文件,而非包含几十个不相关更改的庞大迁移脚本。

若要开始,请初始化项目文件夹中的 Git 存储库:

cd MyDatabaseProject
git init
git add .
git commit -m "Initial commit of database project"

组织项目文件夹

一致的文件夹结构使项目能够一目了然。 最常见的模式按对象类型对文件进行分组:

MyDatabaseProject/
├── MyDatabaseProject.sqlproj
├── Tables/
│   ├── Customers.sql
│   └── Orders.sql
├── Views/
│   └── vw_ActiveCustomers.sql
├── StoredProcedures/
│   └── usp_GetCustomerOrders.sql
├── Scripts/
│   ├── PostDeployment/
│   │   └── seed-data.sql
│   └── PreDeployment/
│       └── prep-db.sql
└── PostDeploy.sql

对于 SDK 样式的项目,默认的通配模式会自动获取文件夹中的每个 .sql 文件。 不需要手动文件输入。

添加 .gitignore 文件

通过排除生成输出和特定于用户的设置,使存储库专注于源文件:

bin/
obj/
*.dacpac
*.user

在每个 CI/CD 构建过程中,.dacpac 会从项目中重新创建,因此没有必要在 Git 中对其进行跟踪。

使用预部署和部署后脚本管理引用数据

声明性模型处理架构对象(如表、视图和存储过程),但某些数据与架构本身一样重要。 状态代码、查找表、默认配置、区域列表。 如果该数据消失,应用程序会中断。 它属于项目,与依赖它的对象一起进行版本管理。

部署前和部署后脚本可解决此问题。 它们是在部署期间执行的 SQL 脚本,但位于编译的数据库模型之外:

  • 部署前脚本在部署计划之前运行。 将其用于在架构更改之前必须完成的任务,例如删除约束或迁移数据。
  • 部署后脚本在部署计划完成后运行。 使用它来填充引用数据、种子查找表或设置应用程序默认值。

将脚本添加到项目

项目仅支持一个预部署脚本和一个部署后脚本。 在.sqlproj文件中用PreDeployPostDeploy项条目声明它们。

<ItemGroup>
    <PreDeploy Include="prep-db.sql" />
</ItemGroup>
<ItemGroup>
    <PostDeploy Include="PostDeploy.sql" />
</ItemGroup>

对多个数据文件使用 SQLCMD 包含

一个脚本文件并不意味着一个巨大的文件。 使用 SQLCMD :r 语法从单个入口点拉取多个文件。 典型的 PostDeploy.sql 如下所示:

:r .\Scripts\PostDeployment\seed-statuses.sql
:r .\Scripts\PostDeployment\seed-regions.sql
:r .\Scripts\PostDeployment\seed-app-settings.sql

需要从生成中排除每个引用的文件,否则生成进程会尝试将其编译为架构对象并失败。 在 .sqlproj 文件中,用于 Build Remove 防止编译,并使 None Include 文件在项目中可见:

<ItemGroup>
    <Build Remove="Scripts\PostDeployment\seed-statuses.sql" />
    <None Include="Scripts\PostDeployment\seed-statuses.sql" />
</ItemGroup>

📝 针对预部署或部署后脚本中的每个引用文件重复此模式。

编写幂等引用数据脚本

部署后脚本在每次部署时运行,而不仅仅是第一次部署。 如果使用纯 INSERT 语句,第二次部署会因重复键冲突而失败。 改用 MERGE 语句使脚本可以安全地重复运行:

MERGE INTO [dbo].[OrderStatuses] AS target
USING (VALUES
    (1, N'Pending'),
    (2, N'Processing'),
    (3, N'Shipped'),
    (4, N'Delivered'),
    (5, N'Cancelled')
) AS source ([StatusID], [StatusName])
ON target.[StatusID] = source.[StatusID]
WHEN MATCHED THEN
    UPDATE SET [StatusName] = source.[StatusName]
WHEN NOT MATCHED THEN
    INSERT ([StatusID], [StatusName])
    VALUES (source.[StatusID], source.[StatusName]);

小窍门

项目构建后,你可以通过将 .dacpac 文件扩展名更改为 .zip 并提取它,来验证部署前和部署后脚本。 每个脚本类型的单个 .sql 文件包含所有引用文件中合并的 T-SQL 内容。

关键结论

在解决方案级别初始化 Git 存储库,并使用 .gitignore 文件排除生成输出,例如 bin/obj/.dacpac 文件。 将架构对象组织到镜像数据库结构的文件夹,每个对象有一个文件。 在部署后脚本中放置引用数据,并使用 SQLCMD :r 语句来确保每个脚本专注于单个表。 若要防止将数据脚本编译为架构对象,请在 .sqlproj 文件中使用 Build RemoveNone Include。 编写 MERGE 语句而非纯 INSERT 语句,使引用数据脚本具有幂等性,可在每次部署时安全运行。 接下来,你可以通过分支和拉取请求工作流利用此存储库。