☰
SqlConverter实战:从SQL Server到SQLite的数据库迁移指南
2026/10/12 14:54:46 网站建设 项目流程

简介:这一工具包用于将 SQL Server 数据库的结构和数据完整转换为 SQLite,包含可编译的 C# 源码,适合需要跨数据库迁移、本地轻量化存储部署或深入理解两种数据库架构差异的开发者与运维人员。资源共 87 个文件,压缩包约 9.66MB,主要包含 .cs 源码文件、.exe 可执行程序、依赖 DLL 库、XML 配置与项目工程文件等,目录结构清晰,既可直接运行也可在 Visual Studio 中重新编译修改。工具源自以色列开发者 Liron Levi 在 CodeProject 上发布的开源作品,由整理者重新打包、翻译界面并验证编译通过,省去了自行寻找与调试的麻烦。源码覆盖数据库 Schema 转换的完整逻辑,包括表结构、索引、触发器、视图、外键等对象的生成,以及主界面、选择表和失败提示等交互模块,能帮助使用者掌握从 SQL Server 读取元数据到创建 SQLite 文件和写入数据的整个流程,也便于按自身需求定制转换规则。目前已有 884 人学习,对 C# 开发者和有数据迁移需求的中小型项目团队具有不错的参考价值。

1. SqlConverter到底是什么:一条命令背后的数据迁移思路

做过数据交付的人都知道,最磨人的不是写SQL,而是把一个库的表结构、数据、约束原样搬到另一个库。SqlConverter这个项目解决的就是这个具体问题:把SQL Server里的数据库,转成SQLite的数据库文件,而且源码开放、可以自己编译成可执行文件。它不是在线服务,也不是闭源的转换小工具,而是一个你拿到手之后可以改、可以调、可以重新构建的本地工具,适合在离线环境或内网环境里完成数据库迁移。

它的价值点在于:SQL Server和SQLite虽然都是关系型数据库,但类型系统、SQL方言、事务机制差别很大,手工迁移大库几乎不可能。有了这样一个可编译的转换器,你就能把迁移过程变成一条命令,反复执行也不会出错。适合谁用?数据工程师、做桌面软件内置数据库的人、需要定期把业务库导成只读分析库的团队,还有那些被要求“把现有系统换成SQLite”却又不敢动线上库的开发者。这篇文章会从原理、编译、参数、坑点一路讲到怎么把它变成日常流程里稳定的一环。

2. 为什么需要SqlConverter:SQL Server到SQLite的迁移痛点与手工方案的局限

2.1 迁移的典型场景:从报表库到边缘节点

先看一个常见的业务场景。某公司的核心业务跑在SQL Server上,但到了月底要做数据分析,分析环境在另一台机器上,数据库是SQLite。每次同步数据,过去都是用导出脚本再导入,方式笨重且容易出错。还有一类场景是PC端软件,宿主机的数据库必须是单文件、免安装的SQLite,但后台管理系统用的SQL Server,需要定期把配置数据推给客户端,这就必须做一次稳定的转换。

另一类需求来自开发期的原型验证。在项目早期,团队想用SQLite做本地缓存,但数据的源头是SQL Server,总不能只导出一部分数据来测试。把整库的结构和业务数据搬过去,才能保证本地开发环境和实际运行环境的行为一致。这个时候,一个能自动完成Schema转换和数据搬运的工具,比任何手工导出手法都更可靠。

这类迁移看起来简单,真正动手才会发现问题:字段类型对不上、SQL语句语法不同、自增列行为不一致、日期格式容易出乱码。SqlConverter这类工具的价值在于把这些规则固化到代码里,而不是每次靠人肉改脚本。

2.2 手工迁移的三个硬伤:类型映射、语法差异、增量维护

手工迁移SQL Server到SQLite,通常的做法是:用SSMS把表结构导出成CREATE TABLE语句,再手动改成SQLite语法;数据也导成INSERT语句,然后批量执行。听起来可行,但大一点的库就撑不住了。

第一个硬伤是类型映射。SQL Server有int、bigint、nvarchar、datetime、decimal、uniqueidentifier等类型,SQLite是动态类型,只有INTEGER、REAL、TEXT、BLOB四种存储类别。手工改的时候,经常会把nvarchar直接映射成TEXT,但长度约束、默认值、排序规则都丢了。更麻烦的是uniqueidentifier,在SQL Server里是一个16字节的二进制结构,在SQLite里如果映射成TEXT,每次查询都得转格式。

