sql >> Database >  >> RDS >> Oracle

Vreemd gedrag met in orakel geneste cursors

Zoals vermeld in een opmerking over uw vorige vraag , uw tweede cursor is niet beperkt tot de werknemer die door de eerste cursor wordt gevonden, omdat u er geen link tussen hebt. Waar heb je:

and employee_id = employee_id

... beide verwijzen naar de tabelkolom, zodat deze helemaal niet als filter fungeert. Je hebt je lokale variabele dezelfde naam gegeven, wat de zaken al verwarrend genoeg maakt, maar het valt sowieso buiten het bereik - deze cursor heeft geen zichtbaarheid van de variabelewaarde die is ingesteld in het hoofdgedeelte van de procedure.

Je moet iets doen als:

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;

Het belangrijkste verschil is dat de tweede cursor nu een parameter heeft en dat de werknemers-ID van de eerste cursor als die parameter wordt doorgegeven.

Ook IN_DATE is gedeclareerd als een datum, dus u hoeft deze niet door te geven via TO_DATE() . Er zullen impliciete datumconversies zijn op andere plaatsen (ingangsdatums enz.) omdat je ze als tekenreeksen behandelt, maar zolang ze geen tijdcomponenten hebben, zal dit waarschijnlijk niets breken, omdat het consistent zou moeten zijn binnen de procedure.




  1. Voeg rijen postgres samen en vervang waarden door laatste wanneer niet null

  2. fout bij het invoegen van android.database.sqlite.sqliteconstraintexception foutcode 19 beperking mislukt

  3. asp.net core Oracle.DataAccess System.BadImageFormatException:kon bestand of assembly niet laden Oracle.DataAccess

  4. Hoe maak je een SQL-zoekopdracht krachtiger?