【问题标题】:Update table inside a loop in a procedure pl/sql在过程 pl/sql 中的循环内更新表
【发布时间】:2020-11-01 14:44:04
【问题描述】:

所以我想为一个部门聘请一位新经理。此过程有 2 个参数,我要更改的部门名称和经理的新 id(取自员工的 id)。所以要更改它,我需要更新所有具有旧经理 ID 的员工,并将他们的经理 ID 更改为新的。这是我到目前为止的代码,问题是它更新了所有员工的经理,所以整个数据库都被更新了。我必须使用程序,而不是函数。任何的想法?谢谢。

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;
/

【问题讨论】:

  • 看来员工的经理是由员工所在的部门决定的。也就是说,部门(dept_id)和经理之间是一对一的关系。如果是这样,您的问题来自有缺陷的数据模型。无需将经理与员工一起存储。只需将employee.dept_id 作为FK 返回department.dept_id。那么如果需要了解员工的经理,就加入employee.dpt_id = department.dept_id

标签: sql oracle plsql


【解决方案1】:

如果你必须创建一个 PL/SQL 过程,那么你可以这样做

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;
/

您的代码导致“它更新了所有员工的经理”的问题是您的更新语句:

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

您的过滤器在这里比较 i.department_id,这是来自您的 FOR i IN (SELECT * FROM employees) 的变量,而不是来自更新语句。您已经确认了 i.department_id=v_deptid,因为您在循环中调用它并使用 if 语句检查它。

获取员工中的所有行、循环结果、检查每一行是否与条件匹配然后触发更新语句(即使您的更新语句正在过滤正确的行)根本没有效率。

【讨论】:

    【解决方案2】:

    我没有你的桌子,但我有 Scott 的所以 - 这是一个例子。

    部门 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
    

    看起来像你的过程(当你正在学习 PL/SQL 时),使用一个变量来获取部门编号,以及一个循环:

    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.
    

    测试:让我们修改部门 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>
    

    似乎有效。虽然,那是低效的;逐行(在一个循环中)执行它通常是缓慢的。而是处理整组数据。像这样的:

    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>
    

    【讨论】:

      【解决方案3】:

      为什么不简单地选择经理 ID 并更新所有相关员工的信息?

      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
      

      【讨论】:

        猜你喜欢
        • 2015-10-20
        • 2021-07-07
        • 1970-01-01
        • 1970-01-01
        • 2020-07-07
        • 2012-11-22
        • 2014-06-19
        • 2017-06-01
        • 1970-01-01
        相关资源
        最近更新 更多