2012-09-12 43 views
0

下面是我写的使用嵌套游标的存储过程。使用oracle嵌套游标的奇怪行为

create or replace 
PROCEDURE SP_RUN_EMPLOYEE_UPDATES 
(
    IN_DATE IN VARCHAr2 
) 
IS 

update_sql varchar2(4000); 

employee_id BI_EMPLOYEE_UPDATE.employee_id%TYPE; 

effective_date date ; 
created_by number; 
created_on date; 
comments varchar2(4000); 

CURSOR 
    employees 
IS 
    SELECT distinct(employee_id) FROM BI_EMPLOYEE_UPDATE WHERE EFFECTIVE_DATE = to_date(IN_DATE,'dd-mm-yy') AND EXECUTED = 'N' AND ACTIVITY_ID = '0'; 

CURSOR 
    e_updates 
IS 
    SELECT * FROM BI_EMPLOYEE_UPDATE WHERE EFFECTIVE_DATE = to_date(IN_DATE,'dd-mm-yy') AND EXECUTED = 'N' AND ACTIVITY_ID = '0' and employee_id = employee_id ; 

BEGIN 

OPEN employees; 

    LOOP 

     effective_date := ''; 
     created_by := ''; 
     created_on := ''; 
     comments := ''; 
     employee_id := ''; 

     FETCH employees into employee_id; 
     EXIT WHEN employees%NOTFOUND; 

     update_sql := 'UPDATE BI_EMPLOYEE SET '; 
     FOR e_update in e_updates 
      LOOP 

      select comments, effective_date , changed_by, changed_on into comments, effective_date , created_by, created_on 
      from bi_employee_update where EMPLOYEE_UPDATE_ID = e_update.EMPLOYEE_UPDATE_ID; 

      update_sql := update_sql || e_update.column_name || ' = ''' || e_update.new_value || ''' , ' ; 

      UPDATE BI_EMPLOYEE_UPDATE 
      SET 
       EXECUTED = 'Y' 
      WHERE 
       EMPLOYEE_UPDATE_ID = e_update.EMPLOYEE_UPDATE_ID ; 

      END LOOP; 

      update_sql := update_sql || ' comments = ''' || comments || ''', updated_by = ''' || created_by || ''', updated_on = ''' || created_on || ''', effective_date = ''' || effective_date || ''''; 
      update_sql := update_sql || ' WHERE emp_id = ' || employee_id ; 

     dbms_output.put_line('KKKK '||update_sql); 
     execute immediate update_sql ; 

    END LOOP; 
    CLOSE employees; 

END; 

问题是在第二个游标中,我得到所有以前的游标组合的数据。

例如如果第一次迭代应该返回a,第二次应该返回b。但实际上第一次迭代返回a,b和第二次也返回a,b。

下面是生成的动态查询完全相同。

第一迭代

预期的(CORRECT):

UPDATE BI_EMPLOYEE SET EMPLOYEE_ID = '1111111111111' , PP_NUMBER = '22222222222' , 
    CORPORATE_TITLE_ID = '2' , comments = 'c11', updated_by = '361', 
    updated_on = '12-SEP-12', effective_date = '25-SEP-12' WHERE emp_id = 18010 

ACTUAL(WRONG):

UPDATE BI_EMPLOYEE SET EMPLOYEE_ID = '1111111111111' , PP_NUMBER = '22222222222' , 
    CORPORATE_TITLE_ID = '2' , LASTNAME = 'Ll22 edited ' , OFFSHORE_ONSHORE = '1' , 
    ONSHORE_REGION = '1' , ONSHORE_DESK_MANAGER = 'henrry ' , 
    comments = 'cc 33 33', updated_by = '361', updated_on = '12-SEP-12', 
    effective_date = '25-SEP-12' WHERE emp_id = 18010 

第二迭代

预期的(CORRECT):

UPDATE BI_EMPLOYEE SET LASTNAME = 'Ll22 edited ' , OFFSHORE_ONSHORE = '1' , 
    ONSHORE_REGION = '1' , ONSHORE_DESK_MANAGER = 'henrry ' , 
    comments = 'cc 33 33', updated_by = '361', updated_on = '12-SEP-12', 
    effective_date = '25-SEP-12' WHERE emp_id = 18009 

实际的(错误的):

UPDATE BI_EMPLOYEE SET EMPLOYEE_ID = '1111111111111' , PP_NUMBER = '22222222222' , 
    CORPORATE_TITLE_ID = '2' , LASTNAME = 'Ll22 edited ' , 
    OFFSHORE_ONSHORE = '1' , ONSHORE_REGION = '1' , 
    ONSHORE_DESK_MANAGER = 'henrry ' , comments = 'cc 33 33', 
    updated_by = '361', updated_on = '12-SEP-12', 
    effective_date = '25-SEP-12' 
    WHERE emp_id = 18009 

这究竟是为什么?

回答

1

如您在对您的previous question的评论中所述,您的第二个光标不限于第一个光标找到的员工,因为您没有它们之间的链接。你在哪里:

and employee_id = employee_id 

...这两个都指向表列,所以它根本不充当过滤器。你已经给你的局部变量设定了相同的名字,这足以让事情混淆不清,反正它已经超出了范围 - 这个游标没有看到在过程主体中设置的变量值。

你需要做的是这样的:

CREATE OR REPLACE PROCEDURE sp_run_employee_updates (p_date IN DATE) IS 
    update_sql varchar2(4000); 
    first_update boolean; 

    CURSOR c_employees IS 
     SELECT DISTINCT employee_id 
     FROM bi_employee_update 
     WHERE effective_date = p_date 
     AND executed = 'N' 
     AND activity_id = '0'; 

    CURSOR c_updates(cp_employee_id bi_employee_update.employee_id%TYPE) IS 
     SELECT * 
     FROM bi_employee_update 
     WHERE effective_date = p_date 
     AND executed = 'N' 
     AND activity_id = '0' 
     AND employee_id = cp_employee_id 
     FOR UPDATE; 

BEGIN 
    -- loop around all employees with pending records 
    FOR r_employee IN c_employees LOOP 
     -- reset the update_sql variable to its base 
     update_sql := 'UPDATE BI_EMPLOYEE SET '; 
     -- reset the flag so we only add the comments etc. on the first record 
     first_update := true; 

     -- loop around all pending records for this employee 
     FOR r_update IN c_updates(r_employee.employee_id) LOOP 
      -- add the comments etc., only for the first update we see 
      if first_update then 
       update_sql := update_sql 
        || ' comments = ''' || r_update.comments || ''',' 
        || ' updated_by = ''' || r_update.changed_by || ''',' 
        || ' updated_on = ''' || r_update.changed_on || ''',' 
        || ' effective_date = ''' || r_update.effective_date || ''''; 
       first_update := false; 
      end if; 

      -- add the field/value from this record to the variable 
      update_sql := update_sql || ', ' 
       || r_update.column_name || ' = ''' || r_update.new_value || '''' ; 

      -- mark this update as executed 
      UPDATE bi_employee_update 
      SET executed = 'Y' 
      WHERE CURRENT OF c_updates; 

     END LOOP; 

     -- apply this update to the bi_employee record 
     update_sql := update_sql || ' WHERE emp_id = ' || r_employee.employee_id; 

     DBMS_OUTPUT.PUT_LINE(update_sql); 
     EXECUTE IMMEDIATE update_sql; 
    END LOOP; 
END sp_run_employee_updates; 

的重要区别,真的,是第二光标现在有一个参数,并从第一光标雇员ID作为参数传递。

另外,IN_DATE被声明为日期,因此您不需要通过TO_DATE()。在其他地方会出现隐式日期转换(生效日期等),因为您将它们视为字符串,但只要它们没有时间组件,这可能不会破坏任何内容,因为它应该是一致的步骤。