You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MySQL Workbench创建Procedure报错求助:1064语法错误排查

修复MySQL存储过程的语法错误

我帮你仔细排查了这个MySQL存储过程里的语法问题,大多是因为把SQL Server(T-SQL)的习惯带到MySQL里导致的,下面是详细的错误分析和修复后的完整代码:

主要错误点梳理

  • 变量声明位置错误:MySQL要求所有变量声明必须放在BEGIN块的最开头,不能在代码逻辑中途(比如while循环里)声明变量
  • 临时表命名错误:MySQL不支持#前缀的临时表,要用CREATE TEMPORARY TABLE esubmit(...)的标准语法
  • 字符串拼接错误:MySQL没有+=拼接运算符,必须用CONCAT()或CONCAT_WS()函数实现字符串拼接
  • 游标未定义直接使用:你用到了游标c和c1但没有提前声明、定义,也没有处理游标结束的NOT FOUND事件
  • 变量声明语法错误:多个同类型变量声明可以用逗号分隔,但要统一格式;不同类型变量要分开声明
  • 动态SQL的变量作用域问题:局部变量不能直接用于PREPARE语句,需要用会话级变量(带@前缀)或者调整逻辑
  • 分隔符设置缺失:创建存储过程时需要先修改语句分隔符(比如DELIMITER $$),避免和语句内的分号冲突

修复后的完整存储过程代码

DELIMITER $$

DROP PROCEDURE IF EXISTS test_mysql_while_loop$$
DROP TEMPORARY TABLE IF EXISTS esubmit$$

CREATE PROCEDURE test_mysql_while_loop()
BEGIN
    -- 所有变量声明必须放在BEGIN块的最开头
    DECLARE v_startdate VARCHAR(45);
    DECLARE v_enddate VARCHAR(45);
    DECLARE v_sql LONGTEXT;
    DECLARE v_status INT;
    DECLARE v_count INT;
    DECLARE v_cid INT;
    DECLARE v_eid INT;
    DECLARE v_setdate VARCHAR(45);
    DECLARE v_setcnt INT;
    DECLARE v_getname VARCHAR(45);
    DECLARE done INT DEFAULT FALSE;
    DECLARE done_c1 INT DEFAULT FALSE;
    
    -- 初始化变量
    SET v_startdate = '2018-05-22';
    SET v_enddate = '2018-05-28';
    SET v_status = 0;
    SET v_count = 1;
    
    -- 创建符合MySQL规范的临时表
    CREATE TEMPORARY TABLE esubmit(
        id INT AUTO_INCREMENT PRIMARY KEY,
        Name VARCHAR(100)
    );
    
    -- 动态拼接添加日期列的SQL语句
    SET v_sql = 'ALTER TABLE esubmit ADD ';
    WHILE v_status != 1 DO
        SET @current_date = DATE_ADD(v_startdate, INTERVAL v_count DAY);
        IF @current_date = v_enddate THEN
            SET v_sql = CONCAT(v_sql, '`', @current_date, '` VARCHAR(45)');
            SET v_status = 1;
        ELSE
            SET v_sql = CONCAT(v_sql, '`', @current_date, '` VARCHAR(45), ');
        END IF;
        SET v_count = v_count + 1;
    END WHILE;
    
    -- 执行动态SQL添加日期列
    PREPARE stmt FROM v_sql;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
    
    -- 声明游标c:遍历work与productdata的关联数据(可根据实际需求调整查询逻辑)
    DECLARE c CURSOR FOR
        SELECT w.prid, w.pid
        FROM work w
        JOIN productdata pd ON w.prid = pd.prid;
    
    -- 游标结束的处理逻辑
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    
    OPEN c;
    read_main_loop: LOOP
        FETCH c INTO v_cid, v_eid;
        IF done THEN
            LEAVE read_main_loop;
        END IF;
        
        -- 获取产品名称
        SELECT prname INTO v_getname FROM productdata WHERE prid = v_cid;
        
        -- 先插入产品名称到临时表
        INSERT INTO esubmit(Name) VALUES(v_getname);
        
        -- 声明游标c1:遍历日期范围内的所有日期
        DECLARE c1 CURSOR FOR
            SELECT DATE_ADD(v_startdate, INTERVAL n DAY)
            FROM (
                SELECT 1 n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6
            ) nums
            WHERE DATE_ADD(v_startdate, INTERVAL n DAY) <= v_enddate;
        
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET done_c1 = TRUE;
        
        OPEN c1;
        date_loop: LOOP
            FETCH c1 INTO v_setdate;
            IF done_c1 THEN
                LEAVE date_loop;
            END IF;
            
            -- 查询指定日期的统计数量
            SELECT COUNT(pid) INTO v_setcnt
            FROM pr_status ps
            WHERE ps.pid = v_eid
              AND DATE(ps.statusdate) = v_setdate
              AND ps.stage BETWEEN '4' AND '5';
            
            -- 更新临时表对应的日期列值
            SET @update_sql = CONCAT('UPDATE esubmit SET `', v_setdate, '` = ', IFNULL(v_setcnt, 0), ' WHERE Name = ''', v_getname, '''');
            PREPARE stmt FROM @update_sql;
            EXECUTE stmt;
            DEALLOCATE PREPARE stmt;
        END LOOP date_loop;
        CLOSE c1;
        -- 重置游标c1的结束状态
        SET done_c1 = FALSE;
    END LOOP read_main_loop;
    CLOSE c;
    
END$$

DELIMITER ;

额外说明

  1. 我调整了原代码中插入数据的逻辑,改为先插入产品名称,再逐个更新日期列的统计值,这更符合MySQL的语法规范
  2. 日期范围生成用了子查询生成数字序列,如果你需要支持更长的日期范围,可以扩展这个子查询,或者用MySQL 8.0+支持的递归CTE
  3. 处理了COUNT()返回NULL的情况,用IFNULL()将空值转为0,避免数据显示异常
  4. 游标c的查询逻辑是基于你的表结构推测的,你可以根据实际业务需求修改这个游标查询

内容的提问来源于stack exchange,提问作者Ganesan Perumalsamy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:08:44