SQL Server用WHILE+临时表模拟游标需手动维护序号,MySQL必须用CURSOR并配CONTINUE HANDLER,Oracle推荐BULK COLLECT+集合分批处理;所有数据库均应避免循环中修改源表。

SQL Server 用 WHILE + 临时表模拟游标,必须手动维护序号
WHILE 本身不支持“遍历结果集”,它只判断布尔表达式。想逐行处理查询结果,得先落地成临时表,再靠自增序号 + 计数器驱动循环。
常见错误是写 WHILE EXISTS(SELECT ...) 却没更新变量,导致死循环或只处理第一行。
- 先建带序号的临时表:
SELECT ROW_NUMBER() OVER (ORDER BY id) AS rn, col1, col2 INTO #temp FROM source_table - 用
SET @i = 1初始化计数器,SELECT @total = COUNT(*) FROM #temp获取总行数 - 循环体内必须用
SELECT @col1 = col1, @col2 = col2 FROM #temp WHERE rn = @i提取当前行——漏掉WHERE就会查全表 - 每次循环末尾必须
SET @i = @i + 1,否则条件永远为真 - 临时表数据超 5000 行且无索引时,
ROW_NUMBER()性能明显下降;建议建聚集索引:CREATE CLUSTERED INDEX IX_temp_rn ON #temp(rn)
MySQL 存储过程必须用 CURSOR,且需显式声明 CONTINUE HANDLER
MySQL 不支持 WHILE 直接配合 SELECT 遍历,必须走游标路线。但游标默认遇到 EOF 就报错,不加异常处理会中断执行。
典型错误是只声明游标、OPEN、FETCH,却没设 DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE,导致第一次 FETCH 后就退出。
- 变量声明顺序很重要:先
DECLARE done INT DEFAULT FALSE,再DECLARE cur CURSOR FOR SELECT ...,最后DECLARE CONTINUE HANDLER ... -
FETCH cur INTO @var1, @var2必须放在WHILE NOT done DO循环内,不能提前 - 避免在循环中反复
INSERT INTO ... VALUES(...);大数据量时改用拼接字符串+PREPARE/EXECUTE或临时表批量插入 - 如果查询字段含表达式(如
UPPER(name)),游标声明里也要保持一致,否则类型隐式转换可能引发截断或匹配失败
Oracle 用 BULK COLLECT + 集合遍历动态 SQL 结果
Oracle 的隐式游标不支持动态 SQL,但 EXECUTE IMMEDIATE ... BULK COLLECT INTO 可以把动态查询结果一次性载入 PL/SQL 集合,再用 FOR i IN ... LOOP 遍历。
关键限制是:集合类型必须与查询字段结构严格匹配,字段名、数量、类型都不能错——哪怕只是别名不同,BULK COLLECT 就会报 ORA-06550。
- 若动态 SQL 字段不确定,先建一个结构固定的临时表(如
CREATE GLOBAL TEMPORARY TABLE temp_result (id NUMBER, val VARCHAR2(100))),再用EXECUTE IMMEDIATE 'INSERT INTO temp_result ...'落地,最后查这个表 - 集合大小受内存限制,单次
BULK COLLECT超过 10 万行易触发 ORA-04030;应分批处理:LIMIT 10000+ 循环 -
EXECUTE IMMEDIATE中的变量要拼进字符串,注意引号嵌套和 SQL 注入风险;优先用绑定变量:USING v_param
所有数据库都该避开的坑:循环中修改被遍历的源表
无论用临时表、游标还是集合,在循环体里直接 UPDATE 或 DELETE 正在遍历的表,都会破坏行定位逻辑——序号偏移、游标跳行、集合下标越界都可能发生。
最隐蔽的问题是:测试数据少时正常,上线后数据量一上去就漏处理或重复处理。
- 安全做法是把待处理 ID 先抽到独立临时表(如
#todo),循环只读它,操作作用于原表 - 若必须边查边删,用
TOP 1 ... ORDER BY id查最小未处理 ID,删完再重新查下一个,避免依赖物理位置 - 不要在 WHILE 循环里反复
INSERT INTO #temp;改为先拼好所有值,最后INSERT INTO #temp SELECT ... UNION ALL SELECT ...

