第二个硬伤是SQL方言的差异。T-SQL里的GETDATE()、ISNULL()、CAST(AS INT)、TOP N写法,在SQLite里分别是datetime(‘now’)、ifnull()、CAST(AS INTEGER)、LIMIT N。如果表里有计算列、默认值带函数、索引带WHERE条件,手工翻译很容易漏。更复杂的是存储过程和视图,SQLite对视图支持还行,但存储过程几乎没有直接对应的机制。

第三个硬伤是增量维护。第一次迁移做完了,后面业务库表结构变了,加了一列改了一个约束,手工流程就得从头再来一遍。你又得对比新旧表结构,重新改脚本,重新导数据。这个过程中只要有一步漏了,产生的SQLite库就是错的,查起来比迁移本身还耗时。

2.3 为什么选择“有源码可编译”的路线而非现成GUI工具

市面上有不少数据库转换工具,图形界面操作很方便,但作为一线工程师,我遇到的实际限制是:GUI工具多数跑在Windows桌面上,而迁移任务经常要放在内网服务器上定时执行;另一些在线转换服务根本不允许把生产数据传上去。

还有一个更麻烦的点:GUI工具转换出来的SQLite库,结果不透明。你给它的是一套隐含规则,但它具体怎么映射类型、怎么处理约束,界面里只显示一个进度条。一旦转换结果有问题,你连排查入口都找不到。有源码可编译的项目就不一样了:你可以看到类型映射表在代码里是怎么定义的,可以加日志,可以改批量提交大小,甚至可以改成支持命令行参数,纳入自己的自动化流程。

我一般会优先选择这种路线,还有一个现实原因:数据库结构不是静态的。每次迁移都可能有新表、新字段、新约束。有源码的话,遇到一次特殊场景就改一次转换逻辑,这个工具会随着你的业务库一起演化,而不是永远停留在某个固定版本。

3. 源码编译与最小可用配置:半个小时跑通第一条迁移

3.1 从源码到可执行文件:依赖与编译要点

SqlConverter既然标注了“有源码可编译”,首先要解决的就是怎么把它从源码变成能运行的exe或命令行程序。这类项目通常是用.NET或C#写的,因为SQL Server连接驱动和SQLite的ADO.NET驱动生态都比较成熟。常见的依赖是System.Data.SqlClient和Microsoft.Data.Sqlite,前者负责连接SQL Server,后者负责写SQLite库。

编译的步骤通常是这样的:先装好.NET SDK,然后还原NuGet包,再执行dotnet build命令。需要注意呼出方的运行环境是Windows还是Linux,SQLite的驱动在不同操作系统上的表现基本上是一致的,但SQL Server连接的认证方式有差异。

编译这个环节最常见的错误是NuGet源不可达,尤其是内网环境。解决方法是先把依赖包下载下来,放到本地离线源里,再指定源路径进行还原。

# 安装 .NET SDK 后,进入源码目录 cd SqlConverter # 还原依赖包,注意内网环境要指定本地源 dotnet restore --source ./packages # 构建 Release 版本,输出到指定目录 dotnet build -c Release -o ./bin

逻辑说明:第一条命令会读取csproj文件里的包引用,从NuGet源下载依赖。如果你在内网,需要把离线包拷到本地目录,并用–source参数指过去。第二条命令是正式构建,-c Release表示发布优化过的版本,-o表示输出路径。

参数说明:如果源码里还用了Entity Framework之类的框架,还原时拉取的包会更多;如果编译报错提示缺少某个运行时版本,说明SDK和运行时不匹配,需要确认本地安装的.NET版本和csproj里的TargetFramework一致。

编译之后,目录下会出现SqlConverter.exe(或无扩展名的可执行文件)以及一堆依赖DLL。不要只拷exe文件,必须连同DLL一起拷到目标机器上,否则运行时会报“找不到程序集”。这个坑我见过多次,都是因为只复制了单文件。

3.2 连接串与命令行参数:第一次执行的最小配置

编译好之后的运行方式,决定了这个工具能不能嵌入到你的工作流里。SqlConverter这一类工具通常支持命令行参数传入源库连接串、目标SQLite文件路径以及一些可选的配置项。

