Presentation is loading. Please wait.

Presentation is loading. Please wait.

PL/* introduction PLSQL TSQL Trigger exercise

Similar presentations


Presentation on theme: "PL/* introduction PLSQL TSQL Trigger exercise"— Presentation transcript:

1 PL/* introduction PLSQL TSQL Trigger exercise
DBMAN 7 PL/* introduction PLSQL TSQL Trigger exercise SzaboZs

2 PL/* introduction PLSQL TSQL Trigger exercise
DBMAN 7 PL/* introduction PLSQL TSQL Trigger exercise SzaboZs

3 TRADITIONAL SQL PROCESSING
QUERY CLIENT SERVER RESPONSE-HEADER ROWS MODIFICATIONS SzaboZs

4 TRADITIONAL SQL PROCESSING
Common tasks: Special tasks that can not be done in SQL or that are not SQL-specific tasks Relatively simple processing of big amount of data Checks before update/insert/delete Even when using localhost, it is better to do these on the server SzaboZs

5 TRADITIONAL SQL PROCESSING
Common tasks More than one type of clients Some function must be implemented It has to be done separately for every language Some common security functions are not advised to be implemented this way Even when using localhost, it is better to do these on the server SzaboZs

6 PL/* The server has two engines: the SQL engine and the PL/* engine: Procedural Language Engine It is usually a problem-oriented/procedural language, that allows us to do different programming tasks on the server Therefore, the server does some of the calculations/methods (usually those methods require a lot of data – high-load applications are not advised!) SzaboZs

7 PL/* Using PL/* programs, we can:
Execute simple, embedded script-code using different structures Create and use PROCEDURES for common functions Use special error handling: EXCEPTIONS Create special procedures that are executed automatically (e.g. to check something before insert/update/delete): TRIGGER SzaboZs

8 PL/. http://www. stanford. edu/dept/itss/docs/oracle/10g/appdev
SzaboZs

9 PL/* Oracle uses a language that has an SQL-like syntax: PL/SQL  Supported by IBM DB2 too PostgreSQL: PL/PGSQL (similar), PL/PERL, PL/PYTHON, PL/TCL, PL/JAVA, PL/PHP, PL/R, PL/RUBY TSQL = Transact-SQL = two-in-one (different design) The basic SQL types and built-in functions can usually be used Some special functions: dbms_random.value(2, 5) / sp_xxx SzaboZs

10 PL/* introduction PLSQL TSQL Trigger exercise
DBMAN 7 PL/* introduction PLSQL TSQL Trigger exercise SzaboZs

11 VARIABLES Client-side variables (SQL*plus): SET VERIFY ON; ACCEPT sal1 PROMPT 'Lower Bound?'; ACCEPT sal2 PROMPT 'Higher Bound?'; SELECT * FROM emp WHERE sal>&sal1 AND sal<&sal2; („SUBSTITUTE VARIABLES”) Server-side variables: DECLARE name type;  RW in the PL/SQL, Nonexistant outside („LOCAL VARIABLES”) VARIABLE name type;  Usually set by external client libraries, R (but writeable) in PL/SQL, R outside („BIND VARIABLES”) SzaboZs

12 STANDALONE BLOCK A separated area in the SQL script-program, that is interpreted by the PL/SQL engine We’ll use the sql developer from inside the VM Syntax: SET SERVEROUTPUT ON [DECLARE {variables}] BEGIN {commands} [EXCEPTION {exception handling}] END; / SzaboZs

13 VARIABLES set verify off; set serveroutput on; ACCEPT var_I PROMPT 'WRITE SOMETHING! '; VARIABLE var_II number; DECLARE var_III number; BEGIN :var_II := &var_I; var_III := :var_II; DBMS_OUTPUT.PUT_LINE(:var_II); DBMS_OUTPUT.PUT_LINE(var_III); END; / PRINT var_II; SzaboZs

14 VARIABLES DECLARE var_III number; BEGIN var_III := :var_II; DBMS_OUTPUT.PUT_LINE('block 2'); DBMS_OUTPUT.PUT_LINE(:var_II); DBMS_OUTPUT.PUT_LINE(var_III); DBMS_OUTPUT.PUT_LINE('ends'); END; / BIND variables depend on the client as well (e.g. prepared statements) SzaboZs

15 SPECIAL VARIABLE TYPES
Reference types {table}.{column}%TYPE; {variable}%TYPE; {table}%ROWTYPE;  Record type ~ Pascal Record: varname.colname Rowid: Automatically generated unique ID for each row in a database "Secret column", like the rownum select emp.*, rowid, rownum from emp; SzaboZs

16 SELECT Order of suffixes
INTO FROM WHERE GROUP BY HAVING UNION/MINUS INTERSECT ORDER BY SzaboZs

17 SELECT INTO SELECT {field(s)} INTO {variable(s)} {other suffixes}
The query must return with exactly one row! The query must return with exactly as many fields as many variables we list after the INTO Or: whole record  %ROWTYPE SzaboZs

18 SELECT INTO DECLARE var NUMBER; BEGIN SELECT empno INTO var FROM emp;
END; / ORA-01422: exact fetch returns more than requested number of rows SzaboZs

19 SELECT INTO DECLARE var NUMBER; var2 EMP%ROWTYPE; BEGIN SELECT empno INTO var FROM emp WHERE ename='KING'; SELECT * INTO var2 FROM emp WHERE ename='KING'; END; / SzaboZs

20 IF IF {condition} THEN {commands} [ELSIF {condition} THEN {commands}]
[other ELSIF clauses] [ELSE END IF; SzaboZs

21 LOOP LOOP {commands} END LOOP; To break the loop: EXIT;
EXIT WHEN {condition}; SzaboZs

22 WHILE WHILE {condition} {LOOP} set serveroutput on; DECLARE var NUMBER; BEGIN var:=0; WHILE var<=5 LOOP DBMS_OUTPUT.PUT_LINE(var); var:=var+1; END LOOP; END; / SzaboZs

23 FOR FOR {variable} IN {lower} .. {higher} {LOOP} set serveroutput on; DECLARE var NUMBER; BEGIN FOR var IN LOOP DBMS_OUTPUT.PUT_LINE(var); END LOOP; END; / SzaboZs

24 FOR . . . set serveroutput on; set verify on;
accept A prompt 'Enter a number:' accept B prompt 'Another number:' DECLARE J NUMBER(5); BEGIN FOR j IN &A..&B LOOP DBMS_OUTPUT.PUT_LINE(j); END LOOP; END; / SzaboZs

25 SET VERIFY ON (default in sql-developer)
Enter a number:4 Another number:9 old 3: for j in &A..&B new 3: for j in 4.9  FOR j IN &A .. &B SzaboZs

26 FOR – IMPLICIT CURSOR set serveroutput on; DECLARE someone emp%ROWTYPE; BEGIN FOR someone IN (SELECT * FROM emp) LOOP DBMS_OUTPUT.PUT_LINE('Name = ' || someone.ename); DBMS_OUTPUT.PUT_LINE('Salary = ' || someone.sal); END LOOP; END; / SzaboZs

27 DECLARING A CURSOR DECLARE var1 NUMBER(5);
var2 EMP%ROWTYPE; CURSOR var2 IS SELECT * FROM EMP;  The query is not executed, we only declare that the given variable will refer to the given query SzaboZs

28 USING CURSORS [FOR] ACCEPT low_sal PROMPT 'Lower bound! ' ACCEPT upp_sal PROMPT 'Upper bound! ' DECLARE CURSOR curs IS select * from emp; record EMP%ROWTYPE; BEGIN FOR record IN curs LOOP IF record.sal BETWEEN &low_sal AND &upp_sal THEN DBMS_OUTPUT.PUT_LINE(record.ename); END IF; END LOOP; END; SzaboZs

29 USING CURSORS DECLARE OPEN FETCH CLOSE %NOTFOUND? Yes No SzaboZs

30 CURSOR ATTRIBUTES %FOUND – Successful FETCH?
%NOTFOUND – Unsuccessful FETCH? %ROWCOUNT – Processed number of rows %ISOPEN – The cursor opened or not? SzaboZs

31 USING CURSORS ACCEPT low_sal PROMPT 'Lower bound! '
ACCEPT upp_sal PROMPT 'Upper bound! ' DECLARE CURSOR curs IS select * from emp; record EMP%ROWTYPE; BEGIN OPEN curs; LOOP FETCH curs INTO record; EXIT WHEN curs%NOTFOUND; IF record.sal BETWEEN &low_sal AND &upp_sal THEN DBMS_OUTPUT.PUT_LINE(record.ename); END IF; END LOOP; CLOSE curs; END; SzaboZs

32 USING CURSORS FOR MODIFICATIONS
CREATE TABLE worker AS … DECLARE CURSOR curs IS SELECT * FROM worker FOR UPDATE [OF SAL] [NOWAIT]; record worker%ROWTYPE; mysal worker.sal%TYPE; SzaboZs

33 USING CURSORS FOR MODIFICATIONS
BEGIN OPEN cursor; LOOP FETCH cursor INTO record; EXIT WHEN cursor%NOTFOUND; mysal := record.sal * 1.2; UPDATE worker set sal = mysal WHERE CURRENT OF cursor; END LOOP; CLOSE cursor; END; / SzaboZs

34 SEARCHING FOR PAIRS Aim: connect pair-records in a table using a calculation The searching process is done by a PL/SQL script program This time: salary difference must be max 20% drop table worker; create table worker as select * from emp; alter table worker add pair number(4); update worker set pair=0; SzaboZs

35 PAIRS Principle: Using cursorA, we loop through the records and for each record, we loop again through the records using cursorB The calculation is done with the current records of cursorA and cursorB Problem: how to identify records that already have pairs?  ROWID  CLOSE+OPEN SzaboZs

36 PAIRS Rowid: for every record, we use the ROWID or the primary key field to select the actual record from the table, thus we can check wether the pair field is set or not SELECT INTO, easier solution SzaboZs

37 PAIRS SET SERVEROUTPUT ON DECLARE
CURSOR curs_a IS select * from workers FOR UPDATE; CURSOR curs_b IS select * from workers FOR UPDATE; rec_a curs_a%ROWTYPE; rec_b curs_b%ROWTYPE; DIFF Numeric(5,2); SzaboZs

38 PAIRS BEGIN OPEN curs_a; LOOP FETCH curs_a INTO rec_a; EXIT WHEN curs_a%NOTFOUND; IF rec_a.pair=0 THEN [searching for a pair] END IF; END LOOP; CLOSE curs_a; END; / SzaboZs

39 PAIRS OPEN curs_b; LOOP FETCH curs_b INTO rec_b;
EXIT WHEN curs_b%NOTFOUND; IF (rec_a.sal>rec_b.sal) THEN Diff:=(rec_a.sal-rec_b.sal)/rec_a.sal; ELSE Diff:=(rec_b.sal-rec_a.sal)/rec_b.sal; END IF; IF rec_b.pair=0 AND Diff<0.1 AND rec_a.empno<>rec_b.empno THEN [setting the pair] END LOOP; CLOSE curs_b; SzaboZs

40 PAIRS DBMS_OUTPUT.PUT_LINE(rec_a.ename || ' ==> ' || rec_b.ename); UPDATE workers SET pair=rec_a.empno WHERE CURRENT OF curs_b; UPDATE workers SET pair=rec_b.empno WHERE CURRENT OF curs_a; ****** EXIT; *****  CLOSE curs_a; OPEN curs_a; SzaboZs

41 PAIRS With Open+Close: Without Open+Close:
BLAKE ==> JONES MARTIN ==> WARD ALLEN ==> TURNER FORD ==> SCOTT Without Open+Close: BLAKE ==> JONES JONES ==> FORD MARTIN ==> WARD ALLEN ==> TURNER WARD ==> MILLER FORD ==> SCOTT SzaboZs

42 PL/SQL BLOCK with exception
A separated area in the SQL*PLUS script-program, that is interpreted by the PL/SQL engine Syntax: SET SERVEROUTPUT ON [DECLARE {variables}] BEGIN {commands} [EXCEPTION {exception handling}] END; / SzaboZs

43 USER-DEFINED EXCEPTIONS
ACCEPT var PROMPT "Write something! "; DECLARE MyEx EXCEPTION; BEGIN IF &var=0 THEN RAISE MyEx; END IF; DBMS_output.put_line('Not zero.'); EXCEPTION WHEN MyEx THEN DBMS_output.put_line('Zero!'); END; / SzaboZs

44 AUTOMATIC EXCEPTIONS  When using SELECT INTO statement: NO_DATA_FOUND
TOO_MANY_ROWS OTHERS SzaboZs

45 AUTOMATIC EXCEPTIONS ACCEPT job PROMPT "Type in a job! ";
DECLARE emprec EMP%ROWTYPE; BEGIN SELECT * INTO emprec FROM emp WHERE job='&job'; dbms_output.put_line(emprec.ename); EXCEPTION WHEN No_Data_Found THEN DBMS_output.put_line('No record!'); WHEN TOO_MANY_ROWS THEN DBMS_output.put_line('Too much records!'); WHEN OTHERS THEN DBMS_output.put_line('Something else!'); END; / SzaboZs

46 PROCEDURE Parameters: {var.name} [{IN|OUT|IN OUT}] {type}
No size specification for types: VARCHAR2 SHOW ERROR; (select * from user_errors) RAISE_APPLICATION_ERROR (err.code, message); [ ] SzaboZs

47 PROCEDURE CREATE OR REPLACE PROCEDURE WriteIt IS BEGIN
WriteLn('SzZs'); END; / select * from user_errors; SzaboZs

48 PROCEDURE CREATE OR REPLACE PROCEDURE WriteLn (text VARCHAR2) IS
BEGIN text2 := text; dbms_output.put_line(text2); END; / SzaboZs

49 PROCEDURE ACCEPT some PROMPT "Write something "; BEGIN WriteIt;
WriteLn('&valami'); END; / SzaboZs

50 EXECUTION / DELETION Execution outside a PL-SQL block: Execute WriteIt; Execute WriteLn('haha'); Deletion: DROP PROCEDURE xxxxx; DROP PROCEDURE WriteLn; Execute WriteIt;  Invalid object SzaboZs

51 FUNCTION CREATE OR REPLACE FUNCTION CountIt (num1 NUMBER, num2 NUMBER)
RETURN NUMBER IS c NUMBER(6); sum NUMBER(6); BEGIN sum := 0; FOR c IN num1 .. num2 LOOP sum := sum + c; END LOOP; RETURN sum; END; / SzaboZs

52 FUNCTION ACCEPT first PROMPT "First num: ";
ACCEPT second PROMPT "Second num: "; BEGIN dbms_output.put_line(CountIt(&first, &second)); END; / DROP FUNCTION CountIt; SzaboZs

53 TRIGGER? trigger (n) = a piece (as a lever) connected with a catch or detent as a means of releasing it; esp: the part of the action moved by the finger to fire a gun to trigger (v) = to initiate, actuate, or set off by sg "Event“ RDBMS: a stored procedure, that runs in connection with some event ("event handler") An event can be: DELETE, INSERT, UPDATE Important: BEFORE, AFTER, INSTEAD OF SzaboZs

54 TRIGGER CREATE OR REPLACE TRIGGER {name} [BEFORE | AFTER | INSTEAD OF] {event} [OR {event} …] ON {table} [FOR EACH ROW [WHEN {condition}]] [DECLARE {variables}] BEGIN {commands} [EXCEPTION …] END; SzaboZs

55 TRIGGER If there is a RAISE_APPLICATION_ERROR() in the trigger's code, then the event that caused the trigger will not be executed (+ automatic ROLLBACK) FOR EACH ROW: the trigger is executed once for each row modified/deleted/inserted. Otherwise: once for every command SzaboZs

56 TRIGGER When using a row-level trigger, we have several automatically created variables: INSERT - :NEW DELETE - :OLD UPDATE - :NEW and :OLD SzaboZs

57 TRIGGER CREATE OR REPLACE TRIGGER SomeTrigger
BEFORE DELETE OR INSERT OR UPDATE ON emp FOR EACH ROW BEGIN IF INSERTING THEN DBMS_OUTPUT.PUT_LINE(NEW: ' || :NEW.ename); ELSIF DELETING THEN DBMS_OUTPUT.PUT_LINE(DEL: ' || :OLD.ename); ELSIF UPDATING THEN DBMS_OUTPUT.PUT_LINE(MOD: ' || :OLD.ename || ' ==> ' || :NEW.ename); END IF; END; / SzaboZs

58 TRIGGER delete from emp where ename='KING';
update emp set ename='SOME' where ename='JAMES'; insert into emp (empno, ename, mgr, deptno) values (1, 'NEWMAN', NULL, 20); DEL: KING 1 row deleted. MOD: JAMES ==> SOME 1 row updated. NEW: NEWMAN SzaboZs

59 TRIGGER CREATE OR REPLACE TRIGGER SomeTrigger2 BEFORE INSERT ON emp
FOR EACH ROW DECLARE MINSAL NUMERIC(6,2); BEGIN SELECT MIN(SAL) INTO MINSAL FROM EMP; IF (:NEW.sal < MINSAL) THEN RAISE_APPLICATION_ERROR(-20000, 'TOO LOW SALARY!'); END IF; END; / SzaboZs

60 TRIGGER insert into emp (empno, ename, mgr, deptno, sal) values (4, 'NEWMAN2', NULL, 20, 5000); insert into emp (empno, ename, mgr, deptno, sal) values (5, 'NEWMAN3', NULL, 20, 5); delete from emp where empno < 100; SzaboZs

61 TRIGGER UJ: NEWMAN2 1 row created.
insert into emp (empno, ename, mgr, deptno, sal) values (5, 'NEWMAN3', NULL, 20, 5) * ERROR at line 1: ORA-20000: TOO LOW SALARY! ORA-06512: at "SCOTT.SOMETRIGGER2", line 6 ORA-04088: error during execution of trigger 'SCOTT.SOMETRIGGER2' DEL: NEWMAN DEL: NEWMAN2 2 rows deleted. SzaboZs

62 TABLE-LEVEL TRIGGER CREATE OR REPLACE TRIGGER SomeTrigger2
BEFORE DELETE OR INSERT OR UPDATE ON emp BEGIN IF INSERTING THEN DBMS_OUTPUT.PUT_LINE('NEW REC'); ELSIF DELETING THEN DBMS_OUTPUT.PUT_LINE('DELETED REC'); ELSIF UPDATING THEN DBMS_OUTPUT.PUT_LINE('MODDED REC'); END IF; END; / SzaboZs

63 THINGS TO REMEMBER… A table-level trigger will be executed once even if affected_rows=0! INSTEAD OF: row-level trigger (even without FOR EACH ROW!), used with views WHEN: we can define conditions for row-level triggers using the :NEW and the :OLD variables DROP TRIGGER xxxx; ALTER TRIGGER xxxx ENABLE/DISABLE; SzaboZs

64 PL/* introduction PLSQL TSQL Trigger exercise
DBMAN 7 PL/* introduction PLSQL TSQL Trigger exercise SzaboZs

65 STANDALONE BLOCK No need for special engine-specific commands – only ONE execution engine! No need to use BEGIN and END; No need to use a special delimiter ($$ or /) Basically, any commands can be used in standard SQL and „procedural” SQL code! SzaboZs

66 VARIABLES (TSQL) DECLARE @name type; SET @name=value;
VARCHAR(50) = 'Yay, I have a value'; = ename FROM emp WHERE empno = 7788; -- only in procedures! PRINT 'Yay, I have an output'; SzaboZs

67 IF IF {condition} {commands} [ELSE {commands}]
Command can be a block of BEGIN .. END; SzaboZs

68 WHILE – no FOR loop! WHILE {condition} BEGIN -- code to execute. Ability to use BREAK/CONTINUE END; INT; = 0; <= 10 BEGIN PRINT 'Inside WHILE LOOP '; + 1; PRINT 'Done WHILE LOOP'; GO SzaboZs

69 DECLARING A CURSOR DECLARE db_cursor CURSOR FOR SELECT * FROM emp;
The query is not executed, we only declare that the given variable will refer to the given query We have to loop through the cursor, using a WHILE loop and a bunch of FETCH statements SzaboZs

70 USING CURSORS DECLARE (cursor, vars) OPEN FETCH Yes
CLOSE = 0 ? No Yes SzaboZs

71 USING CURSORS DECLARE db_cursor CURSOR FOR SELECT ename, sal FROM emp; int = int = varchar(100); OPEN db_cursor FETCH NEXT FROM WHILE = 0 BEGIN IF END CLOSE db_cursor; DEALLOCATE db_cursor; SzaboZs

72 USING CURSORS FOR MODIFICATIONS
DECLARE db_cursor CURSOR FOR SELECT ename, sal FROM emp FOR UPDATE OF sal; varchar(100); OPEN db_cursor FETCH NEXT FROM WHILE = 0 BEGIN PRINT ' ' -> UPDATE emp SET WHERE CURRENT OF db_cursor; END CLOSE db_cursor; DEALLOCATE db_cursor; SzaboZs

73 Function VS Procedure STORED PROCEDURE (SP)
USER DEFINED FUNCTION (UDF) Can return zero , single or multiple values (via parametrs) Must return a single value (which may be a scalar or a table) Transactions are allowed Transactions are NOT allowed Input/Output parameters Only input parameters Can call functions Cannot call procedures Cannot be used in SQL statements Can be used in SQL statements Exceptions allowed Exceptions not allowed SzaboZs

74 PROCEDURES / FUNCTIONS
Parameters: {var.name} [{IN|OUT|IN OUT}] {type} Must use size specification for types: VARCHAR(100) RAISERROR (msg, level, state) vs THROW code, msg, state Exceptions inside a block: BEGIN TRY END TRY BEGIN CATCH END CATCH SzaboZs

75 PROCEDURE IF object_id('WriteLn') IS NOT NULL DROP PROCEDURE WriteLn;
GO CREATE PROCEDURE WriteLn VARCHAR(100)) -- must use size AS BEGIN VARCHAR(100) END; SzaboZs

76 FUNCTION CREATE FUNCTION GetSquared(@num int) RETURNS int AS BEGIN
END; GO SzaboZs

77 Executing Execute dbo.WriteLn 'HELLO'; Execute dbo.GetSquared 55;
Select dbo.GetSquared(55); Select dbo.WriteLn('HELLO'); -- NOPE SzaboZs

78 TRIGGER? trigger (n) = a piece (as a lever) connected with a catch or detent as a means of releasing it; esp: the part of the action moved by the finger to fire a gun to trigger (v) = to initiate, actuate, or set off by sg "Event“ RDBMS: a stored procedure, that runs in connection with some event ("event handler") An event can be: DELETE, INSERT, UPDATE Important: AFTER, INSTEAD OF (!!! NO !!! BEFORE) Every trigger is table-level SzaboZs

79 TRIGGER CREATE TRIGGER {name} ON {table} {timing} {event} AS BEGIN
END; SzaboZs

80 Trigger Example IF object_id('SomeTrigger') IS NOT NULL DROP TRIGGER SomeTrig; GO CREATE TRIGGER SomeTrig ON emp INSTEAD OF INSERT AS BEGIN INT = (SELECT MIN(sal) FROM EMP); INT = (SELECT TOP 1 sal FROM inserted); IF THROW 50000, 'BAD SALARY', 1; -- +INSERT ??? END; INSERT INTO emp (empno, sal, deptno) VALUES (10, 10, 10); INSERT INTO emp (empno, sal, deptno) VALUES (10, 10000, 10); SzaboZs

81 PL/* introduction PLSQL TSQL Trigger exercise
DBMAN 7 PL/* introduction PLSQL TSQL Trigger exercise SzaboZs

82 10.5 Must forbid the insertion of workers who don't apply to some rules The condition-checks are done by separated functions! The trigger does nothing complicated: Row-level trigger (Oracle) / Loops through the inserted rows (TSQL) For every row, it calls the appropriate functions with the inserted fields Throws an exception if the record is considered not correct SzaboZs

83 A TYPICAL TRIGGER (ORACLE)
create or replace trigger Checks before insert on xxx for each row DECLARE Ex1 Exception; Ex2 Exception; BEGIN if (Func1(:NEW.field)<>1) then Raise Ex1; end if; if (Func2(:NEW.field1, :NEW.field2)<>1) then Raise Ex2; end if; EXCEPTION WHEN Ex1 THEN Raise_Application_Error(-20500, 'Error1'); WHEN Ex2 THEN Raise_Application_Error(-20501, 'Error2'); WHEN OTHERS THEN Raise_Application_Error(-20999, 'Error'); END; / SzaboZs

84 A TYPICAL TRIGGER (TSQL)
CREATE TRIGGER Checks ON xxx INSTEAD OF INSERT AS BEGIN DECLARE db_cursor CURSOR FOR SELECT ID, f1, f2 FROM inserted; int; OPEN db_cursor FETCH NEXT FROM @f2 WHILE = 0 BEGIN if THROW ex1 if THROW ex2 INSERT INTO xxx SELECT * FROM inserted WHERE FETCH NEXT FROM @f2 END CLOSE db_cursor; DEALLOCATE db_cursor; END; GO SzaboZs

85 Rules  create table dx as select * from emp;
The mgr field must point to an already existing boss No boss can have more than two employees The job must be an already existing job The salary must be bigger than the average salary of the job SzaboZs

86 SzaboZs

87 SzaboZs


Download ppt "PL/* introduction PLSQL TSQL Trigger exercise"

Similar presentations


Ads by Google