Oracle 11g xe Tutorial 7. PL/SQL Cursor, Procedure, Function, Trigger
I.Cursor
Because DML statement in PL/SQL just allow return one result from table, but when DML statement return a result set, like select * from table_name
, we need to use cursor, cursor in fact is a pointer points to this result set, just like iterator in java.
Usage of cursor
- Use
cursor cursor_name is subquery;
indeclare block
to declare a cursor - Use
open cursor_name;
to open the cursor, now PL/SQL execute the subquery to get the result - Use
fetch cursor_name into variable
to get the data from the result set, and store them into the variable, fetch likeiterator.next
in Java - Use
close cursor_name
to clear the cursor
Example
# Use cursor to get first ename in table emp
SQL> declare
2 cursor c is
3 select * from emp;
4 v_temp c%rowtype;
5 begin
6 open c;
7 fetch c into v_temp;
8 dbms_output.put_line(v_temp.ename);
9 close c;
10 end;
11 /
# Get all the enames in table emp;
# Use Loop
SQL> declare
2 cursor c is
3 select * from emp;
4 v_temp c%rowtype;
5 begin
6 open c;
7 loop
8 fetch c into v_temp;
9 exit when (c%notfound);
10 dbms_output.put_line(v_temp.ename);
11 end loop;
12 close c;
13 end;
14 /
# Use While loop
SQL> declare
2 cursor c is
3 select * from emp;
4 v_temp c%rowtype;
5 begin
6 open c;
7 fetch c into v_temp;
8 while(c%found) loop
9 dbms_output.put_line(v_temp.ename);
10 fetch c into v_temp;
11 end loop;
12 end;
13 /
# Use For loop
SQL> declare
2 cursor c is
3 select * from emp;
4 begin
5 for v_temp in c loop
6 dbms_output.put_line(v_temp.ename);
7 end loop;
8 end;
9 /
cursor_name%notfound
means not data in result set, here just exit the loop- For loop will manager open cursor, close cursor, and fetch cursor automatically
Cursor with argument
cursor cursor_name(arg_1_name, arg_1_type, arg_2_name, arg_2_type ...) is subquery
- Just like the function call in Java, cursor here just like a function
SQL> declare
2 cursor c (v_deptno emp.deptno%type, v_job emp.job%type)
3 is
4 select ename, sal from emp where deptno = v_deptno and job = v_job;
5 v_temp c%rowtype;
6 begin
7 for v_temp in c(30, 'CLERK') loop
8 dbms_output.put_line(v_temp.ename);
9 end loop;
10 end;
11 /
Cursor with update
- Use cursor to update table
cursor cursor_name is subquery for update;
cuurent of c
means current record in cursor
SQL> declare
2 cursor c is
3 select * from emp_2 for update;
4 v_temp c%rowtype;
5 begin
6 for v_temp in c loop
7 if(v_temp.sal < 2000) then
8 update emp_2 set sal = sal * 2 where current of c;
9 elsif (v_temp.sal = 5000) then
10 delete from emp_2 where current of c;
11 end if;
12 end loop;
13 commit;
14 end;
15 /
II. Procedure
Use procedure to assign a name to a code block.
Create a Procedure
# Grant privilege to scott
SQL> conn sys as sysdba
Enter password:
Connected.
SQL> grant create procedure to scott;
# Create procedure
SQL> create or replace procedure p
2 is
3 cursor c is
4 select * from emp_2 for update;
5 begin
6 for v_emp in c loop
7 if(v_emp.deptno = 10) then
8 update emp_2 set sal = sal + 10 where current of c;
9 elsif(v_emp.deptno = 20) then
10 update emp_2 set sal = sal + 20 where current of c;
11 else
12 update emp2 set sal = sal + 50 where current of c;
13 end if;
14 end loop;
15 commit;
16 end;
17 /
# Excute procedure
SQL> exec p;
# Or
SQL> begin
2 p;
3 end;
4 /
Procedure with argument
create or replace procedure p (arg_1_name in arg_1_type, arg_2_name out arg_2_type ...)
in
: pass value in procedure, default isin
out
: return value out of procedurein out
: means it can pass in or return out
# Create procedure with argument
SQL> create or replace procedure p
2 (v_a in number, v_b number, v_ret out number, v_temp in out number)
3 is
4 begin
5 if(v_a > v_b) then
6 v_ret := v_a;
7 else
8 v_ret := v_b;
9 end if;
10 v_temp := v_temp + 1;
11 end;
12 /
# Invoke procedure
SQL> declare
2 v_a number := 3;
3 v_b number := 4;
4 v_ret number;
5 v_temp number := 5;
6 begin
7 p(v_a, v_b, v_ret, v_temp);
8 dbms_output.put_line(v_ret);
9 dbms_output.put_line(v_temp);
10 end;
11 /
Procedure Error
When you write the procedure, if there is some errors, sqlplus won’t give you where the error occurs. If you want to check which line in your code has an error, use:
SQL> show error
Delete procedure
Use drop procedure procedure_name
to delete a procedure.
III. Function
Create a function
# Create a fucntion
SQL> create or replace function sal_tax
2 (v_sal number)
3 return number
4 is
5 begin
6 if(v_sal < 2000) then
7 return 0.10;
8 elsif(v_sal < 2750) then
9 return 0.15;
10 else
11 return 0.20;
12 end if;
13 end;
14 /
# Invoke the function
SQL> select lower(ename), sal_tax(sal) from emp;
IV. Trigger
Trigger is used for some condition has been satisfied or triggered.
# Grant privilege
SQL> conn sys as sysdba
Enter password:
Connected.
SQL> grant create trigger to scott;
Grant succeeded.
# Create a table to record actions
SQL> create table emp2_log
2 (
3 uname varchar2(20),
4 action varchar2(10),
5 atime date
6 );
# Create a trigger to record actions
SQL> create or replace trigger trig
2 after insert or delete or update on emp_2 for each row
3 begin
4 if inserting then
5 insert into emp2_log values (USER, 'insert', sysdate);
6 elsif updating then
7 insert into emp2_log values (USER, 'update', sysdate);
8 elsif deleting then
9 insert into emp2_log values (USER, 'delete', sysdate);
10 end if;
11 end;
12 /
# Execute a DML statement
SQL> update emp_2 set sal = sal * 2 where deptno = 30;
# See the result in emp2_log
SQL> select * from emp2_log;
USER
means current user who operates databasefor each row
means when you modify one row in table, the trigger will be triggered once
Sometimes, you can not update a column by using when this column has been referenced, here you could use trigger to do this:
# Wrong Example
SQL> update dept set deptno = 99 where deptno = 10;
update dept set deptno = 99 where deptno = 10
*
ERROR at line 1:
ORA-02292: integrity constraint (SCOTT.FK_EMP_DEPT) violated - child record
found
# So here use trigger to do this
SQL> drop trigger trig;
SQL> create or replace trigger trig
2 after update on dept
3 for each row
4 begin
5 update emp set deptno = :NEW.deptno where deptno = :OLD.deptno;
6 end;
7 /
# Update dept
SQL> update dept set deptno = 99 where deptno = 10;
- one update statement will create a new state and a old state of one table, the old state is the state before updating, the new state is the state after updating.
:NEW
means this is the new state of the table:OLD
means this is the old state of the table- So here,
deptno = 10
is the old state,deptno = 99
is the new state - because table
emp
has the foreign key ofdeptno
referenced to tabledept
, so, here when we update the tabledept
, the trigger has been triggered, it will update tableemp
at the same time, so update statement can be executed here - So here the trigger has more higher priority then constraint
V. Tree Structure & Recursive
We can also build tree structure in oracle db:
- If the node has no father, it’s a root node
- If the node has no child, it’s a leaf
So in a table, we can create a column to store the id of father node, pid
, also we need a column to indicate if this node is a child or not. To traverse the tree, simply, we could find the root, and find the children of root, and children of children, etc. So in fact, it’s a recursion
problem.
# Create a table
SQL> create table article
2 (
3 id number primary key,
4 cont varchar2(4000),
5 pid number, --id of father node
6 isleaf number(1), -- 0 false, 1 true
7 alevel number(2) -- which level of node
8 );
# Insert data
SQL> insert into article values (1, 'an ant fights with an elephant', 0, 0, 0);
SQL> insert into article values (2, 'the elephant lost!', 1, 0, 1);
SQL> insert into article values (3, 'the ant also hurts', 2, 1, 2);
SQL> insert into article values (4, 'it''s not true', 2, 0, 2);
SQL> insert into article values (5, 'it''s true', 4, 1, 3);
SQL> insert into article values (6, 'how it's possible!', 1,0,1);
SQL> insert into article values (7, 'why it''s not possible?', 6, 1, 2);
SQL> insert into article values (8, 'it can be true', 6,1,2);
SQL> insert into article values (9, 'the elephant went to hospital', 2, 0, 2);
SQL> insert into article values (10, 'nurse is an ant', 9, 1, 3);
SQL> commit;
# Create a procedure p1 to show the result without indention
SQL> create or replace procedure p
2 (v_pid article.pid%type, v_level) is
3 cursor c is
4 select * from article where pid = v_pid;
5 begin
6 for v_article in c loop
7 dbms_output.put_line(v_article.cont);
8 if(v_article.isleaf = 0) then
9 p(v_article.id);
10 end if;
11 end loop;
12 end;
13 /
# Excute the procedure p1
SQL> set serveroutput on
SQL> exec p(0);
# Create a procedure p2 to show the result with proper indention
SQL> create or replace procedure p (v_pid article.pid%type, v_level binary_integer) is
2 cursor c is
3 select * from article where pid = v_pid;
4 v_preStr varchar2(1024) := '';
5 begin
6 for i in 0..v_level loop
7 v_preStr := v_preStr || '****';
8 end loop;
9 for v_article in c loop
10 dbms_output.put_line(v_preStr || v_article.cont);
11 if(v_article.isleaf = 0) then
12 p (v_article.id, v_level + 1);
13 end if;
14 end loop;
15 end;
16 /
# Execute procedure
SQL> exec p(0, 0);
# The result is a tree structure
Share this on