2012-02-07 42 views
1

当我执行这个PL/SQL块时,我总是收到“找不到数据”。如果我注释掉输出行,那么所有的输出都会像我所怀疑的那样输出,但是更新语句从不执行。有任何想法吗?Oracle PL/SQL选择和更新

set serveroutput on; 
Declare 
    TYPE type_emp IS RECORD(
     emp_salary emp.salary%TYPE); 
    rec_emp type_emp; 
    v_stars varchar2(50) ; 
    v_count number(3); 
    s_num number(3); 
begin 
SELECT min(employee_id) into s_num from emp; 
SELECT count(*) into v_count from emp; 
v_count := v_count + s_num; 
for i in s_num .. v_count loop 
    SELECT salary 
    into rec_emp 
    from emp 
    where employee_id = i; 
    if rec_emp.emp_salary <1000 then 
     v_stars := null; 
     elsif rec_emp.emp_salary >=1000 and rec_emp.emp_salary <2000 then 
     v_stars := '*'; 
     elsif rec_emp.emp_salary >=2000 and rec_emp.emp_salary <3000 then 
     v_stars := '**'; 
     elsif rec_emp.emp_salary >=3000 and rec_emp.emp_salary <4000 then 
     v_stars := '***'; 
     elsif rec_emp.emp_salary >=4000 and rec_emp.emp_salary <5000 then 
     v_stars := '****'; 
     elsif rec_emp.emp_salary >=5000 and rec_emp.emp_salary <6000 then 
     v_stars := '*****'; 
     elsif rec_emp.emp_salary >=6000 and rec_emp.emp_salary <7000 then 
     v_stars := '******'; 
     elsif rec_emp.emp_salary >=7000 and rec_emp.emp_salary <8000 then 
     v_stars := '*******'; 
     elsif rec_emp.emp_salary >=8000 and rec_emp.emp_salary <9000 then 
     v_stars := '********'; 
     elsif rec_emp.emp_salary >=9000 and rec_emp.emp_salary <10000 then 
     v_stars := '*********'; 
     elsif rec_emp.emp_salary >=10000 and rec_emp.emp_salary <11000 then 
     v_stars := '**********'; 
     elsif rec_emp.emp_salary >=11000 and rec_emp.emp_salary <12000 then 
     v_stars := '***********'; 
     elsif rec_emp.emp_salary >=12000 then 
     v_stars := '************'; 
    end if; 
    --dbms_output.put_line(rec_emp.emp_salary || ' ' || i || '  '|| v_stars); 
    update emp set emp.stars = v_stars where employee_id = i; 
end loop; 
end; 
+4

除了杰弗里的答案,你可以简化所有的'elsif'语句是这样的:'如果rec_emp。 emp_salary <1000 then v_stars:= null;否则v_stars:= lpad('*',至少(floor(rec_emp.emp_salary/1000),12),'*');结束如果;'。 – 2012-02-07 03:42:04

回答

8

您正在使用非常不寻常的方法遍历表中的所有记录。您的方法假定员工ID是连续的(即没有差距)。它也有一个错误的错误。

比方说,你的表有以下行:

100 
101 
102 
103 
104 

首先你会得到最低的员工ID:s_num = 100

然后,您将得到记录数:v_count = 5

最后,添加这些结合在一起,以获得上限:s_num + v_count = 105

你可以看到现在你的循环问题?在最后一次迭代,查询:

SELECT salary 
into rec_emp 
from emp 
where employee_id = i; 

会找员工ID 105,这引起了NO_DATA_FOUND。 DBMS_OUTPUT调用已经完成,所以你会看到输出;但未处理的异常会导致回滚,这意味着您的UPDATE被撤消。

相反,你可以让你的循环简单得多,例如:

FOR rec_emp IN (SELECT employee_id, salary AS emp_salary FROM emp) LOOP 
    if rec_emp.emp_salary <1000 then 
    v_stars := null; 
    elsif <snip> 
    ... 
    end if; 
    --dbms_output.put_line(rec_emp.emp_salary || ' ' || i || '  '|| v_stars); 
    update emp set emp.stars = v_stars where employee_id = i; 
END LOOP; 

上面的代码会更有效了。它可以变得更有效率,但我不希望这个答案太过于你迄今为止所做的。

我希望这会有所帮助。

+0

谢谢你的好解释。我最终将select语句的SELECT MAX(employee_id)从emp中更改为v_count; – atrueresistance 2012-02-07 16:22:08

1

@Jeffrey Kemp的回答完全正确。此外,可以简化,这归因于单个语句(如在通过@约翰·道尔的评论暗示):

UPDATE emp 
SET emp.stars = 
      CASE WHEN rec_emp.emp_salary < 1000 
      THEN NULL 
      ELSE LPAD('*', LEAST(FLOOR(rec_emp.emp_salary/1000), 12), '*') END