2013-04-24 17 views
5

Ecco il problema:Tabella sta mutando, innesco/funzione potrebbe non vederlo (fermare una media scenda sotto 2,5)

creare un trigger che impedisce qualsiasi modifica il rapporto assunzione che avrebbe rilasciare il voto medio complessivo in una particolare classe inferiore a 2.5. Nota: questo trigger non è inteso per indirizzare il GPA medio di un determinato studente, ma piuttosto dovrebbe indirizzare il voto medio per tutti i voti assegnati in una particolare classe.

Ecco lo schema:

Student-schema =(studentnum, name, standing, gpa, major) 
Class-schema = (schedulenum, semester, department, classnum, days, time, place, enrollment) 
Instructor-schema = (name, department, office) 
Teaches-schema = (name, schedulenum, semester) 
Taking-schema = (studentnum, schedulenum, semester, grade) 

Sto avendo un periodo terribile con questi inneschi, ma qui è il mio tentativo di fare questo lavoro:

CREATE OR REPLACE TRIGGER stopChange 
    AFTER UPDATE OR INSERT OR DELETE ON taking 
    REFERENCING OLD AS old 
    NEW AS new 
    FOR EACH ROW 
DECLARE 

grd_avg taking.grade%TYPE; 

BEGIN 
    SELECT AVG(grade) 
    INTO grd_avg 
    FROM taking 
    WHERE studentnum = :new.studentnum 
    AND schedulenum = :new.schedulenum 
    AND semester = :new.semester; 

    IF grd_avg < 2.5 THEN 
     UPDATE taking 
     SET grade = :old.grade 
     WHERE studentnum = :old.studentnum 
     AND schedulenum = :old.schedulenum 
     AND semester = :old.semester; 
    END IF; 

END; 
/

Sono ovviamente facendo qualcosa di sbagliato, perché quando poi passo ad aggiornare o eliminare una tupla, ottengo l'errore:

ERROR at line 1: 
ORA-04091: table TAKING is mutating, trigger/function may not see it 
ORA-06512: at "STOPCHANGE", line 6 
ORA-04088: error during execution of trigger 'STOPCHANGE' 

Qualche consiglio? Sto usando Oracle.

risposta

4

Penso che si possa risolvere il problema riscrivendo questo come un trigger prima, piuttosto che un trigger dopo. Tuttavia, questo potrebbe essere un po 'complicato per inserimenti ed eliminazioni. L'idea è:

CREATE OR REPLACE TRIGGER stopChange 
    BEFORE UPDATE OR INSERT OR DELETE ON taking 
    REFERENCING OLD AS old 
    NEW AS new 
    FOR EACH ROW 
DECLARE 

grd_avg taking.grade%TYPE; 

BEGIN 
    SELECT (SUM(grade) - oldgrade + new.grade)/count(*) 
    INTO grd_avg 
    FROM taking 
    WHERE studentnum = :new.studentnum 
    AND schedulenum = :new.schedulenum 
    AND semester = :new.semester; 

    IF grd_avg < 2.5 THEN 
     new.grade = old.grade 
    END IF; 
END; 
+0

Grazie per la risposta. Ho provato questo, e ottenere lo stesso errore.Mi chiedo se il mio voto medio sia calcolato correttamente e se devo fare qualche istruzione "elsif" per continuare a cambiare l'aggiornamento/inserimento/cancellazione dovrebbe essere superiore a 2,5. Mi sono perso. lol –

+1

Ok, ecco cosa ho dedotto. Non è possibile eseguire una query di selezione all'interno di pl/sql per un trigger quando è possibile modificare la tabella. Mi chiedo se posso riscrivere questo senza usare pl/sql. –

+1

@ TheRationalist. . . Come pratica, avvolgo inserti/aggiornamenti/eliminazioni in procedure memorizzate, piuttosto che fare affidamento sui trigger. Penso che il problema sia il * after * nel trigger (che ho cambiato nella mia mente ma non nel codice apparentemente). Ho anche modificato la logica in modo che funzioni nel caso dell'aggiornamento: potresti ottenere una divisione per 0 in un'eliminazione, quindi la logica effettiva è un po 'più complicata. –

5

Prima di tutto bisogna leggere su trigger, mutando errore tavolo e composto trigger: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS2005

il trigger è dopo AGGIORNAMENTO O inserire o cancellare. Significa che se si eseguono istruzioni UPDATE O INSERT O DELETE su questa tabella, il trigger verrà attivato. Ma stai cercando di aggiornare nuovamente la stessa tabella all'interno del trigger, che è compl. sbagliato. Questo è il motivo per cui stai ricevendo l'errore. Non è possibile modificare la stessa tabella in cui viene attivato il trigger. Lo scopo del trigger è di sparare automaticamente quando la tabella viene aggiornata, inserita o eliminata nel tuo caso. Quello di cui hai bisogno è una procedura, non un trigger.

0

Ho avuto lo stesso problema e ho notato che se si esegue una selezione sullo stesso tavolo su cui si inserisce il trigger si potrebbe avere/avrà questo problema. È possibile rimuovere PER OGNI FILA o utilizzare i dati in : Nuovo per eseguire il calcolo (se possibile) e quindi effettuare l'aggiornamento.

Nel tuo caso, avrà più senso usare una tabella separata per avere l'avg_grade per semestre.

0

Anche noi ci siamo ritrovati con lo stesso problema nel nostro progetto. Ma dopo aver cercato in pochi forum oracle, abbiamo trovato la soluzione seguente.

1) Salvare i dati della colonna vecchia/nuova in una tabella temporanea come traccia del trigger a livello di riga. 2) Scrivi un trigger a livello di istruzione e utilizza i dati salvati nel passaggio 1.

Questo risolverebbe il problema.

6

utilizzare questa istruzione all'interno di DECLARE, funzionerà.

pragma autonomous_transaction; 
+0

La tua soluzione ha funzionato per me. Per gli altri che hanno superato lo stesso problema per capire perché ciò accade: ORA-04091 Causa: un trigger (o una funzione PL/SQL definita dall'utente a cui si fa riferimento in questa istruzione) ha tentato di esaminare (o modificare) un tavolo che era nel mezzo di essere modificato dalla dichiarazione che lo ha sparato. Azione: riscrive il trigger (o la funzione) in modo che non legga quella tabella. Fonte: http://docs.oracle.com/cd/B10501_01/server.920/a96525/e2100.htm#1002387 – Arthur