数据库脚本自动执行工具
第一款:Evolve
官网:https://evolve-db.netlify.app/requirements/
仓库:https://github.com/lecaillon/Evolve
提供了三种使用方式:
- 类库:可以引入到现有的项目中
- nuget包:可以直接安装并在命令行中使用
- cli:可以不依赖.net sdk直接运行
细节
- 命名要求:
- 前缀:仅执行一次的脚本以V开头,可重复执行脚本以R开头;
- 版本:计数器,且使用“_”分隔,最后使用分隔描述。如:
- V1_3_1_1__Create_table.sql
- 描述:“__(双下横杠)”跟上脚本的描述
- 校验和:系统根据脚本内容计算,校验和不同但名字相同,再次运行会重新执行
- 执行顺序:目录内以版本号的顺序执行。多个目录按照目录的名称顺序执行。
- 事务:默认每个脚本都是一个事务,可以通过参数改为一次迁移一个事务。
占位符功能
可以通过使用${}
括起一个字符串,然后在程序运行前指定其替换的规则。这些占位符将在脚本实际运行时被替换,以达到动态迁移的效果。
evolve.Placeholders = new Dictionary<string, string>
{
["${database}"] = "my_db",
["${schema1}"] = "my_schema"
}
日志记录
首次执行时,会在连接的数据库中创建changelog表,所有被执行过的脚本都会记录在内。其中:
- type:
- 0:版本迁移
- 1:新架构
- 2:空架构
- 3:启动版本,在程序运行前可以设置数据库的起点版本。
- 4:可重复的脚本类型
DbUp
官网:https://dbup.readthedocs.io/en/latest/
仓库:https://github.com/DbUp/DbUp
提供了nuget包、powershell两种方式,不过shell版本没有怎么了解,下面着重说下nuget包版本的。
细节
- 命名方式:没有限制,代码和脚本保持一致,做好约定即可。
- 执行顺序:完全按照名称排序执行
- 事务:支持无事务、单脚本事务、迁移事务
代码脚本
DbUp除了支持传统的sql脚本执行,还支持我们使用C#来书写脚本。在撰写一些较为复杂的脚本或者对于sql语句不熟悉的开发者来说可以有效提高出脚本的效率。
public class Script0005ComplexUpdate : IScript
{
public string ProvideScript(Func<IDbCommand> commandFactory)
{
var cmd = commandFactory();
cmd.CommandText = "Select * from SomeTable";
var scriptBuilder = new StringBuilder();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
scriptBuilder.AppendLine(string.Format("insert into AnotherTable values ({0})", reader.GetString(0)));
}
}
return scriptBuilder.ToString();
}
}
代码解释:查询了SomeTable,并根据结果的第一列值向AnotherTable插入数据。
脚本提供方式
- 嵌入式
- 文件系统
- 静态
- 嵌入式+代码
嵌入式
在项目中新建文件夹,并在csproj中设置其及其所有子文件为Embedded
,那么在程序打包后将一同编译到exe中,无法做后续修改。
builder.WithScriptsEmbeddedInAssemblies(new[]
{
Assembly.GetExecutingAssembly(),
typeof(Something).Assembly
},
(string s) => s.StartsWith("Script"))
文件系统
给出系统路径,每次运行时根据路径读取脚本。程序会读取指定路径下所有的sql文件并执行。
builder.WithScriptsFromFileSystem(path)
对于这种方式,还可以添加自定义的选项:
var options = new FileSystemScriptOptions
{
// true = scan into subdirectories, false = top directory only
IncludeSubDirectories = true,
// Patterns to search the file system for. Set to "*.sql" by default.
Extensions = new [] { "*.sql" },
// Type of text encoding to use when reading the files. Defaults to "Encoding.UTF8".
Encoding = Encoding.UTF8,
// Pass each file path located to this function and filter based on the result
Filter = path => path.Contains("value")
}
builder.WithScriptsFromFileSystem(path, options);
静态
直接在代码中书写脚本并命名,适用于那些固定每次迁移都要执行的脚本。
// Single script
builder.WithScript("name.sql", "content");
// Many scripts
builder.WithScripts(new[]
{
new SqlScript("script1.sql", "content"),
new SqlScript("script2.sql", "content2")
});
// Custom script provider
builder.WithScripts(new MyCustomScriptProvider());
嵌入式+代码
就是先前的代码脚本也可以和sql脚本放一块执行。
脚本类型
Dbup没有通过事先约定的命名来区分脚本类型,而是提供了两个枚举,是我们可以自由定义哪些脚本应该被重复执行。
- RunOnce - 默认的,仅执行一次
- RunAlways - 总是执行
var upgradeEngineBuilder = DeployChanges.To
.SqlDatabase(connectionString, null) //null or "" for default schema for user
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), script => script.StartsWith("SampleApplication.Scripts."), new SqlScriptOptions { ScriptType = ScriptType.RunOnce })
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), script => script.StartsWith("SampleApplication.RunAlways."), new SqlScriptOptions { ScriptType = ScriptType.RunAlways })
.LogToConsole();
预处理器
类似于中间件,在脚本执行之前我们可以对其再做一些更改。比如变量替换什么的,不过变量替换已经有实现了可以直接使用。
实现IScriptPreprocessor
接口:
DeployChanges
.To
.SqlDatabase(..)
.WithPreprocessor(new MyPreprocessor());
日志
可以输出日志到控制台或是对接nlog、Serilog到文件,也可以两种同时使用。
执行记录
初次运行会创建SchemaVersions表,记录已执行的脚本。
变量替换
HTML报告
分组运行顺序
差异点比较
- Evolve有校验和,在内容发生改变时会重新执行;DbUp则没有
- Evolve的命名方式固定,灵活性不如DbUp
- Evolve只能执行sql,DbUp还支持代码脚本
在灵活性和丰富度上,DbUp要略胜一筹。且DbUp虽然沉寂了很久但最近又重新开始维护了,而Evolve很久都没有更新,所以个人还是比较推荐DbUp。后面待项目组开始使用后再持续更新本文。