常见的形式是:源库连接串放在一个JSON配置文件里,或者直接通过命令行传入。命令行传参的方式更适合自动化,但要注意连接串是英文分号分隔的,如果含有特殊字符,整个参数需要用引号包起来。

# 执行转换,把SQL Server的MyDB库转换为MyDB.db SqlConverter \ --source "Server=127.0.0.1;Database=MyDB;User Id=sa;Password=xxx;TrustServerCertificate=true" \ --target "./output/MyDB.db" \ --log "./logs/convert.log"

逻辑说明:–source指的是SQL Server连接串,–target是生成的SQLite文件路径,–log是日志文件路径。第一次执行时建议先把日志路径加上,因为出问题时日志是唯一能看清转换到哪一步的线索。

参数说明:如果SQL Server实例启用了加密认证,TrustServerCertificate=true必须带上,否则连接会报证书校验失败。如果你的SQL Server在另一台机器上,连接串里的Server要写IP加端口,比如127.0.0.1,1433。

第一次跑完后不要急着用。先用命令行工具打开生成的SQLite文件,检查表的数量、每个表的数据量是否和源库一致。这类工具如果转换结果不提供校验功能,这一步就是你唯一的安全网。

# 用 sqlite3 命令行检查转换结果 sqlite3 ./output/MyDB.db ".tables" sqlite3 ./output/MyDB.db "SELECT COUNT(*) FROM dbo.Users;"

在动手迁移之前先确认源库的表数量和数据量,转换后再对比一次,能发现最明显的问题。实际项目中,A同学第一次执行时因为没开日志,转换完成后发现少了两张表,却完全不知道是哪个环节出了问题,白跑了一趟。

3.3 第一版迁移结果的检查清单

跑通了第一条转换命令之后,要建立一个固定的检查流程,而不是凭感觉判断结果对不对。我会建议按这张表逐项核对,每次迁移后都沿着这个路径走一遍。

检查项源库位置目标库位置判断标准
表数量SELECT COUNT(*) FROM sys.tablesSELECT COUNT(*) FROM sqlite_master WHERE type=‘table’数量一致,排除系统表差异
每表行数sys.partitions汇总或逐表COUNT逐表COUNT大表抽样,小表全量
字段数量INFORMATION_SCHEMA.COLUMNSPRAGMA table_info(表名)列数一致,列名一致
主键与唯一索引sys.indexessqlite_master中的索引定义索引类型和字段一致
自增列检查IDENTITY属性sqlite_sequence表当前序列值不小于最大行数
空值分布WHERE 关键字段 IS NULL计数同样条件计数数量一致,不要求值完全一致

第一次转换时,重点不要把时间花在逐条对比数据上,而是先确认结构和数量这类骨架信息。骨架没有大问题,再抽几个核心业务表做内容级抽查。如果抽查时发现数据内容错位,问题多半出在字段一一对应关系的逻辑上,这时再去源码里查映射部分怎么写的。

4. 字段类型与SQL语法差异:转换器的核心映射与调整逻辑

4.1 类型映射表:SQL Server各类型到SQLite的推荐落点

SqlConverter的核心价值在类型映射。SQL Server的类型体系是强类型加精确的精度语义,SQLite则靠存储类别和类型亲和性实现兼容。一个转换器做得好不好,就看这张映射表定义得是否合理。

我整理了一份常用的映射规则,这也是大部分开源转换器默认采用的方案。它们未必完全一致,但方向是统一的:

SQL Server类型SQLite类型说明
intINTEGER直接对应,4字节整型
bigintINTEGERSQLite的INTEGER就是8字节,正好对应
smallint / tinyintINTEGER类型变宽,无副作用
decimal(p,s)NUMERIC用NUMERIC亲和性保留精度,但排序规则弱化
moneyNUMERIC金额字段要注意小数位丢失
float / realREALSQLite的REAL是8字节浮点,float能装上
nvarchar(n) / varchar(n)TEXT长度约束在SQLite中不生效,建议保留为TEXT
text / ntextTEXT大文本在SQLite里就是一个TEXT记录
datetime / datetime2TEXTSQLite没有原生日期时间类型,通常存ISO8601字符串
dateTEXT建议统一存YYYY-MM-DD格式
timeTEXT存HH:MM:SS.SSS格式
uniqueidentifierTEXT存Guid字符串,注意‘-’分隔符
varbinary / imageBLOB字节流直接对应BLOB
bitINTEGER0或1,SQLite没有布尔型

