How to avoid “table mutating” errors

后端 未结 2 1047
一向
一向 2020-12-22 03:15

I have an trigger that needs to read from a table after deleting a row. Essentially, I need to count up the remaining rows that are similar to the current row, and if that c

相关标签:
2条回答
  • 2020-12-22 03:56

    You can create a COMPOUND trigger for such cases:

    create or replace TRIGGER Di_PatMustBeWell
    FOR DELETE ON Diagnosis
    COMPOUND TRIGGER
    
        TYPE Di_Patient_Table_type IS TABLE OF DiagnosisCount.Di_Patient%TYPE;
        Di_Patient_Table Di_Patient_Table_type;
    
         BEFORE STATEMENT IS
          BEGIN
            Di_Patient_Table := Di_Patient_Table_type();
         END BEFORE STATEMENT;
    
         BEFORE EACH ROW IS
         BEGIN
            Di_Patient_Table.EXTEND;
            Di_Patient_Table(Di_Patient_Table.LAST) := :OLD.Di_Patient;
         END BEFORE EACH ROW;
    
        AFTER STATEMENT IS
        BEGIN
           FOR i IN Di_Patient_Table.FIRST..Di_Patient_Table.LAST LOOP
              SELECT NUMDISEASES INTO Numdiseases
              FROM DiagnosisCount
              where Di_Patient = Di_Patient_Table(i);
    
              IF NumDiseases !=  1 THEN
                UPDATE Patient SET Pat_Sick = 0 WHERE Pat_Person = Di_Patient_Table(i);
              END IF;
           END LOOP;
           Di_Patient_Table.DELETE;
         END AFTER STATEMENT;
       END;
    /
    
    0 讨论(0)
  • 2020-12-22 04:02

    Short answer - no trigger, no mutating.


    Yow can use the trigger with pragma autonomous_transaction for counting of remaining diagnoses for certain patient, but it's is not recommended way to do this. Better you create new function or procedure to implement your logic on deleted diagnosis. Something like this:

    create table Diagnosis as select 456 idDiseases, 123 di_patient from dual;
    /
    create table diagnosisCount as select 1 numDiseases, 123 di_patient from dual;
    /
    create table Patient as select 123 Pat_Person, 1 Pat_Sick from dual;
    /
    drop trigger di_patmustbewell;
    
    create or replace function deleteDiagnosis(idDiseases number) return number is
        rows_ number;
        di_patient number;
        Numdiseases number;
    begin
        <<del>> begin 
            delete Diagnosis where IdDiseases = deleteDiagnosis.IdDiseases
            returning Diagnosis.di_patient into deleteDiagnosis.di_patient
            ;
            rows_ := sql%rowcount;
            if rows_ != 1 then raise too_many_rows; end if;
        end del;
        select count(1) into deleteDiagnosis.numDiseases from Diagnosis where Di_Patient = deleteDiagnosis.di_patient;
        if deleteDiagnosis.numdiseases = 0 then <<upd>> begin 
            update Patient set Pat_Sick = 0 where Pat_Person = deleteDiagnosis.di_patient;
            exception when others then 
                dbms_output.put_line('Cannot update Patient di_patient='||di_patient);
                raise;
        end upd; end if;
        return rows_;
    end;
    /
    show errors
    
    declare rows_ number :=  deleteDiagnosis(456);
    begin dbms_output.put_line('deleted '||rows_||' rows'); end;
    /
    
    deleted 1 rows
    
    select * from Patient;
    PAT_PERSON   PAT_SICK
    ---------- ----------
           123          0
    

    An alternative solution, if you prefer (or must) to use a trigger in your application - declare internal function returning count of patient's diagnoses in the trigger body:

    create or replace trigger di_patmustbewell
    after delete on diagnosis for each row
    declare
        numdiseases number;
        function getNumDiagnosis (di_patient number) return number is
            ret number;
            pragma autonomous_transaction;
        begin
            select count(1) into ret from diagnosis where di_patient = getNumDiagnosis.di_patient;
            return ret;
        end getNumDiagnosis;    
    begin
        numDiseases := getNumDiagnosis(:old.di_patient);
        if(numdiseases = 0) then
            update patient set pat_sick = 0 where pat_person = :old.di_patient;
        end if;
    end;
    /
    show errors;
    
    Trigger DI_PATMUSTBEWELL compiled
    

    Hope it helps you a bit.

    0 讨论(0)
提交回复
热议问题