数据库脚本自动执行工具

南山有榛 / 2024-11-11 / 原文

第一款:Evolve

官网:https://evolve-db.netlify.app/requirements/
仓库:https://github.com/lecaillon/Evolve
提供了三种使用方式:

  1. 类库:可以引入到现有的项目中
  2. nuget包:可以直接安装并在命令行中使用
  3. 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包版本的。

细节

  1. 命名方式:没有限制,代码和脚本保持一致,做好约定即可。
  2. 执行顺序:完全按照名称排序执行
  3. 事务:支持无事务、单脚本事务、迁移事务

代码脚本

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插入数据。

脚本提供方式

  1. 嵌入式
  2. 文件系统
  3. 静态
  4. 嵌入式+代码

嵌入式

在项目中新建文件夹,并在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表,记录已执行的脚本。
image

变量替换

HTML报告

分组运行顺序

差异点比较

  • Evolve有校验和,在内容发生改变时会重新执行;DbUp则没有
  • Evolve的命名方式固定,灵活性不如DbUp
  • Evolve只能执行sql,DbUp还支持代码脚本
    在灵活性和丰富度上,DbUp要略胜一筹。且DbUp虽然沉寂了很久但最近又重新开始维护了,而Evolve很久都没有更新,所以个人还是比较推荐DbUp。后面待项目组开始使用后再持续更新本文。