简介:这是一套基于C#开发的SQL Server数据库脚本导出与导入工具,面向.NET开发者、DBA及数据库运维人员,解决日常开发中频繁迁移表结构、存储过程、视图等数据库对象时手动操作繁琐、易出错的问题。工具功能对标SQL Server 2014 Management Studio的“生成脚本”向导,支持批量导出完整数据库或指定对象的可执行SQL脚本,并实现安全、可控的脚本回滚式导入。压缩包共66个文件,含10个核心C#源码(如Program.cs、SqlHelper.cs、frmMain.cs)、9个运行依赖DLL、8个示例SQL脚本、4个可执行EXE文件及配套配置(App.config)、资源文件(.resx)和Visual Studio项目文件(.sln、.csproj),结构完整,开箱即用。资源包仅2.16MB,轻量高效,已有1493人学习下载,适合快速集成到本地开发环境、理解SQL Server元数据提取逻辑,或作为数据库自动化部署的轻量级脚本管理方案。
1. SQL Server 脚本导出导入不是备份替代品:它解决的是结构可复现、跨环境部署和版本可控这三件事
你有没有遇到过这样的场景:开发环境改完存储过程,测试环境没同步,上线前才发现逻辑不一致;或者新同事拉下代码仓库后,本地数据库空空如也,手动建表建索引建约束,一上午就没了;又或者客户现场要求“把当前库的全部对象定义导出来,我们审计用”,你打开 SSMS 点右键 → 生成脚本 → 下一页 → 下一页 → 勾到手抖,最后导出一个 200MB 的 .sql 文件,连注释都带乱码?——这个SQLSERVER脚本导出导入.zip就是为这类真实痛点设计的轻量级工程化工具包。它不替代BACKUP DATABASE,也不做高可用复制,而是专注把数据库的结构定义(DDL)拆成可读、可审、可 Git 管理、可 CI/CD 自动执行的纯文本脚本。核心能力包括:自动识别依赖顺序(比如先建表再建外键)、过滤系统对象与临时对象、支持按 schema / object type / name pattern 精确筛选、导出结果自带IF NOT EXISTS安全包裹、导入时自动跳过已存在对象或强制重建。适合 DBA 做基线快照、开发团队做数据库即代码(Database-as-Code)、运维人员做灰度环境初始化。它用 C# 实现,不依赖 SQL Server Management Studio,命令行即可驱动,Windows/Linux(通过 dotnet runtime)双平台可用。
2. 导出脚本:从连接字符串到可提交的 .sql 文件,每一步都可控
2.1 连接配置与目标范围定义:为什么不能只写服务器名?
导出的第一步不是点按钮,而是明确「导什么」和「从哪来」。这个工具包不接受模糊输入,所有连接参数必须显式声明。常见错误是只填Server=192.168.1.100,结果报错Login failed for user ''—— 因为它默认走 Windows 身份验证,而你的目标实例可能禁用了该模式。正确做法是构造完整连接字符串,并通过-c参数传入:
dotnet SqlScriptExporter.dll -c "Server=192.168.1.100;Database=MyAppDB;User Id=sa;Password=StrongPass123!;Encrypt=False;TrustServerCertificate=True;"提示:
Encrypt=False和TrustServerCertificate=True是本地测试或内网环境的合理组合;生产环境必须启用加密并配置有效证书,否则连接会被拒绝。Database参数指定源数据库,不可省略;若想导出整个实例的所有用户数据库,需配合-all-dbs参数(见后文)。
2.2 对象类型与筛选策略:不是所有对象都该进 Git 仓库
盲目导出全部对象(包括sysdiagrams、queue_messages、MSreplication_options等系统表)会导致脚本体积膨胀、可读性下降、Git Diff 失效。本工具提供三级筛选机制:
层级一:对象大类(必选)
用-t参数指定,支持Table,View,StoredProcedure,Function,Trigger,Index,Constraint,Schema,User,Role,Permission。可多次使用,例如:-t Table -t View -t StoredProcedure层级二:Schema 过滤(推荐)
用-s指定 schema 名,如-s dbo或-s Sales。若需排除sys和INFORMATION_SCHEMA,无需显式写,工具默认跳过。层级三:名称正则匹配(精准控制)
用-n后接 .NET 风格正则表达式,例如只导出以usp_开头的存储过程:-n "^usp_.*"或排除所有测试用对象(命名含
_test或_dev):-n "^(?!.*(_test|_dev)).*$"
实际项目中,我一般会分两轮导出:第一轮-t Table -t Constraint -t Index -s dbo得到核心数据结构;第二轮-t StoredProcedure -t Function -n "^usp_[a-zA-Z0-9_]{3,}$"得到业务逻辑层。这样 Git 提交历史清晰,Code Review 有焦点。
2.3 输出格式与安全包装:为什么IF NOT EXISTS不是可选项?
导出的 SQL 脚本若直接在目标库执行,极易因对象已存在而中断。本工具默认开启--wrap-if-not-exists(等价于-w),对每个CREATE语句自动包裹:
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Users]') AND type in (N'U')) BEGIN CREATE TABLE [dbo].[Users]( [Id] [int] IDENTITY(1,1) NOT NULL, [Name] [nvarchar](50) NOT NULL, CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED ([Id] ASC) ) ON [PRIMARY] END参数说明:
-w开启包裹,-W(大写)则强制使用DROP + CREATE模式(慎用!仅限开发环境重置)。若需关闭包裹(如导出用于文档归档),加--no-wrap。另外,--include-dependencies(简写-d)会自动分析对象依赖关系,确保Table在ForeignKey之前导出,Schema在Table之前导出——这是避免“对象不存在”错误的关键逻辑,不是简单排序。
2.4 批量导出多库与自动化集成:一个命令生成 12 个库的脚本
当管理多个租户库(如Tenant_A,Tenant_B…)或微服务拆分后的分库(OrderDB,UserDB,PaymentDB),手动逐个导出效率极低。工具支持-all-dbs参数,但需配合-exclude-dbs排除系统库和临时库:
dotnet SqlScriptExporter.dll -c "Server=localhost;User Id=sa;Password=123;" \ -all-dbs \ -exclude-dbs "master,model,msdb,tempdb,ReportServer,ReportServerTempDB" \ -t Table -t View -t StoredProcedure \ -o "./scripts/"此命令会在./scripts/目录下生成Tenant_A.sql,Tenant_B.sql等独立文件。更进一步,可结合 PowerShell 脚本实现每日凌晨自动导出并推送到 Git:
# export-daily.ps1 $timestamp = Get-Date -Format "yyyyMMdd_HHmm" $exportDir = "./daily-backup/$timestamp" New-Item -ItemType Directory -Path $exportDir -Force dotnet SqlScriptExporter.dll -c $env:SQL_CONN_STR -all-dbs -exclude-dbs "master,model,msdb" -t Table -o "$exportDir" git add "$exportDir" git commit -m "Auto-export: $timestamp" git push关键点在于:$env:SQL_CONN_STR应设为环境变量,避免密码硬编码;-o指定输出目录,工具会自动按库名创建子目录(如./daily-backup/20240520_0200/Tenant_A/Tables.sql),结构清晰可追溯。
3. 导入脚本:从 .sql 文件到目标库,如何避免“执行一半失败”的灾难
3.1 导入前检查:为什么必须先运行--dry-run?
导入操作具有破坏性(尤其开启-W强制重建时),绝不能跳过预检。--dry-run参数会模拟执行全过程:连接目标库、解析 SQL 文件、检查语法、验证对象依赖、列出将被执行的语句,但不真正执行任何 DDL。典型输出:
[DRY RUN] Connected to server '192.168.1.100', database 'TargetDB' [DRY RUN] Parsed 1 file: ./scripts/MyAppDB.sql (327 lines) [DRY RUN] Will execute 42 statements: - CREATE SCHEMA [dbo] (skipped: exists) - CREATE TABLE [dbo].[Users] (will create) - CREATE INDEX [IX_Users_Email] ON [dbo].[Users] (will create) - CREATE PROCEDURE [dbo].[usp_GetUserById] (will create) - ... (truncated) [DRY RUN] All dependencies resolved. No syntax errors.注意:若
--dry-run报错Could not resolve dependency: [dbo].[Orders] referenced by [dbo].[usp_GetOrderDetails],说明脚本中Orders表定义缺失或顺序错误——此时应回溯导出步骤,确认是否漏选了Table类型,或--include-dependencies未启用。
3.2 执行模式选择:--mode safe与--mode force的本质区别
工具提供两种执行策略,对应不同场景:
--mode safe(默认):对每个CREATE语句加IF NOT EXISTS判断,若对象已存在则跳过。适用于增量部署、CI/CD 流水线中的“确保环境一致”阶段。优点是安全,缺点是无法更新已有对象(如修改字段长度)。--mode force(-f):先DROP再CREATE,强制覆盖。适用于开发环境重置、测试库重建。警告:此模式下若脚本包含CREATE DATABASE,会直接删除并重建整个库!生产环境严禁使用。
实际项目中,我严格区分:
- 流水线部署用
--mode safe,配合 Git 分支策略(如main分支对应生产结构,develop对应开发结构); - 本地开发重置用
--mode force,但必须限定在LocalDB或容器实例中,且命令中显式写死Database=MyAppDB_Local。
3.3 错误处理与事务控制:单文件内是否回滚?
SQL Server 的 DDL 语句(如CREATE TABLE)默认在隐式事务中执行,但跨语句不保证原子性。本工具在导入时采用以下策略:
- 单个
.sql文件内的所有语句,在一个SqlConnection中顺序执行; - 若某条语句失败(如
CREATE INDEX因字段不存在报错),后续语句不会自动跳过,而是继续执行(除非加--stop-on-error); - 工具不开启显式事务(
BEGIN TRAN),因为CREATE DATABASE等语句不允许在事务中执行,强行包裹会导致Transaction context in use by another session错误。
因此,正确做法是:
- 使用
--stop-on-error让失败立即终止(推荐); - 将大型脚本按逻辑拆分(如
01_schemas.sql,02_tables.sql,03_indexes.sql),每个文件独立导入; - 关键路径(如主键、外键)放在靠前文件,依赖项(如视图、存储过程)放后。
# 安全导入流程示例 dotnet SqlScriptImporter.dll -c "Server=prod-sql;Database=MyAppDB;..." \ --mode safe \ --stop-on-error \ -f "./scripts/01_schemas.sql" \ -f "./scripts/02_tables.sql" \ -f "./scripts/03_constraints.sql"3.4 权限与上下文:为什么sa权限不是万能钥匙?
即使连接字符串用sa,导入仍可能失败,原因在于数据库上下文权限。例如:
- 脚本中含
CREATE SCHEMA [Sales],但sa用户在目标库中未被授予CREATE SCHEMA权限(默认不授); CREATE USER [app_user]要求ALTER ANY DATABASE权限;GRANT SELECT ON [dbo].[Users] TO [app_user]要求执行者对[dbo].[Users]有TAKE OWNERSHIP或CONTROL权限。
解决方案不是提权sa,而是最小权限原则:
- 创建专用部署账号
deploy_user; - 在目标库执行:
CREATE USER [deploy_user] FOR LOGIN [deploy_user]; ALTER ROLE [db_owner] ADD MEMBER [deploy_user]; -- 仅限开发/测试库 -- 生产库用更细粒度: GRANT CREATE SCHEMA TO [deploy_user]; GRANT CREATE TABLE TO [deploy_user]; GRANT ALTER ANY ASSEMBLY TO [deploy_user]; -- 若脚本含 CLR - 连接字符串改用
-c "Server=...;User Id=deploy_user;Password=..."。
这样既保障执行,又符合安全审计要求。
4. 避坑指南:五个血泪经验换来的高频问题排查清单
4.1 现象:导出脚本中中文列名/注释显示为?????,导入时报错Incorrect syntax near '?'
原因:SQL Server 默认使用SQL_Latin1_General_CP1_CI_AS排序规则,对 Unicode 字符支持不完整;而工具导出时若未指定编码,Windows 控制台默认GBK,导致 UTF-8 写入的文件被误读。
解决:强制指定输出编码为 UTF-8 with BOM(兼容性最好):
dotnet SqlScriptExporter.dll -c "..." -t Table -o "./scripts/" --encoding utf-8-bom同时,SSMS 或 VS Code 打开脚本时,确认右下角编码显示为UTF-8 with BOM,而非UTF-8或GBK。
4.2 现象:导入时卡在CREATE PROCEDURE,CPU 占用 100%,数分钟后超时
原因:存储过程中含WAITFOR DELAY '00:05:00'或WHILE (1=1) BEGIN ... END等无限等待逻辑,工具将其视为普通 T-SQL 执行,无超时机制。
解决:导出时用-n过滤掉测试用存储过程(如命名含_test,_debug);或导入前用文本工具预处理脚本,删除可疑语句。永远不要导出含WAITFOR或xp_cmdshell的生产脚本。
4.3 现象:--include-dependencies启用后,导出的Table脚本中FOREIGN KEY约束未生成WITH CHECK,导致导入后约束不生效
原因:SQL Server 系统视图sys.foreign_keys中is_not_trusted=1表示约束未验证(可能是WITH NOCHECK创建),工具如实导出,但NO CHECK约束在导入后处于“未启用”状态,不阻止非法数据插入。
解决:导出后,用正则批量替换(VS Code 支持):
- 查找:
(CONSTRAINT \[.*?\] FOREIGN KEY.*?REFERENCES \[.*?\]\.\[.*?\]) WITH NOCHECK - 替换:
$1 WITH CHECK或在导入前,执行ALTER TABLE [dbo].[ChildTable] WITH CHECK CHECK CONSTRAINT [FK_Child_Parent]启用。
4.4 现象:-all-dbs导出时,某个库(如ArchiveDB)报错The database is not accessible,整个导出中断
原因:工具默认串行处理,一个库失败即退出。而ArchiveDB可能处于RESTORING状态或权限不足。
解决:加--continue-on-error参数,失败库记录日志并跳过,继续处理其余库:
dotnet SqlScriptExporter.dll -c "..." -all-dbs --continue-on-error -o "./backup/"日志中会明确写出Skipped database 'ArchiveDB': The database is not accessible,便于事后单独排查。
4.5 现象:导入CREATE FUNCTION时失败,错误CREATE FUNCTIONmust be the first statement in a query batch
原因:工具导出的函数脚本中,CREATE FUNCTION前有GO批处理分隔符,但某些旧版 SQL Server(如 2008 R2)或特定连接模式下,GO不被识别。
解决:导出时禁用GO分隔符,改用--no-go:
dotnet SqlScriptExporter.dll -c "..." -t Function --no-go此时函数定义会以CREATE OR ALTER FUNCTION形式导出(SQL Server 2016+),兼容性更好;若目标库版本低于 2016,则需手动将CREATE OR ALTER替换为IF NOT EXISTS...CREATE。
5. 进阶技巧:用脚本校验结构一致性,把“人工比对”变成自动化门禁
5.1 生成结构哈希值:三行命令判断两个库是否完全一致
最朴素的“一致性校验”是比对导出的 SQL 文件内容,但文件体积大、格式差异(空格、换行)干扰多。更可靠的方式是提取关键元数据生成哈希。本工具内置--hash模式,不生成脚本,只输出各对象的 SHA256 校验值:
# 导出源库哈希清单 dotnet SqlScriptExporter.dll -c "Server=src;Database=ProdDB;..." \ -t Table -t View -t StoredProcedure \ --hash \ -o "./hashes/prod.json" # 导出目标库哈希清单 dotnet SqlScriptExporter.dll -c "Server=tgt;Database=StagingDB;..." \ -t Table -t View -t StoredProcedure \ --hash \ -o "./hashes/staging.json"生成的prod.json结构如下:
{ "Tables": [ { "Name": "dbo.Users", "Hash": "a1b2c3d4e5f6..." }, { "Name": "dbo.Orders", "Hash": "x9y8z7w6v5..." } ], "Views": [ /* ... */ ], "StoredProcedures": [ /* ... */ ] }然后用 PowerShell 快速比对:
$prod = Get-Content "./hashes/prod.json" | ConvertFrom-Json $stg = Get-Content "./hashes/staging.json" | ConvertFrom-Json $prod.Tables | ForEach-Object { $tgt = $stg.Tables | Where-Object { $_.Name -eq $_.Name } if (-not $tgt) { Write-Warning "Missing table: $($_.Name)" } elseif ($_.Hash -ne $tgt.Hash) { Write-Warning "Hash mismatch: $($_.Name)" } }为什么不用
diff直接比脚本?因为CREATE TABLE中ON [PRIMARY]与ON [FG_Data]的文件组差异、索引FILLFACTOR的微小调整,都会导致哈希不同,但业务上可能完全等价。而--hash模式默认忽略这些非功能属性(可通过--hash-include-filegroup显式开启),聚焦在列定义、约束逻辑、T-SQL 主体等核心差异上。
5.2 构建数据库变更流水线:从 Git Commit 到自动部署
真正的 Database-as-Code 不止于导出,而在于将结构变更纳入软件交付闭环。以下是某公司落地的简化流水线:
| 步骤 | 工具/命令 | 说明 |
|---|---|---|
| 1. 开发者提交 DDL | ALTER TABLE [dbo].[Users] ADD [Phone] NVARCHAR(20); | 在database/changes/20240520_add_phone.sql中编写,不直接执行 |
| 2. CI 触发校验 | dotnet SqlScriptExporter.dll -c "$SRC_CONN" -t Table -s dbo --hash > ./tmp/src.hashdotnet SqlScriptExporter.dll -c "$TGT_CONN" -t Table -s dbo --hash > ./tmp/tgt.hashCompare-Object (gc ./tmp/src.hash) (gc ./tmp/tgt.hash) | 若哈希不同,说明目标库未应用变更,流水线失败并告警 |
| 3. 人工审批后部署 | dotnet SqlScriptImporter.dll -c "$TGT_CONN" --mode safe -f "./database/changes/20240520_add_phone.sql" | 审批通过后,执行导入;--mode safe保证幂等 |
| 4. 部署后验证 | SELECT COUNT(*) FROM sys.columns WHERE object_id = OBJECT_ID('dbo.Users') AND name = 'Phone' | 查询元数据确认字段存在 |
关键设计点:
- 所有 DDL 变更必须以
.sql文件形式存入 Git,禁止直接在生产库执行ALTER; - CI 校验用
--hash而非diff,避免格式干扰; - 导入命令固定为
--mode safe,杜绝意外覆盖。
5.3 处理特殊对象:全文索引、空间数据、内存优化表的导出要点
并非所有 SQL Server 对象都能被通用脚本完美覆盖。针对三类高频特殊对象,需额外配置:
| 对象类型 | 导出注意事项 | 示例参数 |
|---|---|---|
| 全文索引(Full-Text Index) | 默认不导出,需显式加-t FullTextCatalog -t FullTextIndex;且必须确保源库已启用全文搜索(sp_fulltext_database 'enable') | -t FullTextCatalog -t FullTextIndex --include-dependencies |
| 空间数据类型(geography, geometry) | 导出时若含CREATE SPATIAL INDEX,需确认目标库spatial_results数据库选项已启用,否则导入报错Spatial results are not enabled | -t SpatialIndex --sql-version 2019(指定版本以启用新语法) |
| 内存优化表(In-Memory OLTP) | 必须加--in-memory参数,否则CREATE TABLE ... WITH (MEMORY_OPTIMIZED=ON)会被忽略;且目标库需有MEMORY_OPTIMIZED_DATA文件组 | -t Table --in-memory --filegroup "modfg" |
血泪经验:某次上线前,DBA 忘记导出全文目录,导致搜索功能失效。从那以后,我每次导出前都强制执行
SELECT * FROM sys.fulltext_catalogs检查是否存在,再决定是否加-t FullTextCatalog。希望帮到你。
本文还有配套的精品资源,点击获取