SQL99语法新特性
先介绍几个SQL99里相当实用的小特性,在日常查询中能够大幅简化操作,值得掌握。
- NATURAL JOIN(自然连接):它能够自动匹配两张表中所有同名字段,并完成等值连接。简单来说,就是省去了手动编写多个 ON 条件的麻烦。
select a.dept_id,b.dept_id from A a NATURAL JOIN B b
- USING:当两张表存在相同字段名(例如都包含 dept_id)时,使用 USING 指定该字段即可,语法更加简洁明了。
select a.dept_id,b.dept_id form A a join A b USING(dept_id)
- match() Against() 全文检索
- 需要查询的字段必须事先创建为 fulltext 索引。
- match 默认不支持中文检索,因此添加索引时需要加上解析器:
WITH PARSER ngram。 - 传统的 LIKE 在这种场景下容易导致索引失效,全文检索的效率和准确性更高。
- 可以通过
SHOW VARIABLES LIKE 'ngram_token_size'查看分词方式,也支持修改配置。
SELECT id_, pat_name, id_card_no, phone_ FROM `inspect_meal_appointment_record` WHERE MATCH ( pat_name ) Against ('张三' );
CREATE FULLTEXT INDEX ind ON inspect_meal_appointment_record (pat_name) WITH PARSER ngram;


基本函数
MySQL 内置的基本函数涵盖范围很广,从字符串处理到日期计算应有尽有。下面这张图汇总了大部分常用函数,建议收藏作为速查手册。

循环
在存储过程中,循环是必不可少的控制结构。这里通过存储过程来演示三种循环方式:REPEAT、WHILE 和 LOOP,以及跳出循环所用的 LEAVE 和继续下一次循环的 ITERATE。
REPEAT 循环

CREATE DEFINER=`root`@`localhost` PROCEDURE `repeatTest`()
BEGIN
DECLARE nums INT DEFAULT 1;
REPEAT
SET nums = nums + 1;
UNTIL nums >= 10
END REPEAT;
SELECT nums;
END
WHILE 循环

CREATE DEFINER=`root`@`localhost` PROCEDURE `whileTest`()
BEGIN
DECLARE nums INT DEFAULT 1;
WHILE nums <= 10 DO
SET nums = nums + 1;
END WHILE;
SELECT nums;
END
LOOP 循环

CREATE DEFINER=`root`@`localhost` PROCEDURE `loopTest`()
BEGIN
DECLARE nums INT DEFAULT 1;
test_label: LOOP
SET nums = nums + 1;
IF nums >= 110 THEN
LEA VE test_label;
END IF;
END LOOP test_label;
SELECT nums;
END
LEAVE 跳出循环
LEAVE 既可以用于循环中,也能用在单独的 BEGIN…END 块内。下面的例子展示了在 WHILE 循环里使用 LEAVE 提前终止循环。

CREATE DEFINER=`root`@`localhost` PROCEDURE `lea veTest`()
BEGIN
DECLARE nums INT DEFAULT 1;
while_label: WHILE TRUE DO
IF nums > 10 THEN
LEA VE while_label;
END IF;
SET nums = nums + 1;
END WHILE;
SELECT nums;
END
ITERATE 执行下一次循环
ITERATE 相当于 Java 中的 continue,只能用于循环内部。下面这个 LOOP 循环中,当 nums < 10 时直接跳转到下一次迭代,只有 nums 在 10~15 之间才会累加并继续执行。

CREATE DEFINER=`root`@`localhost` PROCEDURE `ITERATETest`()
BEGIN
DECLARE nums INT DEFAULT 1;
test_label: LOOP
SET nums = nums + 1;
IF nums < 10 THEN
ITERATE test_label;
ELSEIF nums > 15 THEN
LEA VE test_label;
END IF;
END LOOP test_label;
SELECT nums;
END
游标
游标在存储过程中用于逐条处理查询结果集。下面是一个实际数据迁移时用到的示例:遍历分组后的结果,对每个套餐 ID 更新对应的类型标签表。
CREATE DEFINER=`inspect-v2-develop`@`%` PROCEDURE `updateTypeLabel`()
COMMENT '数据迁移,储存过程'
BEGIN
DECLARE s INT DEFAULT 0;
DECLARE mealId VARCHAR(64);
DECLARE num INT DEFAULT 0;
DECLARE list CURSOR FOR
SELECT deploy_meal_id, COUNT(deploy_meal_id)
FROM inspect_meal_deploy
WHERE is_deleted = '0'
GROUP BY deploy_meal_id;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET s = 1;
OPEN list;
FETCH list INTO mealId, num;
WHILE s <> 1 DO
IF num >= 1 THEN
UPDATE inspect_meal_type_label
SET meal_deploy_id = (
SELECT id_ FROM inspect_meal_deploy
WHERE is_deleted = '0' AND deploy_meal_id = mealId
LIMIT 1
)
WHERE meal_id = mealId AND is_deleted = '0';
END IF;
FETCH list INTO mealId, num;
END WHILE;
CLOSE list;
END
这里关键的一行是 DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET s = 1;,它声明了一个“句柄”,类似于存储过程中的异常处理器。CONTINUE 表示触发句柄后继续执行后续语句(而不是像 EXIT 那样直接退出最近的 BEGIN…END 块)。SQLSTATE '02000' 对应的是“未找到记录”,即当游标遍历完所有行后,会执行 SET s = 1,进而让 WHILE 条件不再满足,正常结束循环。
注意:句柄的声明必须放在游标之后,否则会报错 ERROR 1338 (42000): Cursor declaration after handler declaration,顺序不能颠倒。
总结
SQL99 语法中的 NATURAL JOIN、USING 以及全文检索能够使查询更加简洁高效;MySQL 的循环与游标则是存储过程中处理复杂业务逻辑的利器。熟练掌握这些基础用法,日常编写存储过程将更加得心应手。