这里有一个容易忽略的问题:datetime转换成TEXT之后,原来在SQL Server里可以对日期直接做运算的逻辑,在SQLite里全部要换成字符串函数或者date()函数来算。尤其是有索引的日期列,转换后的值格式如果不统一,索引就等于白建了。所以建议在转换时强制把datetime格式化到统一格式,例如’yyyy-MM-dd HH:mm:ss.fff’,让后续开发少踩格式坑。

还有decimal类型。SQL Server里decimal(18,2)是固定两位小数,转换成NUMERIC之后,SQLite并不会像SQL Server那样自动做四舍五入,它只是按文本或浮点存储。如果业务逻辑里对金额精度有严格要求,转换后必须在应用层做额外的round处理,这是手册里不会写明的隐藏行为。

4.2 语法翻译:从T-SQL到SQLite方言的必要变换

数据类型只是静态层面,真正复杂的是动态SQL语法。SqlConverter的作用范围如果包含了视图和存储过程,这里的处理就细腻很多。

视图的转换相对简单,核心是把T-SQL的写法替换成SQLite的写法。最常见的替换规则包括:ISNULL()改成ifnull(),GETDATE()改成datetime(‘now’,‘localtime’),TOP N改成LIMIT N,CAST(AS VARCHAR(10))改成CAST(AS TEXT),表别名必须加AS关键字,这个是SQLite的硬性要求,不加就报语法错误。

存储过程的转换就麻烦得多。SQLite没有存储过程机制,常规做法是转换成SQL函数或应用层逻辑,更常用的做法是直接跳过存储过程,只转换表和视图,然后把存储过程的逻辑在应用代码里重写。这一点在选型时就要确认清楚:如果源库的大量业务逻辑都在存储过程里,转换到SQLite必然要做二次开发,这不是一个转换工具能解决的事。

常见的做法是,SqlConverter会把能翻译的视图翻译出来,对存储过程做一个“翻译失败的清单”,在日志中列出哪些对象没转成功。这个清单特别重要,因为它暴露了残留风险。我在实际项目中看到有人转换完了直接用,结果上线才发现所有报表数据都不对,原因就是存储过程没搬过去,而生成SQLite库时系统没有任何报错提示。

-- SQL Server写法:视图里取最近一年的订单 CREATE VIEW v_RecentOrders AS SELECT TOP (100) OrderId, OrderDate, ISNULL(TotalAmount, 0) AS Amount FROM dbo.Orders WHERE OrderDate >= DATEADD(year, -1, GETDATE()); -- 转换到SQLite后的等价写法 CREATE VIEW v_RecentOrders AS SELECT OrderId, OrderDate, ifnull(TotalAmount, 0) AS Amount FROM Orders WHERE OrderDate >= datetime('now', '-1 year') LIMIT 100;

逻辑说明:这个例子展示了三种变换。TOP变成了LIMIT,ISNULL变成了ifnull,GETDATE加DATEADD的整体逻辑变成了datetime的修饰符语法。如果转换器连这种内置函数的对应关系都没做,那这个工具就只是把表结构复制过去,远称不上“转换器”。

另外要注意ORDER BY在TOP场景下的配合。SQL Server的TOP N带ORDER BY是指定排序后再取前N条,而SQLite的LIMIT是在查询结果上直接截断,排序和截断的先后顺序由执行计划决定。标准写法应当把子查询嵌套一层,确保先排序再LIMIT。这些细节在转换工具的代码里如果没处理,说明实现还很初级,需要自己改源码补上。

4.3 大表分批与事务提交的参数设置

数据量是转换是否可靠的关键分水岭。小表一次批量插入没问题,大表如果一次性写入,SQLite的journal文件会急剧膨胀,甚至撑爆磁盘;更麻烦的是,中途出错会导致整个事务回滚,之前转换的大表全部白做。

所以大表转换必须分批提交。SqlConverter的常见实现方式是:按主键或自增列分段读取源库的数据,每批插入SQLite后提交一次事务。这里有两个关键参数要关注:一个是每批的行数,一个是事务边界。

# 设置单批行数为5000,并开启日志记录每批耗时 SqlConverter \ --source "Server=127.0.0.1;Database=MyDB;User Id=sa;Password=xxx;TrustServerCertificate=true" \ --target "./output/MyDB.db" \ --batch-size 5000 \ --log "./logs/convert.log"

