简体   繁体   中英

Update table inside a loop in a procedure pl/sql

So i want to hire a new manager for a department. This procedure has 2 parameters, the department name i want to change, and the new id for manager (taken from employee's id). So to change it, i need to update all employee that has the old manager id, and change their manager id into the new one. This is my code so far, the problem is that it updated all of the employees' manager, so the whole database is updated. I must use procedure, not function. Any idea? Thanks.

CREATE OR REPLACE PROCEDURE update_manager(v_deptname IN departments.department_name%TYPE,v_empid IN employees.employee_id%TYPE) IS
v_deptid departments.department_id%type;
BEGIN
SELECT department_id INTO v_deptid FROM departments WHERE department_name=v_deptname;
    FOR i IN (SELECT * FROM employees)
    LOOP
        IF i.department_id=v_deptid THEN
            UPDATE employees
            SET manager_id=v_empid
            WHERE i.department_id=v_deptid;
        END IF;
    END LOOP;
END;
/
BEGIN
update_manager('Marketing',100);
END;
/

I don't have your tables, but I do have Scott's so - here's an example.

Departments and employees in department 10:

SQL> select * From dept order by deptno;

    DEPTNO DNAME          LOC
---------- -------------- -------------
        10 ACCOUNTING     NEW YORK
        20 RESEARCH       DALLAS
        30 SALES          CHICAGO
        40 OPERATIONS     BOSTON

SQL> select deptno, empno, ename, mgr
  2  from emp
  3  where deptno = 10;

    DEPTNO      EMPNO ENAME             MGR
---------- ---------- ---------- ----------
        10       7782 CLARK            7839
        10       7839 KING
        10       7934 MILLER           7782

Procedure that looks like yours (as you're studying PL/SQL), using a variable to fetch department number into, as well as a loop:

SQL> create or replace procedure update_manager
  2    (v_deptname in dept.dname%type,
  3     v_empno    in emp.empno%type
  4    )
  5  is
  6    v_deptno dept.deptno%type;
  7  begin
  8    select d.deptno
  9      into v_deptno
 10      from dept d
 11      where d.dname = v_deptname;      --> this is what you are missing
 12
 13    for cur_r in (select e.empno
 14                  from emp e
 15                  where e.deptno = v_deptno
 16                 )
 17    loop
 18      update emp a set
 19        a.mgr = v_empno
 20        where a.empno = cur_r.empno;
 21    end loop;
 22  end;
 23  /

Procedure created.

Testing: let's modify manager of all employees in department 10:

SQL> exec update_manager ('ACCOUNTING', 7839);

PL/SQL procedure successfully completed.

SQL> select deptno, empno, ename, mgr
  2  from emp
  3  where deptno = 10;

    DEPTNO      EMPNO ENAME             MGR
---------- ---------- ---------- ----------
        10       7782 CLARK            7839
        10       7839 KING             7839
        10       7934 MILLER           7839

SQL>

Seems to work. Though, that's inefficient; doing it row-by-row (in a loop) is usually slow-by-slow. Work on the whole sets of data, rather. Something like this:

SQL> rollback;

Rollback complete.

SQL> create or replace procedure update_manager
  2    (v_deptname in dept.dname%type,
  3     v_empno    in emp.empno%type
  4    )
  5  is
  6  begin
  7    update emp e set
  8      e.mgr = v_empno
  9      where e.deptno = (select d.deptno
 10                        from dept d
 11                        where d.dname = v_deptname
 12                       );
 13  end;
 14  /

Procedure created.

SQL> exec update_manager ('ACCOUNTING', 7782);

PL/SQL procedure successfully completed.

SQL> select deptno, empno, ename, mgr
  2  from emp
  3  where deptno = 10;

    DEPTNO      EMPNO ENAME             MGR
---------- ---------- ---------- ----------
        10       7782 CLARK            7782
        10       7839 KING             7782
        10       7934 MILLER           7782

SQL>

If you must create a PL/SQL procedure then you can do

CREATE OR REPLACE PROCEDURE update_manager(v_deptname IN departments.department_name%TYPE,v_empid IN employees.employee_id%TYPE) IS
  v_deptid departments.department_id%type;
BEGIN
  SELECT department_id INTO v_deptid FROM departments WHERE department_name=v_deptname;
  UPDATE employees
  SET    manager_id=v_empid
  WHERE  department_id=v_deptid;
END;
/

The problem with your code that's causing "it updated all of the employees' manager" is that your update statement:

UPDATE employees
SET manager_id=v_empid
WHERE i.department_id=v_deptid;

Your filter here is comparing i.department_id , this is the variable that's coming from your FOR i IN (SELECT * FROM employees) , NOT from the update statement. You've already confirmed that i.department_id=v_deptid because you are calling this in a loop with an if statement checking it.

It is not efficient at all to get all rows in employees, loop the results, checking each row if it matches a condition and then firing off an update statement (even if your update statement is filtering against the correct row.

Why not a simple select for manager id and an update on all concerned employee ?

DECLARE @department_id uniqueidentifier

SELECT @department_id = departement_id
FROM departments 
WHERE department_name = @v_deptname

UPDATE employees
SET manager_id = @v_empid
WHERE department_id = @department_id

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM