Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Seltsames Verhalten bei verschachtelten Orakel-Cursoren

Wie in einem Kommentar zu Ihrer vorherigen Frage erwähnt , ist Ihr zweiter Cursor nicht auf den vom ersten Cursor gefundenen Mitarbeiter beschränkt, da Sie keine Verknüpfung zwischen ihnen haben. Wo haben Sie:

and employee_id = employee_id

... beide beziehen sich auf die Tabellenspalte, sodass sie überhaupt nicht als Filter fungieren. Sie haben Ihrer lokalen Variablen denselben Namen gegeben, was die Dinge genug verwirrt, aber sie ist sowieso außerhalb des Gültigkeitsbereichs - dieser Cursor hat keine Sichtbarkeit des Variablenwerts, der im Hauptteil der Prozedur festgelegt ist.

Sie müssen etwas tun wie:

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;

Der wirklich wichtige Unterschied besteht darin, dass der zweite Cursor jetzt einen Parameter hat und die Mitarbeiter-ID des ersten Cursors als dieser Parameter übergeben wird.

Außerdem IN_DATE wird als Datum deklariert, sodass Sie es nicht über TO_DATE() übergeben müssen . An anderen Stellen wird es implizite Datumskonvertierungen geben (gültige Daten usw.), weil Sie sie als Zeichenfolgen behandeln, aber solange sie keine Zeitkomponenten haben, wird dies wahrscheinlich nichts beschädigen, da es darin konsistent sein sollte das Verfahren.