MySQL存储过程要求DECLARE必须紧接BEGIN后且为第一条语句,变量需用SET或SELECT…INTO赋值,不可用:=,未初始化变量为NULL,局部变量与会话变量独立不共享。

必须在 BEGIN 后立刻写 DECLARE,否则整个存储过程直接报错 ERROR 1064
DECLARE 必须紧贴 BEGIN,中间不能插任何语句
MySQL 对 DECLARE 的位置极其敏感:它必须是 BEGIN 之后的第一条语句,哪怕前面只有一行注释、一个空行或 SET,都会触发 ERROR 1064 (42000)。这不是警告,是语法拒绝解析。
- ✅ 正确顺序:
BEGIN→DECLARE v_id INT DEFAULT 0;→ 其他逻辑 - ❌ 错误写法:
BEGIN→-- 初始化前检查→DECLARE ...(报错) - ❌ 错误写法:
BEGIN→SET @flag = 1;→DECLARE ...(报错) - 多个变量可合并声明:
DECLARE v_a INT DEFAULT 0, v_b VARCHAR(32), v_c DATETIME DEFAULT NOW();
局部变量只能用 SET 或 SELECT … INTO 赋值,不能用 :=
局部变量没有 @ 前缀,赋值时若误用 :=(用户变量语法),MySQL 会报 ERROR 1337 (42000): Variable 'v_name' is not declared——因为它根本没识别出这是变量赋值,而是当成非法表达式。
- ✅ 正确赋值:
SET v_count = 100;或SELECT COUNT(*) INTO v_count FROM orders WHERE status = 'paid'; - ❌ 错误写法:
SET v_count := 100;(语法错误) - ❌ 错误写法:
SELECT COUNT(*) INTO @v_count ...(@v_count 是会话变量,不是你声明的局部变量) -
SELECT ... INTO要求结果严格单行单列,否则报ERROR 1172 (42000): Result consisted of more than one row
未初始化的局部变量值为 NULL,参与计算不会自动转 0
声明时没写 DEFAULT,变量值就是 NULL。一旦参与算术或字符串操作,结果大概率还是 NULL,而不是你预期的“0”或空字符串——这在条件判断和聚合逻辑里极易埋雷。
- 例如:
DECLARE v_total DECIMAL(10,2); SET v_total = v_total + 100.00;→v_total最终是NULL,不是100.00 - 安全写法:
DECLARE v_total DECIMAL(10,2) DEFAULT 0.00;或SET v_total = IFNULL(v_total, 0) + 100.00; - 注意:
DEFAULT NOW()是合法的,但DEFAULT (SELECT MAX(id) FROM t)会语法报错,标量子查询只能出现在SET或SELECT INTO中
别和 @name 会话变量混用,名字相同也不共享值
局部变量 v_id 和会话变量 @v_id 完全独立。漏写 @ 可能让你以为在读会话变量,实际却在访问未声明的局部变量,直接报错;反之,在存储过程里改了 @v_id,外面会话能立刻看到,但 v_id 不受影响。
- 并发调用时,
v_id是每个过程实例私有的,@v_id是整个会话共享的 —— 多个连接同时执行同一过程,@v_id会被覆盖乱掉 - 调试时想查值?
SELECT v_id;行,SELECT @v_id;不行(除非你真设过它) - 常见静默陷阱:
SET v_id = @v_id + 1;如果v_id没声明,整句非法;如果@v_id是 NULL,结果也是 NULL,而非 1
最易被忽略的一点:DECLARE 的 DEFAULT 值里允许写 NOW() 这类函数,但它的执行时机是「每次进入 BEGIN…END 块时」,不是定义时;而子查询永远不能放 DEFAULT 里——这个限制不报运行时错,是硬性语法拦截,得提前知道。


















