Jak wspomniano w komentarzu do Twojego poprzedniego pytania , drugi kursor nie jest ograniczony do pracownika znalezionego przez pierwszy kursor, ponieważ nie ma między nimi połączenia. Gdzie masz:
and employee_id = employee_id
... oba odnoszą się do kolumny tabeli, więc w ogóle nie działa jako filtr. Tę samą nazwę nadałeś swojej zmiennej lokalnej, co jest dość mylące, ale i tak jest poza zakresem — ten kursor nie ma widoczności wartości zmiennej ustawionej w głównej części procedury.
Musisz zrobić coś takiego:
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;
Tak naprawdę ważną różnicą jest to, że drugi kursor ma teraz parametr, a identyfikator pracownika z pierwszego kursora jest przekazywany jako ten parametr.
Również IN_DATE
jest zadeklarowana jako data, więc nie musisz przekazywać jej przez TO_DATE()
. Będą niejawne konwersje dat w innych miejscach (daty efektywne itp.), ponieważ traktujesz je jako łańcuchy, ale dopóki nie mają składników czasu, prawdopodobnie niczego nie zepsuje, ponieważ powinno być spójne w obrębie procedurę.