
在使用 mattes/migrate 等迁移工具时,MySQL 存储过程因 DELIMITER 语法与 Go MySQL 驱动不兼容而常失败;解决方案是移除 DELIMITER 声明,直接使用分号终止语句,由驱动自动解析。
在使用 `mattes/migrate` 等迁移工具时,mysql 存储过程因 `delimiter` 语法与 go mysql 驱动不兼容而常失败;解决方案是移除 `delimiter` 声明,直接使用分号终止语句,由驱动自动解析。
Go 生态中主流的 MySQL 驱动(如 go-sql-driver/mysql)默认禁用多语句执行(multiStatements=false),因此包含 DELIMITER 的传统 SQL 存储过程定义无法被正确解析——DELIMITER @@ 和 @@ 结尾会被视为非法语法,导致迁移中断。
✅ 正确做法:完全移除 DELIMITER 相关声明,将 CREATE PROCEDURE 语句改写为单语句格式,并确保内部逻辑不依赖自定义分隔符。MySQL 在解析 CREATE PROCEDURE 时,只要语句结构完整、括号匹配、末尾以分号结束,即可被正确识别和创建(前提是服务端配置允许存储过程创建,如 log_bin_trust_function_creators=1)。
以下是适配迁移文件的推荐写法(.up.sql 示例):
-- 创建存储过程:根据订单 ID 返回计算值
CREATE PROCEDURE get_value(IN my_id BIGINT(20), OUT out_value DOUBLE)
BEGIN
SELECT CASE
WHEN o.financial_status = 'test' THEN 0.0
ELSE 1.0
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;
⚠️ 注意事项:
- 参数类型需显式声明方向:IN/OUT/INOUT 不可省略(原问题中 my_id 缺少 IN,虽部分版本容忍,但建议显式写出以增强可移植性);
- 字符串字面量统一用单引号:避免双引号引发 SQL 模式冲突(尤其在 ANSI_QUOTES 开启时);
- 确保迁移用户权限充足:需具备 CREATE ROUTINE 和 EXECUTE 权限,否则会报错 ERROR 1419 (HY000);
- 测试环境一致性:本地开发、CI 测试与生产环境的 MySQL 版本及 SQL mode 应尽量一致,避免 STRICT_TRANS_TABLES 等模式导致隐式转换失败;
- 回滚考虑:.down.sql 中应包含 DROP PROCEDURE IF EXISTS get_value;,保证迁移可逆。
? 小技巧:若逻辑复杂、含多个嵌套语句,仍可拆分为多个独立 CREATE PROCEDURE 语句(每条以分号结尾),无需 DELIMITER —— mattes/migrate 会按行或分号分割并顺序执行,完全满足需求。











