Strange behaviours with oracle nested cursors
下面是我编写的使用嵌套游标的存储过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | 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。
下面是生成的动态查询,它完全相同。
第一次迭代
预期(正确):
1 2 3 | 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 |
实际(错误):
1 2 3 4 5 | 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 |
第二次迭代
预期(正确):
1 2 3 4 | 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 |
实际(错误):
1 2 3 4 5 6 7 | 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 | and employee_id = employee_id |
...两者都引用了表格列,因此它根本不充当过滤器。您已经给本地变量指定了相同的名称,这足以使事情感到困惑,但是无论如何它都超出了范围-该游标无法查看过程主体中设置的变量值。
您需要执行以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | 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作为该参数传递。
另外,