Oracle 更新光标列,然后使用该值选择

Oracle 更新光标列,然后使用该值选择,oracle,plsql,cursor,Oracle,Plsql,Cursor,我有一个游标可以查询这样的表 CURSOR Cur IS SELECT Emp_No,status from Employee FOR UPDATE OF status; 现在,我想使用Emp_no从另一个表更新Employee表中的状态。完成此操作后,我需要使用此状态调用自定义业务逻辑,而不是游标检索的原始状态。最好的方法是什么?这是我写的。顺便说一下,我声明了一个名为v_status的变量 FOR Rec IN Cur LOOP

我有一个游标可以查询这样的表

CURSOR Cur IS
       SELECT Emp_No,status
        from Employee
        FOR UPDATE OF status;
现在,我想使用Emp_no从另一个表更新Employee表中的状态。完成此操作后,我需要使用此状态调用自定义业务逻辑,而不是游标检索的原始状态。最好的方法是什么?这是我写的。顺便说一下,我声明了一个名为v_status的变量

 FOR Rec IN Cur LOOP

           BEGIN

           UPDATE Employee           
SET status = (select a.status from Employee_Status  where a.Emp_No = rec.Emp_No)
           WHERE CURRENT OF Cur ;
           COMMIT;

           END;

           SELECT status INTO v_status 
           FROM  Employee
           where Emp_No = rec.Emp_No;

            IF(v_status = 'Active') THEN
                   -- Custom Business Logic
                  ELSE  
            -- Business logic

            END IF;    
END LOOP;
实现这一目标的更好方法是什么?

为什么不简单地:

FOR Rec IN Cur LOOP

   SELECT a.status INTO v_status from Employee_Status a where a.Emp_No = rec.Emp_No;

           UPDATE Employee           
           SET status = v_status
           WHERE CURRENT OF Cur ;
           COMMIT;

            IF(v_status = 'Active') THEN
                   -- Custom Business Logic
                  ELSE  
            -- Business logic

            END IF;    
END LOOP;
您可以使用:

< P> 1)我希望在你的真实代码中,你的循环中没有<代码>提交/代码>。由于提交会释放事务持有的锁,因此使用
FOR UPDATE
子句取出的行级锁将被释放,其他会话可以自由更新相同的行。在Oracle的更高版本中,如果执行此操作,您将获得“ORA-01002:提取顺序错误”。在Oracle的早期版本中,此错误被忽略,偶尔会导致不正确的结果

2) 是否需要逐行更新
EMPLOYEE
表?我倾向于将更新移到循环之外,以最大化SQL,因为这是处理数据最有效的方法。如果您的业务逻辑符合它的要求,我还建议您执行批量操作,将数据提取到您的业务逻辑可以迭代的本地集合中,以最小化SQL和PL/SQL之间的上下文转换

因此,根据您的注释,在您的示例中,游标的定义中应该有一个谓词,对吗?像这样的

CURSOR Cur IS
  SELECT Emp_No,status
    from Employee
   WHERE status IS NULL
      FOR UPDATE OF status;
如果是这样,那么在单个UPDATE语句中需要相同的谓词(可能不是
EXISTS
子句)。但是,因为您知道您只想处理受UPDATE语句影响的行,所以可以将这些行返回到本地集合中

DECLARE
  CURSOR cur IS 
     SELECT emp_no, status
       FROM employee
      WHERE status IS NULL;
  TYPE l_employee_array IS
    TABLE OF cur%rowtype;
  l_modified_employees l_employee_array;
BEGIN
  UPDATE employee e
     SET status = (select es.status
                     from employee_status es
                    where es.emp_no = e.emp_no)
   WHERE status IS NULL
     AND EXISTS (SELECT 1
                   FROM employee_status es
                  WHERE es.emp_no = e.emp_no)
  RETURNING emp_no, status
       BULK COLLECT INTO l_modified_employees;
  FOR i IN l_modified_employees.FIRST .. l_modified_employees.LAST
  LOOP
    IF( l_modified_employees(i).status = 'Active' ) 
    THEN
      -- Custom logic 1
    ELSE
      -- Custom logic 2
    END IF;
  END LOOP;
END;

Justin表中的员工有已处理和未处理的记录,知道这一点的唯一方法是,未处理的记录的状态为null。因此,我只需要更新这些记录,然后再对其进行处理,而不需要对其余的记录进行处理。如果我更新了所有内容,那么就无法知道要处理哪些记录,因为我无法修改employee表以向其添加列。@Eosphorus-OK。因此,在您的示例中,光标定义中会有另一个谓词来检查
NULL
状态,对吗?您只想处理您正在修改状态的行吗?如果是这样,我已经更新了我的答案。
DECLARE
  CURSOR cur IS 
     SELECT emp_no, status
       FROM employee
      WHERE status IS NULL;
  TYPE l_employee_array IS
    TABLE OF cur%rowtype;
  l_modified_employees l_employee_array;
BEGIN
  UPDATE employee e
     SET status = (select es.status
                     from employee_status es
                    where es.emp_no = e.emp_no)
   WHERE status IS NULL
     AND EXISTS (SELECT 1
                   FROM employee_status es
                  WHERE es.emp_no = e.emp_no)
  RETURNING emp_no, status
       BULK COLLECT INTO l_modified_employees;
  FOR i IN l_modified_employees.FIRST .. l_modified_employees.LAST
  LOOP
    IF( l_modified_employees(i).status = 'Active' ) 
    THEN
      -- Custom logic 1
    ELSE
      -- Custom logic 2
    END IF;
  END LOOP;
END;