Download presentation
Presentation is loading. Please wait.
Published byCecilia McKenzie Modified over 6 years ago
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
Similar presentations
© 2025 SlidePlayer.com Inc.
All rights reserved.