Oracle
 sql >> Database >  >> RDS >> Oracle

Utilizzo di IF EXISTS (SELECT ...) in un trigger BEFORE INSERT (Oracle)

Prima di tutto, se stai usando SQL*Plus, quando crei un oggetto e ti viene detto che ci sono errori di compilazione, il comando show errors ti mostrerà gli errori.

Se hai eseguito show errors , ti verrà detto che IF EXISTS non è una sintassi valida. Potresti fare qualcosa come

SELECT COUNT(*)
  INTO l_cnt
  FROM <<rest of query>>

IF( l_cnt > 0 )
THEN
  RAISE_APPLICATION_ERROR ...
END IF;

Una volta corretto l'errore di compilazione, tuttavia, ti ritroverai con errori di runtime. In un trigger a livello di riga su surveillance , in genere non puoi interrogare surveillance (puoi se tutto ciò che stai facendo è un INSERT VALUES che è garantito per inserire solo una singola riga). In tal caso, verrà visualizzato un errore di trigger mutante in fase di esecuzione.

Dal punto di vista del modello di dati, quando ti ritrovi a progettare una tabella in cui i dati validi per una determinata riga dipendono dai dati archiviati in altre righe della stessa tabella, hai generalmente violato i principi di normalizzazione e generalmente ti viene meglio aggiustare il modello di dati sottostante.

Se sei davvero determinato a mantenere il modello di dati, preferirei creare una vista materializzata che si aggiorna al commit che contiene dati solo per le righe che violano i tuoi criteri. Puoi quindi porre vincoli a quella vista materializzata che genera errori al momento del commit quando i tuoi criteri vengono violati. Ciò richiederà registri di visualizzazione materializzati sul tuo tavolo.

Se vuoi davvero mantenere il modello di dati e vuoi rafforzare la logica con i trigger, avresti bisogno della classica soluzione a tre trigger (o un trigger composto con tre parti se stai usando 11.2 o versioni successive). Creeresti un pacchetto con una raccolta di valori di chiave primaria. Un trigger di istruzione before inizializzerebbe la raccolta. Un trigger a livello di riga inserirebbe le chiavi primarie delle righe che sono state inserite e/o aggiornate in questa raccolta. E poi un trigger di istruzione after itera su questa raccolta e implementa tutti i controlli che desideri. Tuttavia, sono molti i pezzi in movimento, motivo per cui generalmente lo sconsiglio.

Inoltre, anche se riesci a far funzionare tutti questi pezzi, la tua logica non ti proteggerà in un ambiente multiutente. Quando più utenti colpiscono il sistema contemporaneamente, è del tutto possibile che un utente inserisca una riga, il secondo utente inserisca un'altra riga con un intervallo sovrapposto e quindi ogni sessione verrà confermata. In tal caso, entrambi i set di trigger consentiranno la modifica, ma nella tabella rimarranno comunque i dati che violano i tuoi requisiti. La vista materializzata, poiché viene applicata al momento del commit anziché al momento dell'inserimento, funzionerà correttamente in un ambiente multiutente. Se vuoi che i trigger funzionino in un ambiente multiutente, dovresti complicarli ulteriormente aggiungendo una logica aggiuntiva che impone la serializzazione che bloccherebbe insert della seconda sessione dall'esecuzione fino al commit o al rollback della prima sessione. Ciò aggiunge complessità, riduce la scalabilità e, a seconda di come viene implementato, può creare un incubo per il supporto.