mysql存储过程必须用while、repeat或loop实现循环,for循环不被支持;while先判断后执行,repeat先执行后判断,loop需配合leave退出;所有循环均须在begin…end内定义变量并显式控制终止条件,否则易致死循环或语法错误。

SQL存储过程里不能直接写 for 或 while 像 Python 那样用,但所有主流数据库(MySQL、SQL Server、PostgreSQL、Oracle)都支持在存储过程中用控制结构实现循环——关键不是“能不能”,而是“用哪种结构、在哪种场景下更稳”。
WHILE 循环适合按计数批量操作
当你知道要处理多少次(比如插入 100 条测试数据、更新前 500 行),WHILE 最直观。它依赖一个变量和终止条件,逻辑清晰、调试方便。
- MySQL 中必须配合
DELIMITER修改语句结束符,否则;会让CREATE PROCEDURE提前中断 - SQL Server 的
WHILE不需要额外分隔符,但变量声明必须用DECLARE,且赋值用SET或SELECT - 别在循环体里漏掉变量自增,否则变成死循环——
SET @i = @i + 1忘了这句,过程会卡住甚至锁表 - 每次循环执行一次
UPDATE或INSERT,性能差;真要处理上万行,得考虑分批(比如每 100 行COMMIT一次)
游标(CURSOR)适合逐行读取结果集
当你需要遍历查询结果(比如“把所有 status=0 的用户发一封邮件通知”),就得用游标。它本质是把 SELECT 结果变成可逐条取值的指针。
- MySQL 游标必须配合
CONTINUE HANDLER FOR NOT FOUND捕获“取完了”的信号,否则FETCH失败后会直接报错退出 - SQL Server 用
@@FETCH_STATUS判断是否取到有效行,值为 0 才继续,不是布尔值 - 游标开销大:它会把结果集缓存在内存或临时表里,大数据量时可能拖慢整个过程,优先考虑能否改写成集合操作(比如用单条
UPDATE ... WHERE替代) - 记得配对使用
OPEN/CLOSE和DEALLOCATE,尤其在异常路径里漏掉CLOSE,可能导致后续调用失败
不同数据库的循环语法差异很实际
写一次存储过程想跨库复用?基本不可能。光是循环关键字和变量作用域就足够绊倒人。
- MySQL 支持
LOOP、WHILE、REPEAT,但LOOP必须用LEAVE显式跳出,没有break关键字 - PostgreSQL 的
FOR循环最像高级语言:FOR i IN 1..10 LOOP直接定义范围,还支持遍历查询结果:FOR rec IN SELECT * FROM t LOOP - Oracle 用
FOR i IN 1..10 LOOP,但变量不用DECLARE,直接在BEGIN后定义;而 SQL Server 不支持这种语法,只能靠WHILE+ 计数器硬扛 - PostgreSQL 的
DO $$ ... $$块能临时跑循环,但不能存为对象;MySQL 和 SQL Server 必须先CREATE PROCEDURE才能执行
真正容易被忽略的点:事务与错误处理
循环里出错,默认不会自动回滚——尤其当批量更新中途失败时,可能已改掉前几十行,剩下全挂了。
- MySQL 存储过程中加
DECLARE EXIT HANDLER FOR SQLEXCEPTION,否则异常一抛,过程就停,没机会清理资源 - SQL Server 推荐用
TRY...CATCH包住整个循环体,而不是只包单次操作 - 哪怕只是日志记录,也建议在循环开始前
START TRANSACTION,失败时ROLLBACK,成功后COMMIT——别依赖默认自动提交 - 别在循环里频繁查表(比如每次循环都
SELECT COUNT(*)),提前算好总数或用变量缓存,否则 I/O 压力会指数级上升










