SQL Server脚本导出导入:版本对齐、执行顺序与避坑清单
简介这份压缩包面向 SQL Server 数据库开发与运维人员提供一套用 C# 实现的脚本导出导入工具及配套源码可用于批量生成数据库对象脚本、完成数据库之间的导出导入其功能参照 SQL Server 2014 Management Studio 的“生成脚本”流程实现适合需要将备份、迁移与自动化脚本生成接入自有程序的开发者参考。包内共 66 个文件约 2.16MB包含 C# 源码与项目/解决方案文件、编译后的可执行程序及依赖库、SQL 脚本、配置文件、窗体资源与缓存文件等源码、可运行程序和脚本示例相互配套便于对照理解导出导入的实现路径。该资源已有 1490 人学习。读者可从中获得可二次开发的工程骨架、数据库对象脚本化思路、导入导出流程的代码组织方式以及配置与界面模块的参考写法适合用来搭建内部运维小工具或作为学习 C# 操作 SQL Server 的实例。1. SQL Server 脚本导出导入先别急着点“下一步”版本和兼容级别先对齐上周帮一位开发者收拾一台“半废”的测试库他在本机用高版本管理工具把库生成脚本拿到另一台只装了较低版本实例的机器上执行第一句就报“不支持的语法”后面几百张表全断在那儿。这不是玄学是脚本导出导入最常见的坑——工具版本、实例版本、数据库兼容级别三者没对齐导出的脚本就只是一堆跑不起来的文本。SQLSERVER脚本导出导入.zip这套资源解决的就是这条链路把库结构和数据生成为可执行的 T-SQL 脚本再在目标实例上还原不依赖备份文件适合跨机器搬库、留一份可读的结构快照、给测试环境造数据的人。装之前先把版本和环境列清比装完再翻车省事得多。2. 环境准备实例版本、兼容级别与工具链三件套2.1 先确认实例版本和数据库兼容级别生成脚本这件事真正决定“能不能跑”的不是你装的管理工具多新而是目标实例的版本以及源库的兼容级别。常见做法是先在源库上跑一句查询把版本、 edition、兼容级别一次看全再决定生成脚本时选“较高版本”还是“较低版本”的服务器。-- 查看实例版本与当前库兼容级别决定脚本生成的目标版本 SELECT VERSION AS instance_version, -- 实例完整版本串 SERVERPROPERTY(ProductVersion) AS product_version, SERVERPROPERTY(ProductLevel) AS patch_level, SERVERPROPERTY(Edition) AS edition, name, compatibility_level -- 关键脚本语法下限由它决定 FROM sys.databases WHERE name DB_NAME();compatibility_level是这套流程里最容易被忽略的参数。它决定目标库能不能用某些语法比如较新的字符串聚合、TRIM、批处理级WITH等。源库兼容级别是 160 而目标实例根本装不到这个级别脚本里就会混入对方不认的写法。我的习惯是生成脚本前把目标实例版本记在纸上生成时“为服务器版本编写脚本”一律选目标版本或更低宁可少用新语法也别让脚本在别人机器上第一行就倒下。顺带看一眼排序规则。跨机器还原时源库是区分大小写的排序规则、目标实例默认不区分建表脚本里的字符串列会跟着走后面应用层查数据就可能出现“明明有却查不到”的怪现象。常见做法是生成脚本时保留原排序规则并在还原后核对一遍-- 还原后核对排序规则是否与源库一致 SELECT name, collation_name FROM sys.columns WHERE object_id OBJECT_ID(dbo.YourTable) AND collation_name IS NOT NULL;2.2 管理工具与依赖组件怎么装这套资源本身是脚本集合落地前提是本机有一个能连实例的客户端。图形化工具装哪个版本取决于你要连的最低实例版本工具版本可以高于实例但反过来不行。安装时有两个勾选项值得留意——一是客户端协议二是命令行工具。很多人只装图形界面结果脚本里用到sqlcmd就抓瞎。命令行方式的安装步骤按这个顺序走# 1. 安装微软官方软件源以常见 Linux 发行版为例命令按本机包管理器调整 curl -sSL https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - curl -sSL https://packages.microsoft.com/config/ubuntu/22.04/prod.list | sudo tee /etc/apt/sources.list.d/mssql-release.list # 2. 更新索引后安装命令行工具与驱动 sudo apt-get update sudo ACCEPT_EULAY apt-get install -y mssql-tools18 unixodbc-dev # 3. 把工具目录加进 PATH避免每次写全路径 echo export PATH$PATH:/opt/mssql-tools18/bin ~/.bashrc source ~/.bashrc # 4. 验证客户端能连上实例-C 表示信任服务器证书内网自签场景常用 sqlcmd -S 127.0.0.1,1433 -U sa -P YourStrong!Passw0rd -C -Q SELECT VERSION这里的ACCEPT_EULAY是安装时接受许可条款的开关非交互环境必须带上否则安装会卡在交互提示上。unixodbc-dev是驱动依赖缺了它sqlcmd能装上但连库时报驱动找不到。第 4 步的-C参数针对自签证书场景正式环境有受信证书时不该长期带着它。验证能返回版本串说明客户端这一侧通了接下来才是脚本本身的活。提示图形化工具和命令行工具装在同一台机器上不冲突但 PATH 里如果有多个版本的sqlcmd先which -a sqlcmd看清楚调的是哪一个版本错位排查起来很费时间。2.3 目录结构与资源怎么放把压缩包解开后建议按“源脚本 / 目标脚本 / 日志”三类分目录别一股脑丢桌面。我一般这样摆mkdir -p ~/sqlwork/{schema,data,logs} # schema 放仅结构的脚本data 放数据脚本logs 放执行输出 # 大库生成脚本时按对象类型拆分单文件过大容易在编辑器里卡死 ls -lh ~/sqlwork/schema | head拆分目录的意义在还原阶段特别明显结构脚本要先跑完数据脚本再跟上中间任何一步失败都能单独重跑不用从头再来。单个脚本文件超过几十兆时图形化编辑器打开会非常吃力用sqlcmd -i直接执行更稳。3. 生成脚本把“仅架构”和“架构加数据”分开做3.1 图形化生成脚本的关键选项生成脚本向导里选项不少真正影响成败的就那几个。走一遍流程源库右键 → 任务 → 生成脚本 → 选择对象整库或指定表→ 高级选项。高级里这几项必须改选项建议值说明为服务器版本编写脚本目标实例版本或更低决定可用语法上限选错直接语法报错编写脚本的数据类型架构和数据 / 仅架构按用途选别默认要编写脚本的数据的类型INSERT默认数据量小时够用大表另想办法包含 IF NOT EXISTSTrue重复执行不炸适合反复还原的测试库脚本 DROP 和 CREATE按需目标库已有同名对象时才勾编写统计信息脚本一般 False统计信息由目标库自己重建更合适设置脚本编写选项 → 继续编写脚本按需出错时是否继续首轮调试建议 False“包含 IF NOT EXISTS”这一项是我踩过坑之后每次必勾的。没有它脚本跑第二遍就报对象已存在测试环境反复还原时只能手动删库费时还容易误删。数据脚本用默认的INSERT方式适合几千行以内的配置表、字典表大表用这种方式生成出来的脚本会大到没法看得换批量导出思路。3.2 用命令行生成便于重复和留痕图形化点一次两次还行天天点就是浪费。用sqlcmd配合-o输出到文件能把这步固化成可重复的动作# 仅导出结构用 sqlcmd 执行一段生成逻辑不现实更常见是用工具生成一次后固化为模板 # 这里演示的是把已生成的结构脚本在目标端执行的标准姿势 sqlcmd -S 127.0.0.1,1433 -U sa -P YourStrong!Passw0rd -C \ -d TargetDb \ -i ~/sqlwork/schema/schema_all.sql \ -o ~/sqlwork/logs/schema_apply.log \ -b # -b遇错返回非零退出码脚本化流水线里必带-i指定输入脚本-o把输出重定向到日志-b让出错时返回非零退出码。很多人不带-b脚本中途报错但命令返回成功自动化流程以为跑完了实际只建了一半表这种“静默半成品”是最难查的翻车场景。日志文件一定要留出问题时它是唯一的黑匣子。对于数据量大的表更稳的路子是导出为可分批执行的INSERT集合或者干脆放弃脚本方式、改用实例自带的批量导入导出能力。脚本导入导出这套方案的定位是中小数据量、需要可读可审的结构搬运不是海量数据迁移的主力。注意生成脚本时如果勾了“脚本 DROP 和 CREATE”在目标库执行前一定确认没有同名生产对象这条我见过有人手滑对着正式实例跑后果不用多说。4. 还原实操执行顺序、批处理分隔与事务控制4.1 执行顺序和批处理分隔符脚本还原最常见的失败不是权限是顺序。正确顺序是先建库或建空库→ 建架构和表 → 建索引与约束 → 导数据 → 建视图、存储过程、函数 → 建外键。生成脚本时如果一次性全选工具通常会按依赖排好但拆开手动执行就很容易把外键建在数据前面导致插入被外键卡住。另一个隐蔽的坑是批处理分隔符。生成的脚本里大量使用GO而GO不是 T-SQL 语句是客户端工具识别的批处理分隔符。用某些驱动或非官方客户端整段执行时GO会被当普通文本直接语法报错。所以执行这类脚本老老实实用sqlcmd或官方管理工具别图省事塞进普通驱动执行。# 分阶段执行每段单独留日志失败能定位到阶段 for f in schema/01_tables.sql schema/02_indexes.sql data/03_data.sql schema/04_views.sql; do echo $f sqlcmd -S 127.0.0.1,1433 -U sa -P YourStrong!Passw0rd -C \ -d TargetDb -i $HOME/sqlwork/$f -b \ -o $HOME/sqlwork/logs/$(basename $f).log || { echo 失败于 $f; exit 1; } done这段循环把四个阶段串起来任何一段失败就停下并打印失败文件比一次性跑完全部再回头找错高效得多。-b在这里是关键它让失败可见。4.2 事务包裹与回滚预案数据脚本执行到一半失败最想要的是“后悔药”。把数据段包在一个事务里失败即回滚目标库不会停在半截状态-- 数据导入段建议显式事务包裹失败整体回滚避免半成品数据 SET XACT_ABORT ON; -- 任何运行时错误都终止批处理并回滚事务 BEGIN TRY BEGIN TRANSACTION; -- 此处为生成脚本中的 INSERT 语句区 INSERT INTO dbo.Dict (Code, Name) VALUES (A01, N示例项); COMMIT TRANSACTION; END TRY BEGIN CATCH IF TRANCOUNT 0 ROLLBACK TRANSACTION; THROW; -- 保留原始错误号和信息方便排查 END CATCH;SET XACT_ABORT ON让运行期错误直接中断并回滚不透支事务THROW保留原始错误信息比重新拼一条错误消息有用得多。要注意的是生成的脚本里往往自带GO事务不能跨GO批次所以事务包裹要么在生成时不带GO、要么按批分段包。这一点不提前想清楚包了事务照样回滚不了。4.3 账号与权限的连带影响脚本搬到另一台实例登录名和应用账号也得一起处理。只搬库不搬账号应用连上来会报登录失败搬了账号但没对上库用户映射又会报无权访问。常见做法是先建登录名再建库用户并映射-- 目标实例侧先建服务器登录名再在库内建用户并映射 CREATE LOGIN app_user WITH PASSWORD ChangeMe_Str0ng!; GO USE TargetDb; GO CREATE USER app_user FOR LOGIN app_user; GO ALTER ROLE db_datareader ADD MEMBER app_user; ALTER ROLE db_datawriter ADD MEMBER app_user; GO密码别直接写死在脚本里入库这段只是示意写法。实际还原时把账号段单独抽成一次性的初始化脚本执行完就删。权限按最小化给只给读写角色别图省事丢进sysadmin。5. 避坑清单脚本导出导入最容易翻车的五件事第一条脚本执行报“对象已存在”还原中断。现象是第二次还原同一份脚本第一条CREATE TABLE就报错停下。原因是生成脚本时没勾“包含 IF NOT EXISTS”或勾了“脚本 DROP 和 CREATE”却没确认目标对象。解决办法是重新生成时勾上 IF NOT EXISTS或在还原前显式清空目标库的同名对象两个动作选一个别混着来。第二条中文数据变成问号。现象是导入后字符串列全是?。原因是脚本文件编码和实例排序规则不匹配工具按非 Unicode 方式写入了VARCHAR列。解决办法是生成脚本时确保字符串列按NVARCHAR输出、文件保存为带 BOM 的 UTF-8导入时用sqlcmd -f 65001指定编码。已导入的数据只能重导没有补救捷径。第三条脚本在大表数据段卡死或超时。现象是INSERT段执行几十分钟没动静最后连接超时。原因是单条批处理过大、日志增长过快。解决办法是把数据脚本按批拆分每批几百到几千行或用-l调大登录超时、分批提交。上百万行的表本就不该走脚本方式换批量导入工具更合适。第四条外键约束导致数据插入失败。现象是数据段报外键冲突。原因是执行顺序把外键建在了数据之前或数据本身存在孤儿记录。解决办法是调整顺序表 → 数据 → 外键若历史数据确实有孤儿行先查出来清理再补外键。第五条GO被当成普通语句报语法错误。现象是换了个客户端执行第一处GO就报错。原因是该客户端不识别批处理分隔符。解决办法是改用官方命令行工具或管理工具执行别在普通驱动里整段跑含GO的脚本。6. 验证跑通用三条查询确认结构与数据都对上了脚本跑完不等于搬成功了。我一般不看“执行成功”的提示只看三样东西对象数量、数据行数、关键约束。先比对源库和目标库的对象数量-- 目标库侧统计各类型对象数量与源库结果逐行比对 SELECT type_desc, COUNT(*) AS cnt FROM sys.objects WHERE is_ms_shipped 0 AND type IN (U,V,P,FN,IF,TF) GROUP BY type_desc ORDER BY type_desc;表、视图、存储过程、函数数量一一对上说明结构段完整。再看数据行数挑几张核心业务表和字典表比对-- 目标库侧核对关键表行数与源库同查询结果对比 SELECT Orders AS tbl, COUNT_BIG(*) AS rows_cnt FROM dbo.Orders UNION ALL SELECT Dict, COUNT_BIG(*) FROM dbo.Dict;COUNT_BIG而不是COUNT几十亿行的大表也不会溢出。最后确认约束和索引真的建上了而不是被脚本静默跳过-- 检查外键、主键、唯一约束是否齐全 SELECT OBJECT_NAME(parent_object_id) AS tbl, name, type_desc FROM sys.objects WHERE type IN (PK,F,UQ) AND is_ms_shipped 0 ORDER BY tbl, type_desc;这三步做完结构、数据、约束三层都验过才算真的跑通。另外建议在目标库上跑一次DBCC CHECKDB的快速版确认页结构没有在导入过程中被写坏——脚本导入不像备份还原那样自带校验这一步没人替你做。从那以后我每次搬库都强制走一遍“对象数 → 行数 → 约束”这三条查询不管多急都不跳。脚本导入这套方案的优势就是每一步都可读可审代价是没人替你兜底验证只能自己来。希望帮到你。本文还有配套的精品资源点击获取