作者简介:黄开祥,瑞蓝创数据库工程师
原创内容未经授权不得随意使用,转载请联系小编并注明来源
在使用 OceanBase MySQL 租户开发存储过程、函数或触发器时,你大概率会碰到这样的尴尬:创建语句里明明有分号,但客户端却把它当成了语句的结束符,直接把不完整的代码发给了数据库,然后抛出一堆语法错误。DELIMITER 就是为解决这个场景而生的。
1. 为什么需要 DELIMITER?
默认情况下,MySQL 客户端(以及 OBClient)使用分号(;) 作为 SQL 语句的结束符。当你输入:
SELECT 1;
SELECT 2;
客户端会识别到两个语句,分别发送并执行。
但创建一个存储过程时,体内部往往包含多条用分号结尾的语句:
CREATE PROCEDURE sp_test()
BEGIN
DECLARE a INT DEFAULT 0;
SET a = a + 1;
SELECT a;
END;
如果直接粘贴执行,客户端看到第一个分号就认为语句结束了,于是会把发给服务器,自然报错。
DELIMITER 的作用就是临时通知客户端:“从现在起,不要再用分号作为语句结束符,改用我指定的符号(比如 // 或 $$)。” 这样一来,分号就可以安全地出现在存储过程体内,整个定义作为一个完整的语句提交。
DELIMITER 是客户端命令,不是 SQL 语句,服务器端完全不认识它。
2. 基本语法
DELIMITER
-
可以是任意字符序列,常见的有 //、$$、;; -
执行 DELIMITER ;可以将分隔符恢复为默认的分号。
用法模板:
DELIMITER //
CREATE PROCEDURE ...
BEGIN
...;
...;
END //
DELIMITER ;
三个关键点:
-
先改分隔符在创建对象之前执行DELIMITER -
用新分隔符结束定义:在之后用新分隔符(如 //)标记整个语句结束。 -
恢复分隔符:定义完成后务必执行DELIMITER ;,否则后续普通 SQL 都会因缺少分号而“卡住”。
3. OceanBase MySQL 租户实战案例
环境说明:本文示例基于 OceanBase 4.x MySQL 租户,客户端为 OBClient 或标准 MySQL 命令行客户端。
3.1 创建存储过程
-- 切换分隔符为 //
DELIMITER //
CREATE PROCEDURE calc_bonus(IN emp_id INT, OUT bonus DECIMAL(10,2))
BEGIN
DECLARE salary DECIMAL(10,2);
SELECT base_salary INTO salary FROM employees WHERE id = emp_id;
IF salary > 10000 THEN
SET bonus = salary * 0.2;
ELSE
SET bonus = salary * 0.1;
END IF;
END //
-- 恢复分隔符
DELIMITER ;
-- 调用存储过程
CALL calc_bonus(101, @b);
SELECT @b;
3.2 创建函数
DELIMITER $$
CREATE FUNCTION get_grade(score INT) RETURNS VARCHAR(10)
READS SQL DATA
BEGIN
DECLARE grade VARCHAR(10);
IF score >= 90 THEN SET grade = '优秀';
ELSEIF score >= 75 THEN SET grade = '良好';
ELSEIF score >= 60 THEN SET grade = '及格';
ELSE SET grade = '不及格';
END IF;
RETURN grade;
END $$
DELIMITER ;
-- 使用函数
SELECT get_grade(85);
3.3 创建触发器
DELIMITER //
CREATE TRIGGER update_log BEFORE UPDATE ON accounts
FOR EACH ROW
BEGIN
INSERT INTO account_logs(account_id, old_amount, new_amount, change_time)
VALUES(OLD.id, OLD.amount, NEW.amount, NOW());
END //
DELIMITER ;
3.4 批量定义多个对象
在一个 SQL 脚本中连续创建多个例程时,只需在开头修改一次分隔符,最后恢复即可:
DELIMITER $$
CREATE PROCEDURE p1() BEGIN ... END$$
CREATE FUNCTION f1() RETURNS INT BEGIN ... END$$
CREATE TRIGGER trg1 ... END$$
DELIMITER ;
每个定义结束都要用新分隔符 ,否则客户端仍会把它们看作一个未完成的语句。
4. 常见错误 & 避坑指南
坑 1:DELIMITER 后忘记空格
DELIMITER// -- 错误,客户端无法识别
DELIMITER // -- 正确
关键字和分隔符之间必须至少有一个空格(或 Tab),否则会被当成一个非法命令。
坑 2:忘记恢复分隔符为
DELIMITER //
CREATE PROCEDURE demo() BEGIN SELECT 1; END //
-- 此后所有普通语句都需要用 // 结束
SELECT NOW() // -- 如果不小心用了分号,会卡住
一旦忘了 DELIMITER ;,后续的 SELECT、INSERT 等语句只能识别新分隔符。养成在每个定义脚本末尾写上 DELIMITER ; 的好习惯。
坑 3:在存储过程体内包含 DELIMITER 命令
DELIMITER //
CREATE PROCEDURE wrong()
BEGIN
DELIMITER ; -- 毫无意义且会破坏客户端解析
SELECT 1;
END //
DELIMITER ;
DELIMITER是给客户端看的,不是 SQL 的一部分。写在过程体内不会被执行,反而会扰乱客户端的解析逻辑,甚至导致脚本中断。
5. OceanBase 特别说明
-
OceanBase MySQL 租户完全兼容 MySQL 5.7/8.0 的存储过程、函数、触发器语法,因此 DELIMITER 的用法与标准 MySQL 一模一样。 -
OceanBase 的 MySQL 模式 PL 引擎在处理复杂逻辑时性能优异,结合 DELIMITER 可以轻松创建包含多条控制流语句的例程。 -
如果在 OBClient 中执行一个包含 DELIMITER 的 SQL 文件( source file.sql ),OBClient 会逐行解析并识别分隔符切换,与原生 mysql 客户端行为一致。
6. 总结
DELIMITER 本质上是一个客户端专用的小工具,它帮你跨越“语句内分号”的陷阱,让完整的 DDL 定义准确无误地提交到数据库。在 OceanBase MySQL 租户下,掌握它只需要记住三点:
-
先在 OBClient 中用 DELIMITER 改成非分号符号(如 // ); -
用新符号结束每个定义; -
最后恢复分号。
END
瑞蓝创 OceanBase OBCP V4 精英训练营
点击下方图片立即了解详情


▼ 点击「阅读原文」,了解更多产品技术文章


