Acest capitol va prezinta toate tipurile de triggere care sunt asociate
cu momentul executiei instructiunii commit.
Dintre subiectele tratate:
Acesta este evenimentul care incearca sa faca datele din baza de date
identice cu cele din forma. Acest proces implica:
Trimiterea consta in scriarea stergerilor, inserarilor si
actualizarilor din forma in baza de date, dar fara executarea acestor
tranzactii in baza de date. Fiecare commit trebuie sa eralizeze o
trimitere.
Trimiterea poate surveni si separat de un `commit' prin procedura
package POST. Nu exeista tasta functionala asociata cu POST.
Commit garanteaza faptul ca tranzactiile care au fost trimise au
devin permanente.
Executia unui commit survine in oricare sin situatiile:
`Do you want to commit the changes you have made ?'
Termenii urmatori se refera la executia unui commit :
Ori de cate ori apare o trimitere (ca parte dintr-o executie Commit
su Post), SQL*Forms genereaza un punct de salvare. La aparitia unei
erori in timpul executiei tranzactiei, SQL*Forms readuce tranzactia
la punctul de salvare.
Daca evenimentul in executie este un Commit (nu doar un Post) si
executia se incheie cu succes, intreaga tranzactie este realizata.
In alte cuvinte, un Commit realizeaza intreaga tranzactie, pe cand
o revenire poate realiza intoarcerea la un punct de salvare din
corpul tranzactiei.
SQL*Forms mentine propriul sau unit pentru commit. Aceasta inseamna
ca orice punct de salvare generat dintr-un trigger va fi ignorat.
PRE-COMMIT POST-COMMIT
PRE-DELETE ON-DELETE POST-DELETE PRE-INSERT ON-INSERT POST-INSERT PRE-UPDATE ON-UPDATE POST-UPDATE
1. Navigheaza la nivelul formei --------------- \ | | 2. Activeaza PRE_COMMIT \ | 1 | \ | | 3. Proceseaza blocurile \ --------------- \ --------------------------------- \ --------------- \ | | Pentru fiecare bloc \ | 2 | | | Verifica fiecare inregistrare --------------- (1) Executa stergerile --------------- | | (2) Executa Inserarile/actualizarile | n | | | --------------------------------- --------------- 4. Activeaza POST_COMMIT
---------------------- | activeaza PRE-DELETE | ---------------------- / \ | | V V --------------------- ---------------------- | activeaza ON-DELETE | SAU | sterge inregistrarea | --------------------- ---------------------- | | \ / V V ----------------------- | activeaza POST-DELETE | -----------------------
----------------------------- | activeaza PRE-INSERT/UPDATE | ----------------------------- / \ | | V V ---------------------------- ------------------------ | activeaza ON-INSERT/UPDATE | SAU | insereaza/actualizeaza | | | | inregistrarea | ---------------------------- ------------------------ | | \ / V V -------------------------------- | activeaza POST-INSERT/UPDATE | --------------------------------
Cand are loc un Commit sau Post, se produc urmatoarele :
* In fiecare bloc : - e procesata fiecare inregistrare marcata pentru stergere: se activeaza PRE-DELETE trigger se activeaza ON-DELETE trigger (daca exista) SAU se sterge inregistrarea se activeaza POST-DELETE trigger - e procesata fiecare inregistrare marcata pentru inserare/actualizare : se activeaza PRE-INSERT/UPDATE trigger se activeaza ON-INSERT/UPDATE trigger (daca exista) SAU inregistrarea este inserata/actualizata se activeaza POST-INSERT/UPDATE trigger
ON-DELETE on Emp Block - - - - - - - - - - - - - - UPDATE emp SET del_flag = 'Y' WHERE empno = :emp.empno; - - - - - - - - - - - - - -
De obicei Pre-Insert trigger este folosit pentru a atrbui o valoare
unica unei chei primare a unui camp. Trigerul poate fie sa urilizeze
o secventa de numere generata automat, fie sa citeasca un numar dintr-o
tabela cu secventa de numere.
De notat ca aceasta metoda "pierde" mai putine numere in secventa decat
daca s-ar folosi: SEQUENCE.name.NEXTVAL ca valoare implicita a unui
camp.
PRE-INSERT on EMP BLOCK - - - - - - - - - - - - - - - SELECT empno.nextval INTO :emp.empno FROM SYS.DUAL Exemplul 2 - Folosind o tabela cu secventa de numere ---------- PRE-INSERT on EMP Block - - - - - - - - - - - - - - - - - - - - - SELECT maxvalue + 1, ROWID INTO :emp.empno, :emp.scr_rowid FROM maxseqnos WHERE table_name = 'EMP' FOR UPDATE OF maxvalue; POST_INSERT on EMP Block - - - - - - - - - - - - - - - - - - - UPDATE maxseqnos SET maxvalue = :emp.empno WHERE ROWID = :emp.scr_rowid;
... cresterea salariului lui Scott tabela de pe segment de ecran revenire ------------------------------------------------------------- interogarea liniei 3000 3000 actualizarea liniei 4500 3000 COMMIT -------------------------------------- (PRE-COMMIT) PRE-UPDATE 4500 3000 actualizare 4500 4500 3000 POST-UPDATE 4500 4500 3000 (POST-COMMIT) --------------------------------------- generare COMMIT ------------------ \ / \ / \ / \ / \/ 'transaction complete'
In alegerea tipului triggerului pe care sa-l folositi pentru un anume
scop, trebuie sa stabiliti ce valori poate accesa fiecare tip de
trigger.<
De exemplu, daca doriti sa aflati daca marirea de salariu a lui Scott
este mai mare de 10% din vechiul salariu, ati avea nevoie sa folositi
mai degraba un trigger Pre-Update (care poate accesa vechiul si noul
salariu) decat un trigger Post-Update (care are acces doar la noul
salariu).
Tabelul de mai jos indica vaorile pe care le poate accesa fiecare
trigger.
Tipul triggerului Ce poate accesa din inregistrarea pe care o proceseaza ----------------- -------------------------------------------- Pre-Insert doar campurile ecran ale liniei curente Post-Insert doar inregistrarea noua, fie in baza da date, fie pe ecran Pre-Update noile valori ale datelor pe ecran SI vechile valori ale datelor in baza de date Post-Update numai valorile revizuite, fie in inregistrarea din baza de date, fie pe ecran Pre-Delete inregistrarea din baza de date SI inregistrarea care a fost stearsa de pe ecran! Chir daca linia nu mai este afisata,un trigger Pre-Delete SE POATE referi la campurile sale Post-Delete nici inregistrarea din baza de date, nici cea stearsa de pe ecran
Suita de triggere de mai jos pastreaza `urma' numerelor inregistrarilor
actualizate, inserate sau sterse. Triggerul Post-Commit scrie informatia
intr-un fisier de log.
PRE-COMMIT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - :GLOBAL.changes_to_commit := 'Y'; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - POST-UPDATE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - DEFAULT_VALUE('0', 'GLOBAL.updates'); :GLOBAL.updates := TO_NUMBER(:GLOBAL.updates) + 1; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - POST-DELETE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - DEFAULT_VALUE('0', 'GLOBAL.deletes'); :GLOBAL.updates := TO_NUMBER(:GLOBAL.deletes) + 1; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - POST-INSERT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - DEFAULT_VALUE('0', 'GLOBAL.inserts'); :GLOBAL.updates := TO_NUMBER(:GLOBAL.inserts) + 1; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - DEFAULT_VALUE('N', 'GLOBAL.changes_to_commit'); IF :FLOBAL.changes_to_commit = 'Y' THEN :GLOBAL.changes_to_commit := 'N'; INSERT INTO LOG_FILE (user_id, form_name, update_date, inserts, updates, deletes) VALUES (USER, :SYSTEM.CURRENT_FORM, SYSDATE, :GLOBAL.inserts, :GLOBAL.updates, :GLOBAL.deletes); ERASE ('global.updates'); ERASE ('global.deletes'); ERASE ('global.inserts'); END IF; POST-COMMIT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cand doriti sa preveniti stergerea inregistrarilor in anumite
circumstante, va trebui sa validati stergerea in doua locuri :
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PROCEDURE ARE_THERE_EMPS IS BEGIN SELECT NULL INTO :GLOBAL.DUMMY FROM EMP WHERE :DEPT.DEPTNO = DEPTNO; RAISE TOO_MANY_ROWS; EXEPTION WHEN TOO_MANY_ROWS THEN MESSAGE ('You cannot delete a departament thet has emploees'); RAISE FORM_TRIGGER_FAILURE; WHEN NO_DATA_FOUND THEN NULL; END; ARE_THERE_EMPS form-level procedure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - KEY-DELREC on Dept Block PRE-DELETE on Dept Block - - - - - - - - - - - - - - - - - - - - - - - - - - - - are_there_emps; are_there_emps; delete_record; - - - - - - - - - - - - - - - - - - - - - - - - - - - -
valoarea valoarea Atribut `if SQL found/acted' if SQL found/acted pe 0 linii pe inca o linie SQL%NOTFOUND TRUE FALSE SQL%FOUND FALSE TRUE SQL%ROWCOUNTO number of rows found/affected
IF SQL%FOUND THEN ...
In legatura cu sintaxa unei comenzi DML intr-un trigger nu apare nimic
neobisnuit.
Doar amintim :
Ori de cate ori SQL*Forms executa pentru prima oara un trigger,
asociaza o zona de lucru numita `cursor' cu ficare instructiune SQL.
Intrucat cursorul este asociat implicit, se numeste cursor
implicit.
Dupa executia unei comenzi SQL puteti testa rezultatul interogand
atributele cursorului implicit (vezi tabelul pentru numele atributelor)
Observatie: testati atributele cursorului imlicit in sectiunea
executabila a blocului, NU in cea de exceptii. Actionand pe zero sau
mai mult de o coloana NU provoaca o exceptie.
Exemplul 1: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - DELETE FORM emplist WHERE empno = :emp.empno; IF SQL%FOUND THEN INSERT INTO emp_history (empno, ename, hiredate, leaving_date) VALUES (:emp.empno, :emp.ename, :emp.hiredate, SYSDATE); END IF - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Exemplul 2: Acest trigger nu reuseste operatia de actualizare daca ORDID curent nu e gasit in tabela ORD. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UPDATE orders SET shipdate = SYSDATE WHERE ordid = :ord.ordid; IF SQL%NOTFOUND THEN MESSAGE ('Cannot find an order of that number'); RAISE FORM_TRIGGER_FAILURE; END IF; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ROWID ------------------------------------------------------- | | | 0 0 0 4 B 2 9 8 . 0 0 0 9 . 0 0 0 1 | | |---------------| |-------| |-------| | | Block Row File | | | -------------------------------------------------------
Cand creati un bloc intr-o forma, SQL*Forms adauga un camo invizibil si
neactualizabil numit ROWID. Cand o inregistrare este readusa in spatiul
de lucru, valoarea lui ROWID este readusa si ea. Inregistrarile recent
create nu corespund nici unei linii si, de aceea, au ROWID nul.
SQL*Forms foloseste ROWID pentru a gestiona tranzactiile in bocurile
din SQL*Forms si tabelele asociate cu ele. Din acest motiv, in
majoritatea cazurilor nu este necesar sa va preocupe ROWID.
- ROWID indica locatia liniei in baza de date si, de aceea, furnizeaza calea calea mai scurta spre linie. SQL*Forms il foloseste oricand executa DML. Maip uteti folosi acest mecanism selectand identificatorul liniei SELECT...FOR UPDATE ..., si folosind ROWID in clauza WHEN a unei comenzi DML ulterioare.
Exemplu: PRE-UPDATE/PRE-DELETE on ORD Block - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - INSERT INTO order_history (ordid, orderdate, commplan, custid, shipdate, total) SELECT ordid, orderdate, commplan, custid, shipdate, total FROM ord WHERE ROWID = : ord.ROWID; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
De exemplu: IF SQL%ROWCOUNT > 5 THEN
Dupa ce am vazut varietatea de tipuri de triggere si diversitatea
codului triggerelor, sa vedem care instructiuni apartin carui tip
de trigger. Regulile sunt aceleasi, odata ce ati invatat sa
identificati ce proceduri package sunt limitate la key-triggers.
Tabelul de mai jos ar trebui sa clarifice regulile.
Tipul comenzii Tipul Triggerului ------------------- ------------------------- SQL: SELECT oricare DDL nici unul! DML tranzaction triggers PL/SQL: PL?SQL standard oricare proceduri package key triggers limitate ON-NEW-FIELD-INSTANCE proceduri package nerestrictionate
Triggere eveniment Domeniu Nume ------- --------------------- -------- Camp PRE-FIELD, POST-FIELD | (Field) POST-CHANGE | ON-VALIDATE-FIELD ----------------------- | ON-NEW-FIELD-INSTANCE ==> |si procedurile package | | ON-ERROR | restrictionate | | ON-MESSAGE ----------------------- | ..................................................... | | Bloc PRE-QUERRY, POST-QUERRY | ON-LOCK ---- | PRE-DELETE, PRE-INSERT, PRE-UPDATE | | ON-DELETE, ON-INSERT, ON-UPDATE |-> AND DML | POST-DELETE, POST-INSERT, POST-UODATE | | PRE-RECORD, PRE-BLOCK ---- | | POST-RECORD, POST-BLOCK ------------------- ON-VALIDATE-RECORD | PL/SQL, SELECT si | ON-DATABASE-RECORD | proceduri pkg | ON-REMOVE-RECORD | nerestrictionate | ON-NEW-ERCORD ------------------- ON-CLEAR-BLOCK | ..................................................... | Forma PRE-FORM, POST-FORM | PRE-COMMIT, POST-COMMIT ==> AND DML -------- Triggere Program ================ Domeniu Nume ------- ---------------- Camp toate KEY-triggerele -------- ..................................................... | | Bloc toate KEY triggerele | ------------------- | PL/SQL, SELECT si | | TOATE procedurile | | package | ------------------- | ..................................................... | Forma toate KEY-triggerele --------
Generare de numere de secventa & `Zavorarea (**Non-base Tables**)
Acest exercitiu este realizat pentru a va arata cum se folosesc
tabelele cu numere de secventa pentru a crea identificatori unici
pentru inregistrarile inserate in baza de date.
1a. PRE-INSERT in blocul CUS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SELECT maxseqno + 1, rowid INTO :cus.custid, :cus.scr_rowid FROM seqnos where table_name = 'CUS' for update of maxseqno; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - b. POST-INSERT in blocul CUS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UPDATE seqnos SET maxseqno = :cus.custid WHERE rowid = cus.scr_rowid; Pentru actualizarea lui `ordid' 2a. PRE-INSERT in blocul ITEM - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SELECT NVL(MAXLINE, 0) + 1, rowid INTO :item.itemid, :item.scr_rowid FROM ord WHERE ordid = :ord.ordid for update of ordid b. POST-INSERT in blocul ITEM - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UPADTE ord SET maxline = :item.itenid WHERE rowid = :item.scr_rowid; Daca aveti timp : 3a. Procedura la nivel de forma UPD_ORD_TOT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - BEGIN SELECT nvl(sum(itemtot), 0) INTO :ord.total FROM item WHERE ordid = :ord.ordid; UPDATE ord SET total = :ord.total WHERE ordid = :ord.ordid; END; b. POST-UPADTE, POST-DELETE in blocul ITEM - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - upd_ord_tot; c. POST-INSERT in blocul ITEM - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UPADTE ord SET maxline = :item.itemid WHERE rowid = :item.scr_rowid; upd_ord_tot;