PL/SQL存储过程陷入无限循环,求排查及部门低薪员工实现方案
问题描述
现有一个7列的员工表emple,存储员工信息,已有大量数据且持续新增。需要编写PL/SQL存储过程,查询每个部门薪资最低的2名员工(支持新增部门),但当前编写的存储过程陷入无限循环,代码如下:
create or replace procedure lowemployees as cursor cur1 is select * from emple order by dept_no,salario; empleadus emple%rowtype; i number; depcursor emple.dept_no%type; depcompare emple.dept_no%type; begin i:=0; open cur1; fetch cur1 into empleadus; depcursor:=empleadus.dept_no; depcompare:=depcursor+1; while cur1%found loop while i<2 loop dbms_output.put_line(empleadus.emp_no||'|'||empleadus.nombre||'|'||empleadus.oficio||'|'||empleadus.salario||'|'||empleadus.dept_no); i:=i+1; fetch cur1 into empleadus; end loop; depcursor:=empleadus.dept_no; depcompare:=depcursor+1; while depcursor<depcompare loop fetch cur1 into empleadus; i:=0; depcursor:=empleadus.dept_no; end loop; dbms_output.put_line(empleadus.emp_no||'|'||empleadus.nombre||'|'||empleadus.oficio||'|'||empleadus.salario||'|'||empleadus.dept_no); depcompare:=depcursor+1; fetch cur1 into empleadus; end loop; close cur1; end lowemployees;
问题分析
无限循环的核心原因是第二个while depcursor<depcompare逻辑完全错误:
depcompare被设为depcursor+1,只要当前部门编号不是最大值,depcursor<depcompare永远为真,会持续fetch直到游标耗尽,且外层循环未正确判断游标状态,导致逻辑混乱。- 外层循环在处理完每个部门后额外输出数据并重复
fetch,逻辑冗余且破坏了游标遍历的连贯性。
解决方案
方案1:修复原存储过程逻辑
调整游标遍历逻辑,按部门分组取前2条薪资最低记录:
create or replace procedure lowemployees as cursor cur1 is select * from emple order by dept_no, salario; empleadus emple%rowtype; current_dept emple.dept_no%type; count_per_dept number; begin count_per_dept := 0; open cur1; fetch cur1 into empleadus; while cur1%found loop current_dept := empleadus.dept_no; count_per_dept := 0; -- 取当前部门前2名薪资最低的员工 while cur1%found and empleadus.dept_no = current_dept and count_per_dept < 2 loop dbms_output.put_line(empleadus.emp_no||'|'||empleadus.nombre||'|'||empleadus.oficio||'|'||empleadus.salario||'|'||empleadus.dept_no); count_per_dept := count_per_dept + 1; fetch cur1 into empleadus; end loop; -- 跳过当前部门剩余员工 while cur1%found and empleadus.dept_no = current_dept loop fetch cur1 into empleadus; end loop; end loop; close cur1; end lowemployees;
方案2:用SQL窗口函数实现(更高效)
针对大量数据,使用ROW_NUMBER()窗口函数可避免繁琐游标逻辑,性能更优且代码简洁:
纯SQL查询方式
SELECT emp_no, nombre, oficio, salario, dept_no FROM ( SELECT e.*, ROW_NUMBER() OVER (PARTITION BY dept_no ORDER BY salario) AS rn FROM emple e ) WHERE rn <= 2 ORDER BY dept_no, salario;
封装为存储过程
create or replace procedure lowemployees as begin FOR emp_rec IN ( SELECT emp_no, nombre, oficio, salario, dept_no FROM ( SELECT e.*, ROW_NUMBER() OVER (PARTITION BY dept_no ORDER BY salario) AS rn FROM emple e ) WHERE rn <= 2 ORDER BY dept_no, salario ) LOOP dbms_output.put_line(emp_rec.emp_no||'|'||emp_rec.nombre||'|'||emp_rec.oficio||'|'||emp_rec.salario||'|'||emp_rec.dept_no); END LOOP; end lowemployees;
内容的提问来源于stack exchange,提问作者Gaussjordan
相关产品推荐
相关产品推荐

