Go 中无法直接执行 SQL 文件,需先读取文件内容,安全拆分语句(跳过注释与空行),再在事务中逐条调用 tx.Exec();MySQL 驱动须启用 multiStatements=true,生产环境推荐使用 golang-migrate 等专业迁移工具。

Go 里怎么执行 SQL 文件(.sql)?
直接用 database/sql 打开连接后,无法原生执行多语句 SQL 文件——它只认单条语句。你得自己读文件、拆分语句、逐条 db.Exec()。别指望 db.Exec("source init.sql") 这种 MySQL CLI 语法能在 Go 里跑通。
常见错误现象:ERROR 1064: You have an error in your SQL syntax,本质是把整个文件当一条语句传给了驱动;或者遇到 CREATE DATABASE 后紧跟 USE,但驱动不支持多语句(尤其 MySQL 默认禁用 multiStatements=true)。
- 先用
ioutil.ReadFile(Go 1.16+ 推荐os.ReadFile)读取 SQL 文件内容 - 用正则或简单换行+分号切分语句,跳过空行、
--和/* */注释(注意:别用复杂 SQL 解析器,除非真有嵌套注释或引号内分号) - 对每条非空语句调用
db.Exec();若需事务控制,必须显式tx, _ := db.Begin()+tx.Exec()+tx.Commit() - MySQL 驱动若要支持
CREATE DATABASE或多语句,DSN 必须加?multiStatements=true(如user:pass@tcp(127.0.0.1:3306)/?multiStatements=true),否则会报错
用 github.com/golang-migrate/migrate/v4 更靠谱吗?
靠谱,而且是生产首选。它不是“执行 SQL 文件”,而是管理迁移(migration):带版本号、可上可下、自动记录状态(在数据库建 migrations 表)。你扔给它的 SQL 文件必须按命名规范(如 20230501120000_create_users.up.sql),否则直接拒绝加载。
使用场景:服务启动时自动同步表结构、CI/CD 中部署 DB 变更、本地开发环境快速重置 schema。
- 安装 CLI:
go install github.com/golang-migrate/migrate/v4/cmd/migrate@latest - 执行迁移:
migrate -path ./migrations -database "mysql://user:pass@tcp(127.0.0.1:3306)/mydb" up - Go 代码内集成:用
migrate.New()加载文件系统源,再调Migrate.Up();注意它默认不回滚失败的 partial migration,得靠goose或自定义逻辑兜底 - 兼容性影响:不支持 SQLite 的
ALTER TABLE ... RENAME COLUMN这类方言特性,SQL 文件得保持跨引擎兼容,或按数据库类型分目录
为什么不能直接 exec.Command("mysql", "-e", "source xxx.sql")?
能跑,但不推荐——绕过了 Go 的连接池、超时控制、错误上下文,且严重依赖系统环境。CI 环境没装 mysql-client、容器里 PATH 不对、密码明文写进命令行,全是坑。
典型错误:exec: "mysql": executable file not found in $PATH,或 Access denied for user 'root'@'localhost'(因为 CLI 认证方式和 Go 驱动不同)。
- 若真要用,必须用
exec.Command("mysql", "-h", "127.0.0.1", "-u", "user", "-ppass", "dbname")并通过cmd.Stdin写入 SQL 内容,避免密码暴露在进程列表 - Windows 下路径含空格会导致命令解析失败,需额外包裹引号逻辑
- 无法获取每条语句的执行耗时、影响行数、错误位置(比如第 87 行语法错),调试成本远高于纯 Go 实现
SQL 文件里有 GO 语句(如 PostgreSQL 的 \i 或 \set)怎么办?
Go 驱动完全不识别 psql 元命令。像 \i other.sql、\set myvar 'hello'、DO $$ ... $$ 块里的变量替换,都会直接报错。
解决方案只有一个:预处理。用脚本(Python/Shell/甚至 Go 自己)先扫描并展开这些元命令,生成纯标准 SQL 再交给 Go 执行。
- PostgreSQL 场景下,优先改用
pgx驱动 +pgxpool,它支持ExecEx()和自定义 query hooks,但依然不解析\i - 如果 SQL 文件由 DBA 提供且含大量
\i,建议协商改为扁平化结构,或让他们导出为单文件(pg_dump --no-owner --no-privileges) - 别尝试用正则匹配
DO \$\$.*?\$\$来提取块——PL/pgSQL 里字符串可能含未转义的$$,必须用真实 parser,得上github.com/jackc/pgconn底层协议层才可靠
实际项目里最常被忽略的是字符集和 SQL 模式。MySQL 连接若没设 charset=utf8mb4&parseTime=true,中文插入会乱码;PostgreSQL 若没在 SQL 文件头加 SET client_encoding = 'UTF8';,某些环境会默认 Latin1 导致迁移失败。这些细节不写进初始化逻辑,上线第一分钟就卡住。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











