SqlConverter:从Sql Server到SQLite的完整迁移指南

发布时间:2026/10/12 18:06:50
SqlConverter:从Sql Server到SQLite的完整迁移指南 简介专门用于把 SQL Server 数据库迁移到 SQLite 的转换工具需要具备基础的 C# 编译与数据库知识即可使用。它保留源库的表结构、索引、外键、视图与触发器并转换业务数据适合把服务端数据移植到本地轻量库、移动端或嵌入式环境的开发人员。作者依据 CodeProject 上以色列开发者 Liron Levi 的原始源码重新打包并翻译解决了网上常见版本无法编译运行的问题源码可自行编译调整且包含 x86/x64 运行版本。资源共 87 个文件压缩包约 9.66MB其中 26 个 cs 源码文件构成核心转换逻辑16 个 dll 与 3 个 exe 支撑直接运行resources/resx 管理界面资源sln/csproj 便于加载工程和重新生成另有 App.config 可调整运行参数。从 DatabaseSchema、TableSchema、SqlServerToSQLite 等模块可以看到结构映射与批量数据迁移的实现脉络。已有 884 人学习/下载适合需要可靠转换工具或研究跨数据库移植实现的开发者。1. SqlConverter 是什么为什么要从 Sql Server 迁到 SqliteDB很多团队在项目早期用 Sql Server 做开发库等到了部署阶段才发现麻烦现场服务器不一定有 Windows 环境客户机房可能只有 Linux或者干脆就是嵌入式设备更别提 Docker 里装 Sql Server 动辄几个 GB 的内存占用。这时候把数据迁到 SQLite 就成了最务实的出路——单文件、零配置、跨平台还能直接被业务进程内嵌读取。但迁移这件事远不是「导出 Excel 再导入」这么简单。SqlConverter 这个项目解决的就是这条迁移链路里最脏最累的一环把 Sql Server 里的表结构、索引、约束、自增列和几百万行数据完整搬到 SQLite 的数据库文件里。它的价值在于「有源码可编译」——你不用依赖某个闭源工具的黑匣子逻辑转换规则不对就自己改代码重新编译。适合谁用正在做产品本地化部署的桌面端开发者、需要把历史 Sql Server 数据归档成轻量库的运维、以及要在离线环境里反复做数据迁移的交付工程师。这篇文章我会把它的转换原理、编译方法、参数调优和踩坑点一次讲透。2. 核心转换逻辑类型映射与依赖关系处理2.1 Sql Server 到 SQLite 的数据类型映射规则Sql Server 和 SQLite 的类型系统差异是转换脚本最核心的战场。SQLite 用「动态类型 类型亲缘性」这套机制Type Affinity 只分 TEXT、NUMERIC、INTEGER、REAL、BLOB 五类而 Sql Server 有几十种具名类型。所以第一步要做的就是把 Sql Server 的字段类型翻译成 SQLite 能理解和存储的形式。我在 SqlConverter 里看到的映射策略大致是这么走的int/bigint/smallint/tinyint 直接对应 INTEGERdecimal/numeric/money 转成 NUMERIC但如果业务对小数精度要求不高建议直接转 REALvarchar/nvarchar/char/nchar/text 统一走 TEXT因为 SQLite 的 TEXT 不区分长度varchar(50) 和 varchar(255) 存进去没有区别datetime/datetime2/smalldatetime 转 NUMERIC 或者 TEXT取决于你想存成 Unix 时间戳还是 ISO 字符串bit 转 INTEGER0/1uniqueidentifier 转 TEXT存字符串形式的 GUIDimage/varbinary 转 BLOB。最后统一把生成的字面量包一层。这里有个关键差异必须处理Sql Server 里is_nullable为 false 的列SQLite 建表时也应保留NOT NULL但很多转换工具为了图省事会丢弃这个约束后果就是下游程序按旧逻辑写入空值时SQLite 根本不报错数据坏在源头。所以写转换脚本时务必从INFORMATION_SCHEMA.COLUMNS里读出来并保留。2.2 自增列、默认值和触发器的移植方案Sql Server 的IDENTITY(1,1)自增列是迁移的重灾区。SQLite 里对应的机制是INTEGER PRIMARY KEY AUTOINCREMENT但有个前置条件该列必须是整型且是主键。如果你的自增列不是主键比如一张业务表里id INT IDENTITY单独存在、主键是联合唯一键那 SQLite 没法直接用 AUTOINCREMENT需要退而求其次用普通 INTEGER PRIMARY KEY 或者干脆在应用层维护序列。我一般建议改成INTEGER PRIMARY KEY就行因为 SQLite 的 rowid 机制本身就会自动分配一个比当前最大值大 1 的整数值——除非你删过最大行需要绝对不回用否则 AUTOINCREMENT 没必要。默认值这块有个大坑Sql Server 默认值很多时候是函数表达式比如GETDATE()、NEWID()、SYSUTCDATETIME()。这些在 SQLite 里不能用需要映射为CURRENT_TIMESTAMP对应 GETDATE()、datetime(now)或lower(hex(randomblob(16)))对应 NEWID()。SqlConverter 对这部分做了正则替换你在自己开发时也要维护一张内置函数映射表把 Sql Server 常用函数逐一映射过去。触发器迁移更麻烦。Sql Server 的触发器是按FOR INSERT / FOR UPDATE / FOR DELETE定义的还分 AFTER 和 INSTEAD OF。SQLite 的 CREATE TRIGGER 语法虽然长得像但语义有差异比如 SQLite 没有 INSTEAD OF INSERT 里的INSERTED虚拟表取而代之的是NEW.和OLD.引用。转换脚本至少要完成把INSERTED替换成NEW、DELETED替换成OLD、改写AFTER INSERT AS的语法位置。触发器的 WHERE 条件里如果有UPDATE(column)这种写法SQLite 不支持需要改成NEW.col OLD.col OR (NEW.col IS NOT NULL AND OLD.col IS NULL)这种等价判空写法很繁琐但必须做。2.3 索引与主外键的转换顺序迁移顺序决定成败。我见过很多半成品转换器先建索引再导数据结果插入时疯狂重建索引速度慢到无法忍受。正确顺序应该是先建表带约束但不带索引→ 批量灌数据 → 再建索引和外键SQLite 对大批量插入加临时索引的开销控制很差这个顺序能让整体耗时降一个量级。主键迁移时注意SQLite 的表默认是 rowid 表如果主键是TEXT类型的非整型值建表时直接写PRIMARY KEY (col)就行但类型必须声明为 TEXT PRIMARY KEY。如果是联合主键按原样写成PRIMARY KEY (a, b)即可SQLite 会自动建一个对应的唯一索引。外键约束默认在 SQLite 里是关闭的需要连接字符串里加foreign_keysON才生效转换脚本应该在生成 SQLite 连接时默认打开否则约束丢失会导致上下游数据不一致而不自知。索引类型方面Sql Server 的聚集索引在 SQLite 里没有等价物直接转成普通索引。Filtered index带 WHERE 的过滤索引SQLite 不支持跳过并在转换日志里提示手动确认。唯一索引没问题直接照搬。索引列的排序方向ASC/DESCSql Server 和 SQLite 都支持按原值写入即可。3. 编译与改造拿源码跑起你自己的转换器3.1 编译环境准备与源码目录结构解读拿到 SqlConverter 源码后第一件事不是急着点编译而是先看目录结构弄清楚哪些是生成代码、哪些是核心逻辑。通常这类项目长这样一个主程序工程负责命令行解析和流程编排一个 SchemaReader 模块负责通过SMO或INFORMATION_SCHEMA读取 Sql Server 的元数据一个 ScriptBuilder 模块负责把元数据渲染成 SQLite 的建表脚本和数据插入脚本还有若干辅助工具类处理字符串转义、类型映射和特殊字符清洗。编译环境建议直接用 VS 2022装上.NET Desktop Development工作负载如果源码用的老框架可能需要额外装对应的 SDK。打开解决方案后先还原 NuGet 包常见依赖包括Microsoft.SqlServer.SqlManagementObjects用来读 Sql Server 元数据、System.Data.SQLite或Microsoft.Data.Sqlite用来写 SQLite 文件。如果编译报错说找不到 SQL Server 相关程序集多半是没装 SMO 的 NuGet 包手动Install-Package Microsoft.SqlServer.SqlManagementObjects就能过。3.2 最小可用的编译命令和命令行调用示例# 还原项目依赖在解决方案根目录执行 dotnet restore SqlConverter.sln # 以 Release 配置编译输出到 bin 目录 dotnet build SqlConverter.sln -c Release -o ./out # 查看转换器支持的命令行参数编译成功后 ./out/SqlConverter.exe --help # 执行一次转换从 Sql Server 的 TestDB 库转到 data.sqlite ./out/SqlConverter.exe -s Serverlocalhost;DatabaseTestDB;User Idsa;Passwordyour_pwd;Trusted_ConnectionFalse; -t ./data.sqlite --schema-only参数说明-s指定 Sql Server 连接字符串注意要开启Trusted_ConnectionFalse并显式提供账号密码否则在服务场景里容易踩到 Windows 身份验证的权限坑-t指定输出 SQLite 文件路径如果目录不存在需要先建好加了--schema-only表示只导结构不导数据建议第一次跑的时候先用这个参数验证建表语句正确性数据量大时不要一上来就全量灌入。如果你拿到的是 .NET Framework 老版本源码而不是 .NET Core/.NET 5 的 SDK 风格项目那就不能用dotnet build了得用 msbuild 编译# 打开 Visual Studio Developer Command Prompt 后执行 msbuild SqlConverter.sln /p:ConfigurationRelease /p:Platformx64 /p:OutputPath./out注意写法区别msbuild 的参数是/p不是--且 OutputPath 属性设置的是相对于项目文件的路径不是解决方案根目录。这块环境差异很容易劝退第一次上手的人我就见过有人拿 msbuild 的命令参数去套 dotnet半小时没编译通过。3.3 让转换器支持你的私有类型改映射表的三个关键位置源码的价值就在于能改。常见的定制需求是类型映射不够全比如 Sql Server 的geography、geometry空间数据类型或者hierarchyid标准映射表里可能只给了 fallback 到 BLOB 或 TEXT。这时候要改的是三个位置第一个位置是类型映射字典找到类似Dictionarystring, string TypeMap的静态类或方法把geometry映射到你想要的目标类型比如TEXT存 WKT 字符串或者BLOB存二进制。第二个位置是生成建表语句的模板类因为空间类型通常伴随 SRID 信息如果只映射类型不改建表模板SRID 就丢了SQLite 里查询空间数据会出偏差。第三个位置是数据读取器Sql Server 读出来的是SqlGeometry对象必须显式调用.ToString()或.Serialize()转换成你能存进 SQLite 的格式不然框架会报类型转换异常。改完映射表后建议加一个--validate参数做全字段类型复核实际做法是转换完成后跑一段 SQL检查 SQLite 里有没有出现意外的 NULL 填充或类型不匹配这能帮你把改动的影响收敛在可控范围。4. 命令行实战从连接串配置到数据灌入的完整流程4.1 连接字符串配置与权限坑写连接字符串时先分清场景在开发机用 Windows 身份验证跑通最快Serverlocalhost;DatabaseTestDB;Trusted_ConnectionTrue;就够了。但在交付和 CI/CD 环境里目标机器的 Sql Server 不一定开了 Windows 认证所以生产连接串一定要按 SQL Server 身份验证来且需要确认sa或迁移账号有db_datareader和db_ddladmin权限——前者读数据后者读表结构缺一个都会在转换中途报权限错误。常见的错误是连接字符串里漏了EncryptFalse或TrustServerCertificateTrue新版驱动默认强制加密Sql Server 没配证书时报证书链错误。我建议统一按这个模板写兼容性最好Server10.20.30.40,1433;DatabaseSourceDB;User Idyour_migration_user;Password**;EncryptFalse;TrustServerCertificateTrue;Connection Timeout30;EncryptFalse和TrustServerCertificateTrue看着矛盾但实际含义是「不要求强制 TLS 加密但是如果服务器返回证书不校验证书链的合法性」。这在跨网段的迁移任务里能省掉很多 TLS 握手阶段因为 DNS 解析或证书过期导致的报错。4.2 结构迁移与数据灌入先干跑再全量完整跑一次转换前我强烈建议分两阶段执行。第一阶段只导结构用--schema-only检查三样东西表数量是否和源库一致每张表的主键是否都正确迁移自增列在 SQLite 里是否真的能自增。检查方法用 Python 或任何能连 SQLite 的客户端跑两行 SQL-- 查表清单是否存在 SELECT name, sql FROM sqlite_master WHERE typetable ORDER BY name;-- 插入一条不指定主键的测试记录确认自增生效 INSERT INTO your_table (col_a, col_b) VALUES (test, 1); SELECT last_insert_rowid();如果last_insert_rowid()没有返回新分配的主键 ID说明自增列没有正确映射成 PRIMARY KEY回源头看 2.2 节的方案。第二阶段做全量数据灌入。SqlConverter 一般会采用SqlBulkCopy风格的分批读取策略每 5000 行读一批然后写一批批大小可以在源码里找到BatchSize常量去调。如果单条记录里含大字段比如 text/ntext 或 image 列建议调小到 1000否则 SQLite 在写 BLOB 时容易因为事务占用内存过大导致变慢。整个灌入过程要盯住的是 SQLite 锁默认 journal_mode 是 delete写入时整个文件被锁其他读进程会报database is locked。反过来如果源库有人在写入或长事务未提交你读出来的快照数据可能不一致异常捕获逻辑要能定位到这种情况。4.3 事务与进度批量提交还是不提交SqlConverter 源码里一般会有事务包装逻辑但默认行为可能和你的需求有偏差。两种策略对比来看策略优点缺点适用场景单事务全量提交一步到位若失败可整体回滚百万级大表内存溢出的概率高失败要重来单表数据量 10 万分批提交每 N 行 Commit内存稳定中途失败可续传部分已提交数据需要清理逻辑复杂数据量 100 万或含大字段实操经验是如果你预期有脏数据比如源库里某字段有非法编码或超范围数值别用单事务。批量提交配合一个--skip-count参数跳过程序自动统计行数的步骤能显著提升插入速度。还有一个容易忽略的参数是--bulk-size或BatchSize它控制单批 INSERT 语句里拼接的 VALUES 元组数量。建议先默认跑一段看耗时如果能稳定落在每秒 2-5 万行就没有改的必要如果连 1 千行都要好几分钟优先看日志里是不是每条记录触发了一次单独的文件写操作这通常是事务未开启或批提交参数没生效。5. 迁移避坑与常见问题排查5.1 表名和列名的保留字冲突Schema 清洗必须做现象转换完的 SQLite 文件执行SELECT * FROM user直接报语法错误但源 Sql Server 上明明查得好好的。原因user、order、group、key、index这些词在 Sql Server 里是关键字或部分上下文保留字Sql Server 允许用方括号括起来当表名SQLite 也允许但转换脚本如果没在生成建表语句时给对象名加上双引号就会裸着被语法解析器当成关键字拦截。更隐蔽的是列名比如desc、read这类词在源库没问题到了 SQLite 就不行。解决转换脚本里所有标识符统一用双引号包裹user或者干脆在类型映射阶段做一轮QuoteIdentifier()处理。核实方法是转换完成后用sqlite3打开目标库手工执行一条SELECT * FROM user;确认双引号写法可查然后用PRAGMA table_info(user);核对列名是否和源库一致。5.2 大字段溢出与编码问题导致的行级失败现象数据导入到某个固定行数就抛The string contains a character that is not valid in the current code page或者 VARCHAR 字段的值在 SQLite 里变成了乱码。更常见的翻车方式是指定nvarchar(max)时源库包含 PHP 序列化或 JSON 字符串里的特殊字符破坏了 INSERT 语句的拼接。原因Sql Server 的VARCHAR是扩展 ASCII 码页NVARCHAR才是 Unicode。SqlConverter 如果按 DataTable 的列类型逐行读取默认编码可能不是 UTF-8当字段里混入韩文、藏文或 Emoji 表情符号时转码丢数据或抛异常。另一个常见原因是拼接 INSERT 语句时直接把字符串字面量拼进 SQL没有走参数化查询遇到单引号和\以及空字符时拼接被破坏。解决转换逻辑里读取列值后显式转成 UTF-8 字节再写入 SQLite 连接参数并且放弃字符串拼接 INSERT 的写法所有数据灌入走 Command 参数化Parameter.AddWithValueU0000 这类控制字符在参数化模式下才能完整保留。另外连接 SQLite 时DateTime类型如果源库精度是datetime2(7)SQLite 默认只保留毫秒级精度需要按业务自行决定是否四舍五入或截断我建议设置datetimeformatCurrentCulture或者直接用 TEXT 存储别依赖驱动默认转换。5.3 源库有视图、存储过程转换后「消失」了现象转换完的表都在但打开 SQLite 文件找不到视图和存储过程。原因SQLite 本身没有存储过程视图支持有限且不完整。大多数转换器只对表做迁移遇视图直接跳过或仅输出警告。Sql Server 里视图若引用多张表 JOINSQLite 能建但性能差异巨大存储过程则确实没有等价物需要提前在业务代码里改写成应用层事务或普通 SQL 调用。解决建议在需求层就明确——视图不一定非得迁过去可以把视图对应的查询逻辑在服务层重写成 ORM 查询或参数化 SqlCommand存储过程如果逻辑复杂只能在业务代码里逐段翻译。迁移工具只负责表和索引其他的一切都该在 pre-migration 阶段筛查记录不要在转换完成后再追悔。5.4 误以为--schema-only之后直接跑全量就行结果外键全报错现象先导结构再导数据本来没问题但全量灌数据到子表时报FOREIGN KEY constraint failed。原因源库的 parent 表数据还没导入完成或导入顺序乱了。Sql Server 没有强制要求先插父表再插子表但 SQLite 的foreign_keysON开启后插入子表时若父表对应主键不存在会直接报错。如果之前编译时顺手打开了外键约束批量导入阶段的外键检查会导致本就慢的灌入雪上加霜。解决数据导入阶段临时关闭外键约束在连接完成后立刻执行PRAGMA foreign_keysOFF;全部导完后执行PRAGMA foreign_key_check;巡检完整性确认没有孤儿记录后再打开。如果你是完全离线环境且不需要级联删除干脆后续连接都不开外键把一致性校验交给业务逻辑SQLite 本身不背这个锅。6. 进阶自定义转换规则与回归验证做到这一步说明你已经不满足于「能用」而是想让 SqlConverter 更贴合自己的业务了。常见的扩展方向有三个自定义类型映射文件、转换结果回归比对、以及增量迁移。我重点说增量迁移——这是生产环境最需要的功能。Sql Server 侧一般有个ModifiedTime或UpdatedAt字段。做增量迁移比如每小时同步新增数据可以改成在源库按这个时间字段过滤WHERE ModifiedTime lastSyncTime读取后写入 SQLite 用 UPSERTINSERT ... ON CONFLICT(id) DO UPDATE SET ...代替纯 INSERT。改动的关键是 SqlConverter 需要支持传入一个过滤条件字符串作为参数这个参数不写死在源码里而是在命令行或配置文件里动态注入./out/SqlConverter.exe -s Server... -t ./data.sqlite --increment --since 2024-01-01 00:00:00 --filter ModifiedTime这样拆分的好处是首次全量用一个脚本每小时增量用同一个脚本加参数不引入第二套同步机制。实测几百万行的表增量同步的耗时会从全量的分钟级降到秒级前提是源库在这列上建了索引不然排查的人会想骂人。回归验证的常见做法是在转换前后各跑一段统计 SQL把两张表按行数、关键字段 SUM、主键最大值做对比写成一个简单的 diff 脚本# 源库Sql Server统计 SELECT COUNT(*) FROM dbo.orders; SELECT SUM(order_amount) FROM dbo.orders; # 目标库SQLite统计 sqlite3 data.sqlite SELECT COUNT(*) FROM orders; sqlite3 data.sqlite SELECT SUM(order_amount) FROM orders;两组数对不上就去查中间环节定位问题通常在类型转换丢弃精度或 NULL 值没正确处理。这类回归脚本建议每次都跑别凭感觉相信工具的输出。最后说个我自己的教训刚接手 SqlConverter 时我图省事直接全表迁移忽略了源库的编码设置和已存在视图结果在客户现场花了一整天清数据。后来凡是改动映射表或编译配置我都在自己机器上先跑一台模拟源库、再跑一遍全量和增量验证确认没问题才交付。做这种数据迁移工具交付的置信度比功能炫酷重要得多。希望这篇笔记能让你在真正动手时避开我踩过的坑一次把 Sql Server 到 SQLiteDB 的这条路走顺。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询