逻辑说明:–batch-size控制每次从SQL Server读取多少行,然后写入SQLite并提交事务。这个值不是越大越好,事务越大,单次提交耗时越长,而且出问题时回滚的范围也越大。也不是越小越好,太小会导致提交次数过多,SQLite的写性能会断崖式下降。

参数说明:我一般会把5000设为默认值。如果源表是大宽表,单行字段多且含大文本,就把行数降到1000;如果表结构简单、整型为主,20000也能跑。还有一个隐性指标:SQLite的WAL模式是否开启。如果转换器在创建目标库时执行了PRAGMA journal_mode=WAL,那么大批次写入的稳定性会明显提升,并发读也不会锁库。

还有一个常见问题,就是分批读取本身依赖排序。源表如果没有主键,分批读就没有可靠的边界。遇到这种表,我会先确认表是否能接受额外加一个row_number列作为分页条件,不行就老老实实全量读入再分批写。这个情况在遗留系统里很常见,转换器如果没处理,肯定会在某张大表上跑挂。

5. 避坑与常见问题:转换结果和预期不一致时的排查路径

5.1 乱码与字符集问题

现象:转换完成后,SQLite里的中文和特殊字符全部变成问号或乱码。

原因:SQL Server连接串里没有指定Character Set,或者驱动默认用了ANSI编码读取,而SQL Server里的字段实际存的是Unicode。另一种情况是源库里的nvarchar字段本身存的是合法Unicode,但转换器在写入SQLite时,把TEXT转换成了UTF-8,而读取侧用的是GBK,两边编码不一致。

解决:在连接串中显式加上编码相关参数。对于Microsoft.Data.SqlClient来说,连接默认就支持Unicode,但如果用的是旧版ODBC驱动,就需要指定Character Set为UTF-8;输出侧把SQLite的PRAGMA encoding设置为UTF-8,并在写入时统一用UTF-8字符串操作。乱码是数据迁移里最隐蔽的问题,因为它不影响程序启动,只影响数据内容,通常要到业务层比对结果才能发现。

我至今保留一个习惯:任何迁移完成后,先查一条包含中文的记录,从源库和SQLite各取一次,逐字节对比。这种检查和表数量检查一样重要,不能省。

5.2 自增列与主键约束在SQLite中的重建

现象:转换后的SQLite表,插入新数据时不按预期从最大值加1开始,而是从1开始重新计数。或者主键约束丢失,重复值可以插入。

原因:SQL Server里的IDENTITY属性在转换时只被映射成INTEGER字段,但没有被标记为INTEGER PRIMARY KEY。在SQLite中,只有INTEGER PRIMARY KEY才具备自增能力。如果转换器只映射了“类型为INTEGER”而没有设置主键属性,结果就是一个普通整数列,没有自增能力。

解决:确认转换源码里,识别到IDENTITY列时,把SQLite建表语句写成”Id INTEGER PRIMARY KEY AUTOINCREMENT”。注意AUTOINCREMENT和INTEGER PRIMARY KEY是两回事:前者通过sqlite_sequence表记录序列值,确保不会重用已删除的Id;后者是纯ROWID别名,删除最后一行后新插入的Id可能重用。为了保持和SQL Server行为一致,应当使用AUTOINCREMENT。

-- SQL Server侧的表结构定义 CREATE TABLE dbo.Users ( Id INT IDENTITY(1,1) PRIMARY KEY, UserName NVARCHAR(50) ); -- SQLite侧期望看到的结构 CREATE TABLE Users ( Id INTEGER PRIMARY KEY AUTOINCREMENT, UserName TEXT );

这里有一个反直觉的细节:SQL Server的IDENTITY不一定从1开始,也可能配置了IDENTITY(100,5)。转换器如果不能读取种子值和增量值,转换后的SQLite表很难完全模拟原行为。通常的做法是转换数据后,查一下当前最大值,再执行SQLite的序列重设命令,让后续自增值从正确位置继续。

5.3 视图和存储过程只能翻译不能执行

现象:转换日志显示“视图转换成功”,但查询视图时报错,说某个函数不存在。存储过程没有被转换下来,日志里甚至没有出现。

原因:SQLite支持视图,但函数支持范围远小于T-SQL。如果视图里用了自定义函数或SQL Server特有函数,转换器只是做了文本替换,替换完也没有验证语法;存储过程则是完全没有对应物,转换器可能直接跳过,这也体现在日志上。

