2012-09-12 44 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()。在其他地方會出現隱式日期轉換(生效日期等),因爲您將它們視爲字符串,但只要它們沒有時間組件,這可能不會破壞任何內容,因爲它應該是一致的步驟。