
在使用 mattes/migrate 等 Go 迁移工具时,MySQL 存储过程因含 DELIMITER 语句常导致执行失败;实际可直接移除 DELIMITER 及其配对符号(如 @@),依靠 SQL 驱动自动识别语句边界,从而在单条语句模式下成功创建存储过程。
在使用 `mattes/migrate` 等 go 迁移工具时,mysql 存储过程因含 `delimiter` 语句常导致执行失败;实际可直接移除 `delimiter` 及其配对符号(如 `@@`),依靠 sql 驱动自动识别语句边界,从而在单条语句模式下成功创建存储过程。
MySQL 官方驱动(如 go-sql-driver/mysql)默认禁用多语句执行(multiStatements=false),这是出于安全考虑,也意味着迁移文件中若包含 DELIMITER 声明及自定义结束符(如 @@),会导致语法解析错误——驱动无法识别该非标准语法,直接报错“Unknown database error”或“You have an error in your SQL syntax”。
但关键在于:在单迁移文件、单 SQL 语句上下文中,DELIMITER 并非必需。MySQL 服务端在接收到 CREATE PROCEDURE ... END; 结构时,只要 END 后紧跟分号(;),且该分号未被嵌套在字符串或注释中,即可正确解析整个存储过程定义。因此,只需将原始 SQL 简化为标准单语句格式即可:
CREATE PROCEDURE get_value(my_id BIGINT(20), OUT out_value DOUBLE)
BEGIN
SELECT CASE
WHEN o.financial_status = 'test' THEN 0
ELSE 1
END INTO out_value
FROM `order` o
LEFT JOIN `my_table_2` io ON io.field_2 = o.id
LEFT JOIN `my_table_3` ip ON io.field_3 = ip.id
WHERE o.id = my_id;
END;
✅ 此写法完全兼容 mattes/migrate + mysql 驱动,无需修改连接参数(如开启 multiStatements=true,该选项存在注入风险且不推荐用于生产迁移)。
⚠️ 注意事项:
- 确保 END; 后无空格或换行干扰(部分旧版驱动对空白敏感);
- 避免在过程体中使用未转义的单引号嵌套(建议统一用 ' 包裹字符串,而非双引号);
- 若迁移需兼容 PostgreSQL 或其他数据库,请避免使用 MySQL 特有语法(如 OUT 参数、BEGIN...END 块),此时应拆分为数据库专用迁移文件;
- 测试阶段务必在真实 MySQL 5.6+ 环境验证——尤其注意 sql_mode 设置(如 STRICT_TRANS_TABLES)可能影响 SELECT ... INTO 行为。
总结:Go 迁移中创建 MySQL 存储过程的核心原则是「回归标准 SQL 语句本质」。放弃客户端侧的 DELIMITER 抽象,信任服务端的语句解析能力,既提升兼容性,又降低安全风险。