解决:转换前精确扫描源库的对象清单。我一般先执行一条查询,列出所有视图和存储过程。转换完成后,逐一在SQLite里执行SELECT来验证视图是否可用。存储过程必须提前确认是否在迁移范围内,如果依赖方只有数据展示,存储过程可以丢弃;如果业务逻辑也在库内,那就得将每个存储过程的逻辑改写成应用层函数,这个工作量必须预留出来。

遇到这种情况,需要修改源码的人为处理策略:默认跳过的,注释掉跳过逻辑,改为输出一个待办列表到日志文件,方便评估工作量。这一步往往是决定项目工期是否可控的关键,早发现早评估。

5.4 数据量超过2GB时的SQLite上限风险

现象:源库数据量超过2GB,转换过程中途失败,或者转换完成但打开数据库时报错。

原因:SQLite的默认页面大小是4096字节,最大页面数是1024的平方级,总和上限约140TB。但很多转换工具在创建数据库时用的是默认配置,或者SQLite库文件所在的文件系统本身有2GB单文件限制。旧格式的SQLite库上限是2GB,新格式已经支持更大容量,但前提是初始化时页面大小设置正确。

解决:在转换前先确认目标SQLite文件所在磁盘的分区格式,再确认转换器是否在创建库时执行了合理的PRAGMA page_size配置。我一般会先创建一个空库,手动执行PRAGMA page_size=8192,然后再开始写入数据。page_size决定了单页能容纳的数据量,也影响大字段和索引的存储效率,转换器如果有初始化SQL的入口,这里就是关键调整项。

还有一种情况是单表超过2GB,但整体库没超过。这种情况通常是大表里有BLOB或TEXT扩展字段。处理的常用做法是:把大字段单独拆出来存到另外的表,保持主表的行宽度较小,让单页能容纳更多行,从而提升查询性能和降低I/O压力。这个优化不是转换器默认会做的,但在数据量大的场景下几乎是必须的。

注意:SQLite的journal_mode和临时文件目录也和数据量强相关。转换超大库时,要确保临时目录有足够的空间,否则写入到一半会报database or disk is full,这个错误标识容易被误判为SQLite本身的问题。

6. 进阶用法:把迁移命令化、参数化,纳入日常数据交付流程

第一次手动跑通转换只是起点,真正有价值的是把这个工具整合进你的交付流程,让迁移变成可重复、可审计、可定时执行的常规操作。

我通常会把它拆成三个步骤。第一步是封装配置文件,把连接串、目标路径、批大小、日志目录都放到一个JSON文件里,命令行只传一个profile参数。这样做的好处是:每个迁移任务的环境差异全都固化在配置中,不会因为某次执行漏了一个参数而产生不同的结果。配置文件也方便放进版本管理,谁改了什么一目了然。

{ "source": "Server=127.0.0.1;Database=MyDB;User Id=sa;Password=xxx;TrustServerCertificate=true", "target": "./output/MyDB.db", "batchSize": 5000, "log": "./logs/MyDB_convert.log", "excludeTables": ["audit_log", "temp_import"] }

第二步是执行校验。转换完成后,先跑表数量和行数对比,再对关键表做抽查,校验结果输出成一个文本报告。这个报告和日志文件一起归档,后续如果数据对不上,直接翻归档就能定位是哪一批转换引入的问题。经验告诉我,建立这个习惯之后,排障时间能减少一半以上。

第三步是定时化。如果你的SQL Server业务库每天或每周有更新,可以把转换命令写进计划任务,凌晨执行,早上上班直接拿SQLite文件做分析。源库里有新增表的时候,转换工具如果支持自动发现表,不需要改配置就能跑,这个细节值得确认一下。

我踩过的一个教训是:没有把转换器的版本随流程一起固化。某次升级了工具源码,重新编译后忘了兼容旧配置,导致批量提交大小和日志格式都变了,结果和旧数据对不上,排查花了一整天。后来的习惯是,每次重新编译都在配置里注明工具版本号,一份源码对应一份配置,绝不混用。

整个流程跑稳之后,SQL Server到SQLite的迁移就不再是临时任务,而是数据管线里一个稳定节点。你不再需要每天登录服务器手敲命令,也不用担心某次手滑少传了一个参数。让工具本身成为流程的一部分,这个方向值得投入。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询