创建MySQL存储过程时出现Error 1064错误的排查与解决
存储过程创建报错排查与MySQL/Oracle语法差异
问题描述
从Oracle切换到MySQL后,创建名为HITUNGMASUK的存储过程时出现两处Error 1064语法错误:
- 参数定义行附近语法错误
- 执行
DELIMITER;命令时出错
报错信息
Error Code: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'ZID_TRANSAKSI VARCHAR(64), ZTGL1 DATE, ZTGL2 DATE ) BEGIN DECLARE ...' at line 2 -------------------------------------------------- Query: DELIMITER; Error Code: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'DELIMITER' at line 1
原存储过程代码
DELIMITER $$ CREATE PROCEDURE HITUNGMASUK ( ZID_TRANSAKSI VARCHAR(64), ZTGL1 DATE, ZTGL2 DATE ) BEGIN DECLARE done INT DEFAULT FALSE; DECLARE PTAHUN VARCHAR(4); DECLARE PBULAN VARCHAR(2); DECLARE PHITUNG INTEGER DEFAULT 0; DECLARE CEKKARTUSTOK INTEGER DEFAULT 0; DECLARE PID_BARANG VARCHAR(255) DEFAULT 'X'; DECLARE PPANJANG INTEGER DEFAULT 0; DECLARE CEKKARTUSTOK CURSOR FOR SELECT B.id_barang, SUM(B.total_Panjang) AS PANJANG FROM transaksi_gudang A, line_item_barang B WHERE A.ID_Transaksi LIKE ZID_TRANSAKSI AND A.Tanggal BETWEEN ZTGL1 AND ZTGL2 AND B.ID_GR = A.ID_Transaksi GROUP BY B.id_barang; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; PTAHUN = YEAR(ZTGL1); PBULAN = MONTH(ZTGL1); OPEN CEKKARTUSTOK; teams_loop: LOOP FETCH CEKKARTUSTOK INTO PID_BARANG,PPANJANG; IF PID_BARANG = 'X' THEN INSERT INTO kartustok(TAHUN,BULAN,ID_BARANG,KODE_GUDANG,MASUK) VALUES (); END IF; IF done THEN LEAVE teams_loop; END IF; END LOOP; CLOSE CEKKARTUSTOK; SELECT FROM kartustok C WHERE C.TAHUN = PTAHUN AND C.BULAN = PBULAN AND C.ID_BARANG = P END $$ DELIMITER;
错误原因与修复方案
1. DELIMITER命令语法错误
- 原因:
DELIMITER命令后必须跟空格和目标分隔符,原代码中DELIMITER;没有空格,MySQL无法识别;同时DELIMITER $$与CREATE PROCEDURE连写易导致解析混淆。 - 修复:恢复分隔符时使用
DELIMITER ;(注意DELIMITER和;之间的空格),且DELIMITER $$后换行再写CREATE PROCEDURE。
2. 参数与变量定义错误
- 原因1:MySQL存储过程参数需显式指定
IN/OUT/INOUT模式(默认虽为IN,但省略会触发解析异常);另外变量CEKKARTUSTOK既被声明为整数又被声明为游标,命名冲突。 - 修复1:参数前添加
IN标识,重命名整数变量为CEKKARTUSTOK_COUNT避免与游标冲突。 - 原因2:MySQL变量赋值必须使用
SET关键字,原代码中PTAHUN = YEAR(ZTGL1);是Oracle写法,MySQL不支持。 - 修复2:改为
SET PTAHUN = YEAR(ZTGL1);、SET PBULAN = MONTH(ZTGL1);。
3. 其他逻辑语法错误
INSERT INTO的VALUES()为空,需补充对应列的有效值;SELECT语句未指定查询列,需明确列名;WHERE子句中C.ID_BARANG = P的P未定义,修正为已声明的PID_BARANG;- 游标处理顺序错误:应先判断
done状态再执行业务逻辑,避免处理空数据。
修复后的完整代码
DELIMITER $$ CREATE PROCEDURE HITUNGMASUK ( IN ZID_TRANSAKSI VARCHAR(64), IN ZTGL1 DATE, IN ZTGL2 DATE ) BEGIN DECLARE done INT DEFAULT FALSE; DECLARE PTAHUN VARCHAR(4); DECLARE PBULAN VARCHAR(2); DECLARE PHITUNG INTEGER DEFAULT 0; DECLARE CEKKARTUSTOK_COUNT INTEGER DEFAULT 0; -- 重命名避免与游标冲突 DECLARE PID_BARANG VARCHAR(255) DEFAULT 'X'; DECLARE PPANJANG INTEGER DEFAULT 0; -- 游标声明需放在所有变量声明之后 DECLARE CEKKARTUSTOK CURSOR FOR SELECT B.id_barang, SUM(B.total_Panjang) AS PANJANG FROM transaksi_gudang A JOIN line_item_barang B ON B.ID_GR = A.ID_Transaksi -- 使用标准JOIN语法替代旧写法 WHERE A.ID_Transaksi LIKE ZID_TRANSAKSI AND A.Tanggal BETWEEN ZTGL1 AND ZTGL2 GROUP BY B.id_barang; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; -- MySQL变量赋值必须用SET SET PTAHUN = YEAR(ZTGL1); SET PBULAN = MONTH(ZTGL1); OPEN CEKKARTUSTOK; teams_loop: LOOP FETCH CEKKARTUSTOK INTO PID_BARANG, PPANJANG; -- 先判断是否遍历完成,避免处理空数据 IF done THEN LEAVE teams_loop; END IF; -- 修正INSERT逻辑,补充有效值示例 IF PID_BARANG = 'X' THEN INSERT INTO kartustok(TAHUN,BULAN,ID_BARANG,KODE_GUDANG,MASUK) VALUES (PTAHUN, PBULAN, PID_BARANG, 'DEFAULT_GUDANG', PPANJANG); END IF; END LOOP teams_loop; CLOSE CEKKARTUSTOK; -- 修正SELECT语句,指定查询列与变量名 SELECT * FROM kartustok C WHERE C.TAHUN = PTAHUN AND C.BULAN = PBULAN AND C.ID_BARANG = PID_BARANG; END $$ DELIMITER ; -- 注意DELIMITER与;之间的空格
MySQL与Oracle存储过程核心语法差异
- 参数模式:MySQL必须显式指定
IN/OUT/INOUT(默认IN但建议显式声明),Oracle可省略默认IN; - 变量赋值:MySQL用
SET 变量 = 值,Oracle支持直接变量 = 值; - 分隔符处理:MySQL需要用
DELIMITER临时修改语句分隔符,避免存储过程内的;提前结束解析,Oracle无需此操作; - 命名规则:MySQL不允许变量与游标同名,Oracle无此限制;
- 日期函数:MySQL直接用
YEAR(date)/MONTH(date),Oracle需用EXTRACT(YEAR FROM date)或TO_CHAR(date, 'YYYY'); - 游标循环:MySQL支持
LOOP...END LOOP加标签,Oracle除LOOP外还支持FOR...IN游标循环; - 处理程序位置:MySQL的
DECLARE HANDLER必须放在所有变量/游标声明之后,Oracle的异常处理位置更灵活。
内容的提问来源于stack exchange,提问作者Kevin Elia
相关产品推荐
相关产品推荐

