直接上结论:CI/CD或跨平台部署必须选sqlcmd。因其原生支持GO、无需PowerShell模块、Linux开箱即用;而Invoke-SqlCmd依赖SqlServer模块且不识别GO,CI中易因漏装或语法解析失败导致上线异常。

直接用 sqlcmd 或 SqlPackage 执行预编译的 .sql 或 .dacpac 文件最稳妥,别写动态拼接脚本——90% 的线上失败都源于编码、连接库名、GO 分隔符或权限这四件事没对。
SQL Server:为什么 sqlcmd 比 Invoke-SqlCmd 更适合 CI/CD
sqlcmd 是原生命令行工具,不依赖 PowerShell 模块,Linux runner 上开箱即用;Invoke-SqlCmd 需要提前安装 SqlServer 模块,且在 PowerShell Core(7+)里默认不可用,CI 中容易漏装。
两者都不解析 T-SQL,只负责转发内容,所以关键不在“谁执行”,而在“传什么过去”。
-
sqlcmd -i script.sql -S server -U user -P pass -d database_name必须带-d,否则过程建在master里,上线后查不到 -
.sql文件必须是 UTF-8 无 BOM 编码,否则中文注释或字符串会乱码,sqlcmd不报错但执行结果异常 -
GO是客户端批处理分隔符,sqlcmd原生支持;Invoke-SqlCmd完全不识别GO,必须用正则提前拆成多段再循环调用 - 权限要够:至少
db_ddladmin或db_owner,不能只给db_datareader
MySQL:DELIMITER 不切对,语法错误就躲不掉
MySQL 服务端默认以分号 ; 结束语句,而存储过程体里大量含分号,不改分隔符,客户端根本不会把整段 CREATE PROCEDURE ... END 当作一条语句发过去——报错 ERROR 1064 (42000) 就是这个原因。
Linux系统管理专家,覆盖12大模块:用户权限、SSH、存储、网络、systemd、防火墙、日志监控、备份恢复、TLS证书、Ansible、容器、IaC。提供配置、验证、加固、监控、备份、自动化、故障排查、回滚闭环。关键词:useradd、sudo、sshd_config、chmod、SEL...
- 在
.sql文件开头加DELIMITER $$,结尾加DELIMITER ; - 所有
CREATE PROCEDURE的END后面都用$$,不是; - CI 脚本里禁用
mysql -e "source xxx.sql":它不支持DELIMITER切换;改用mysql -D db_name - 如果过程里调用了自定义函数,该函数必须已在目标库存在,否则报
ERROR 1305 (42000): FUNCTION xxx does not exist
用 .dacpac 部署比手写 SQL 脚本更稳,但得过三关
.dacpac 是 SSDT 编译出的二进制包,自带依赖分析、增量变更和部署预检能力,适合中大型项目。但它不是“扔进去就完事”,有三个硬门槛。
- 生成阶段必须用
dotnet build或 VS 编译,不能手动 zip 源文件凑数——.dacpac内部含校验和与元数据,篡改即失效 - 部署时用
SqlPackage.exe /Action:Publish /SourceFile:xxx.dacpac /TargetConnectionString:"...",连接字符串里必须含Initial Catalog=xxx - 首次部署前建议先用
/Action:Script生成实际执行的 T-SQL,人工过一遍——尤其注意DROP类操作是否符合预期 - 若过程含
EXECUTE AS或证书签名等高级权限,.dacpac默认不导出,需在 SSDT 项目属性里勾选Include Composite Objects
判断要不要重建存储过程,不能只看名字存不存在
每次 DROP + CREATE 会导致依赖它的应用短暂报 Invalid object name,还丢掉原有执行计划缓存和权限设置。真正该比的是逻辑是否变更。
- SQL Server:用
HASHBYTES('SHA2_256', CONVERT(NVARCHAR(MAX), definition))查sys.sql_modules,和本地文件算出的哈希比对;别用OBJECT_ID()判是否存在 - MySQL:
SHOW CREATE PROCEDURE proc_name输出不稳定(含时间戳、字符集声明),推荐导出后统一sed清洗再算md5sum - 所有哈希比对都要排除注释和空格差异——建议用
sqlformat工具标准化后再哈希 - CI 流水线里一旦发现哈希不一致,才触发重建;否则跳过,保留原对象及权限
最常被忽略的点:部署脚本里路径写相对路径,CI runner 切换工作目录后就找不到 .sql 文件;还有人把 GO 当成 T-SQL 关键字,在存储过程体里误用,导致语法直接崩。动手前先 sqlcmd -E -d testdb -i test.sql -v "test=1" 在测试库跑通最小闭环,比读十遍文档都管用。

















