You are on page 1of 89

Some sample query for sql/plsql

ROWNUM

?1a. Select FIRST n records from a table

select * from emp where rownum <= &n;

?1b. Select LAST n records from a table

select * from emp minus select * from emp


where rownum <= (select count(*) - &n from emp);

?2a. Select * from emp where ROWNUM >= 1; --- what happens?

## returns all rows of table

?2b. Select * from emp where ROWNUM > 1; --- what happens?

## gives ‘NO ROWS SELECTED’

?2c. Select * from emp where ROWNUM > 0; --- what happens?

## returns all rows of table

?2d. Select * from emp where ROWNUM = 1; --- what happens?

## Yes, this returns the 1st row

?2e. Select * from emp where ROWNUM > 5; --- what happens?

## gives ‘NO ROWS SELECTED’

?2f. Select * from emp where ROWID is null; --- what happens?

## gives 'NO ROWS SELECTED'

1
?2g. Select * from emp where ROWID is not null; --- what happens?

## Returns all rows from the table.


Decode()

?3. To select ALTERNATE(EVEN NUMBERED) records from a table

select * from emp where rowid in (select


decode(mod(rownum,2),0,rowid,
null) from emp);

## here in the DECODE function,


1st argument (mod(rownum,2)) is expression to check,
2nd argument (0) is a search value,
3rd argument (rowid) is to return the even rows if expr returns 0,
4th argument (null) is to return no rows if expr does not return 0.

?3a. To select ALTERNATE(ODD NUMBERED) records from a table

select * from emp where rowid in (select


decode(mod(rownum,2),0,null,
rowid) from emp);

?4. If sal >= 2000, increment salary by 1000


else return sal from emp table

select ename,empno,job,sal,
decode(mod(sal,2000),sal,sal,
sal+1000) salinc from emp;

n AND nth MAXIMUM & MINIMUM SALARIES

?5. Find the 3rd MAX salary in the emp table

select distinct sal from emp a


where 3 = (select count(distinct sal) from emp b
where a.sal <= b.sal); -- sals in descending order

## The count() function in the subquery is set to return the count of each distinct salaries
arranged in descending order (since <= is used here (i.e for max '<=' is used)). So when
it reaches the 3rd record (i.e count is 3) its corresponding sal will be returned finally.

for eg.,

2
emp a emp b
4000 4000
3000 3000
2500 2500
1800 1800

?6. Find the 3rd MIN salary in the emp table

select distinct sal from emp a


where 3 = (select count(distinct sal) from emp b
where a.sal >= b.sal); -- sals in ascending order

?7. Find 3 MAX salaries in the emp table

select distinct sal from emp a


where 3 >= (select count(distinct sal) from emp b
where a.sal <= b.sal) order by a.sal desc;

?8. Find 3 MIN salaries in the emp table

select distinct sal from emp a


where 3 >= (select count(distinct sal) from emp b
where a.sal >= b.sal); select distinct hiredate from emp a
where &n = (select count(distinct to_char(hiredate,'ddd'))
from emp b where a.hiredate <= b.hiredate);

?9. Find the nth MAX date from emp table

select distinct hiredate from emp a


where &n = (select count(distinct to_char(hiredate,'ddd'))
from emp b where a.hiredate <= b.hiredate);
or
select distinct hiredate from emp a where
3 = (select count(distinct hiredate) from emp b where
a.hiredate <= b.hiredate);

## here no need of converting date to char datatype as in 1st query

?10. Delete DUPLICATE records based on deptno

delete from emp a where rowid !=


(select max(rowid) from emp b where

3
a.deptno=b.deptno);

## this query will retain all the records which are having unique deptno that are having maximum
rowid and delete the rest duplicate ones ie., which are having repeated deptnos.

?11. Select DISTINCT RECORDS from emp table

select * from emp a where


rowid = (select max(rowid) from emp b where
a.empno=b.empno);

## here we can have 'in' also instead of '='

17/06/97 **** JOINS ****


OUTER JOIN

?1. List employees' names and their managers' names (USING


OUTER JOIN)

select lo.ename "EMP NAMES", hi.ename "MGR NAMES" from


emp lo, emp hi where lo.mgr = hi.empno(+);

## where (we can find the empno against each manager) each lo.mgr is a manager of a
hi.empno
or
select a.empno,a.ename,a.mgr,b.ename from emp a, emp b where
b.ename = (select b.ename from emp b where a.mgr = b.empno) ;
or
select a.empno,a.ename,a.mgr,b.ename from emp a, emp b where
where a.mgr = b.empno ;

?2. List Dept no., Dept name for all the departments in which there
are no employees in the department (REPEATED QUESTION
NO(3) BELOW UNDER “INTERVIEW QUESTIONS”)

select empno,ename,b.deptno,dname from emp a, dept b


where a.deptno(+) = b.deptno and empno is null; -- (1)

## (1) gives empno and ename as null also


or

select * from dept where deptno not in (select deptno from emp); -- (2)
or

4
select * from dept a where
not exists (select * from emp b where
a.deptno = b.deptno); -- (3)

## (2) & (3) GIVE THE SAME RESULT as (1)

?3. List the count of number of employees in each department (REPEATED QUESTION NO(4)
BELOW UNDER “INTERVIEW QUESTIONS”)

select count(EMPNO), b.deptno, dname from emp a, dept b


where a.deptno(+)=b.deptno
group by b.deptno,dname;

## NOTE : Here if we give count(*) it counts as 1 for deptno = 40. So we must give
count(empno) only for our required correct output.

?4a. Creating a table 'parts' with some constraints

create table parts(


partno number(2),
partname char(15),
alt_partno number(2) references parts(partno),
constraint pky primary key(partno));

## 1st record insertion shd be null for alt_partno

eg. insert into parts values (09,'refil',null);

?4b. List all partnames and their alternate partnames

select a.partname "PARTNAME" ,b.partname "ALT PARTNAME" from


parts a, parts b where
b.partno(+) = a.alt_partno

?5. Details of employees along with their location (EQUI JOIN)

select empno,ename,a.deptno,b.dname,b.loc
from emp a, dept b where a.deptno = b.deptno;

?6. Details of ALLEN along with his location

select empno,ename,a.deptno,b.dname,b.loc
from emp a, dept b where a.deptno=b.deptno
and ename = 'ALLEN';

5
?7. List down the employees working in CHICAGO

select empno,ename,loc from


emp a, dept b where a.deptno=b.deptno and
loc = 'CHICAGO';

?8. List down details of employees getting sal < that of ALLEN

select b.empno, b.ename,b.deptno,b.sal


from emp a, emp b where a.ename = 'ALLEN'
and b.sal <= a.sal;
or
select empno,ename,deptno,sal from emp
where sal < (select sal from
emp where ename = 'ALLEN');

19/06/97 **** VIEWS ****

## information on table views can be obtained from the table


called USER_VIEWS ##

SINGLE TABLE VIEWS

SQL> create view empv1 as select * from emp;

.To create a view and insert - ( To insert a record in a view, the view
should consist of the NOT NULL (MANDATORY) column of the
base table)

SQL>create view emp_view as select ename,job,sal from


emp where sal <1500;

## view created

SQL>insert into emp_view values('xxx','clerk',1000);

SQL> insert into emp_view values('xxx','clerk',1000)


2 *
3 ERROR at line 1:
4 ORA-01400: mandatory (NOT NULL) column is missing or NULL during insert

CREATE VIEWS WITH THE CHECK OPTIONS

SQL> create or replace view empview as select


sal,empno,ename from emp where sal>1500;

6
## in the above case no record can be inserted into the table through the view EMPVIEW if the
value of sal<1500, however the record can be inserted into the table directly.

SQL> create or replace view empview sal,empno,ename,dname


as select sal,empno,ename,dname from emp a, dept b where
a.deptno=b.deptno and sal >1500 with check option;

## information on the check option can be obtained from the table called ALL_VIEWS

To create a view with 'with check option' and updating

SQL>create view emp_view as select ename,job,sal


from emp where sal <1500 with check option;

## View created.

SQL>update emp_view set sal=sal + 1000;


update emp_view set sal=sal + 1000
*
ERROR at line 1:
ORA-01402: view WITH CHECK OPTION where-clause violation

MULTI TABLE VIEWS

SQL>create or replace view empview


as select empno,ename,a.deptno,dname from
emp a, dept b where a.deptno=b.deptno;

## in complex table views no update,insert, etc.(i.e. DML commands) will be applicable

SQL>create view emp_dept_view as


select empno,ename,a.deptno,dname from
emp a,dept b where a.deptno=b.deptno;

SQL>create view dept_grp_view as


select deptno,max(sal) "max sal" from
emp group by deptno;

CREATE VIEW FROM A NON EXISTING TABLE USING 'FORCE' KEYWORD

SQL> create FORCE view v1 as select * from aaa;

Warning: View created with compilation errors.

CREATING VIEWS WITH COLUMNS RENAMED

create or replace view empview (emp_number,emp_name,salary,dept_name)


as select empno,ename,sal,dname from emp a, dept b where
a.deptno=b.deptno;

7
ALTERING VIEWS

alter view empview compile;

## this command is useful if the table was altered using the command "alter table ...." , the
command now regenerates the view ###

TO DROP A VIEW

drop view empview ;

21/06/97 **** SET OPERATORS ****


1. MINUS

SQL> select job from emp where deptno=20 MINUS


select job from emp where deptno=30;
or
SQL> select distinct
job from emp where deptno=20 and
job not in (select job from emp where deptno=30);

## Here if we don't give distinct in subquery then repeated jobs may be displayed if job
is repeated in the dept=20.

2. UNION

SQL> select job from emp where deptno=10 UNION


select job from emp where deptno=20;
or
SQL> select distinct job from emp where deptno in(10,20);
or
SQL> select distinct job from emp where deptno=10 or deptno=20;

3. UNION ALL

SQL> select job from emp where deptno=10 UNION ALL


select job from emp where deptno=20;
or
SQL> select job from emp where deptno in(10,20);

4. INTERSECT

SQL> select job from emp where deptno=20 INTERSECT


select job from emp where deptno=30;
or
SQL> select distinct job from emp where deptno=20
and job in (select distinct job from emp where deptno=30);

8
26/06/97 **** INT QUESTIONS (QUERIES) ****

?1. Produce the following report:


dept no dept name avg_sal of dept

select b.deptno "dept no",dname "dept name",avg(sal) "avg sal of dept"


from emp a, dept b where a.deptno = b.deptno
group by b.deptno, dname;

?2. Produce the above report for sal > 2000

select b.deptno "dept no",dname "dept name",avg(sal) "avg sal of dept"


from emp a, dept b where a.deptno = b.deptno
group by b.deptno, dname having avg(sal) > 2000;

?3. List dept no., Dept name for all the departments in which there are
no employees in the department (REPEATED QUESTION NO.(2)
AT TOP UNDER “JOINS”)

select empno,ename,b.deptno,dname from emp a, dept b


where a.deptno(+) = b.deptno and empno is null; -- (1)

## (1) gives empno and ename as null also


or
select * from dept where deptno not in (select deptno from emp); -- (2)
or
select * from dept a where
not exists (select * from emp b where
a.deptno = b.deptno); -- (3)

## (2) & (3) GIVE THE SAME RESULT as (1)

?4. List the count of number of employees in each department


(REPEATED QUESTION NO(3) AT TOP UNDER “JOINS”)

select count(EMPNO), b.deptno, dname from emp a, dept b


where a.deptno(+)=b.deptno
group by b.deptno,dname;

## NOTE : Here if we give count(*) it counts as 1 for deptno = 40. So we must give count(empno)
only for our required correct output.

9
?5. List ename,sal,new salary with 25% rise in existing sal for those
employees who have present sal< 2000 and are from departments
10 & 20

select ename, deptno, sal, sal+(sal*.25) " new sal" from emp
where sal < 2000 and deptno in (10,20);

?6. List empno,ename,sal,manager's name, manager's sal for all


employees who earn more than their manager

select a.empno empno, a.ename name, a.sal "emp sal",


b.ename "mgr name", b.sal "mgr sal" from emp a, emp b
where a.mgr = b.empno and a.sal > b.sal;

?7. List the name and salary of all the employees working in all the
departments who earn more salary than everybody in deptno=20

select ename, deptno, sal from emp where


sal > (select max(sal) from emp where deptno=30);

?8. Copy only the structure(not records) from an existing table while
creating a table

create table empp as select * from emp where 1= 2;

## Here 1=2 is a false condition hence copies only the structure of emp to empp table(but not the
records)

17/07/97 **** SOME QUESTIONS ****

?1. Create referential integrity on emp table with respect to deptno on


deptno of dept table

alter table emp add (constraint emp_fk foreign key(deptno)


references dept(deptno));

?2. Create a check to ensure that only ename and job is entered in
uppercase

alter table emp1 add (constraint nm_upp check(ename = upper(ename)),

10
constraint job_upp check(job = upper(job)));

?3. List all employees who have joined before the 'president'

select empno, ename, hiredate from emp1 where hiredate < (select hiredate
from emp1 where job = 'PRESIDENT');

?4. List all employees who have joined after 12th march 1982

select * from emp where hiredate > '12-MAR-82';

?5. Assuming hiredate as birthdate of an employee list hiredate and


retirement of all employees

select empno,ename,hiredate,
to_number(to_char(hiredate,'yy')) + 58 "RETIREMENT DATE" from
emp; /* not correct */

?6. List empno,ename,'Manager' or 'Staff', sal with an employee


appearing only once in the list. Print 'Manager' for a manager
and 'Staff' for non-managers

select empno,ename,sal, decode(job,'MANAGER','Manager','Staff')


from emp ;

?7. List the empno,ename,sal of the first 5 highest paid employees.


(Do not use the rownum or rowid functions. Use only std SQL
features)

select max(sal) from emp where sal <


(select max(sal) from emp ) <
(select max(sal) from emp where sal <
(select max(sal) from emp where sal <
(select max(sal) from emp ))));

?8. List the command to produce the following effect on the dept
table

11
before after
deptno deptno
10 10
12 20
17 20
22 30
26 30
30 30
32 40

update dept set deptno = round(deptno+5,-1) where mod(deptno,10)!=0;

?9. If the salary of Clerks does not fall in the range 0-1000 then
display 'Not Valid' against the clerks' names

select empno,ename,sal,decode(sign(sal), sign(1000-sal),'Within Range','Not Valid')


from emp where job = 'CLERK';

## select empno,sal,sign(sal),sign(sal-1000) from emp

?10. Find all departments which have more than 3 employees

select deptno,count(empno) from emp


group by deptno having count(empno) >= 3;

********************************************************************************
shuba
********************************************************************************

1. Creation of a new table

Create table dept (dept number (2), dname char(20));

2. Creation of a table from another table

create table emp1 as select * from emp;

3. Copy of structure only from another table

create table emp1 as select * from emp where 1=2

## (1=2 is a false condition hence only the stucture is copied)

12
4. To insert at macro level into a table *

insert into emply values (&no,'&name',&dept,'&job','&dob','&jn_dt',&pay);

5. To append data from one table to another

insert into emp1 select * from emp;

6. Creation of constraints on a new table

create table emp1


(empno number(5) primary key,
ename varcha2(20) not null,
sal number(10,2) check (sal>1000),
phone number(6) unique);

## here the constraint name is alloted by oracle

7. Creation of contraints on a new table with contraint names

create table emp1 (empno number(5) constraint pk primary key,


ename varcha2(20) constraint nt not null,
sal number(10,2) constraint ch check (sal>1000),
phone number(6) constraint un unique);

## if constraint name is not provided by the owner the system by default assigns a contraint name
in the format "SYS_C-----" where ----- is a continuous number

8. Creation of constraint with "REFERENCES" Table called DEPT


should be existing where the column Called deptno with primary
key constraint

create table emp1


(empno number(5) constraint pk primary key,
ename varcha2(20) constraint nt not null,
deptno NUMBER(2) references dept(deptno) [on delete cascade],
sal number(10,2) constraint ch check (sal>1000),
sex char constraint ck (sex in ('M','F')),
phone number(6) constraint un unique);

## the syntax in square brackets is optional, i.e deleting any row in DEPT TABLE will delete all
related rows in the dependent table

13
9. Creation of constraint on an existing table (egs)

SQL>alter table emp add (constraint FK foreign key (deptno)


references dept(deptno) [on delete cascade]);

SQL>alter table emp add constraint pk primary key(empno);

SQL>alter table emp1 add (constraint pk primary key(empno),


constraint fk1 foreign key (mgr) references emp1(empno)
on delete cascade);

SQL> alter table emp add constraint pk primary key(empno,phone)

## pk creates a composite primary key on two fields

SQL>delete from emp1 where empno = (select empno from emp1


where ename = 'KING');

10. To drop a constraint on a table

alter table dept1 drop constraint pk;

## refer constraint name in table called USER_CONSTRAINTS

11. To drop the main tables, its constraints with its referencing tables

drop table dept cascade constraints ;

## drops all referential integrity constraints referring to the keys in the dropped table also , does
drop the dependent tables.

12. Creation of table with DEFAULT values

create table emp1


(empno number(5) primary key,
ename varcha2(20) not null,
sal number(10,2) default 3500,
phone number(6) unique);

## Strictly speaking DEFAULT is not a constraint since this information will not be available in
the user_constraints table

## DEFAULT clause is applicable to tables with more than one column only

##To find out DEFAULT setup values on a table, check in table called
"USER_TABS_COLUMNS”

14
## to find the information on only primary key and foreign keys check in table called
"USER_CONS_COLUMNS "

13 To modify default clause on a column

alter table emp modify (sal default null);

14. Cascade constraints - to only drop constraints and its references


that are related in other dependent tables.

alter table dept drop constraint pk cascade;

## here any table referencing this table DEPT for pk will drop automatically the constraint on all
dependent tables

15. To disable/enable a constraint on a table

SQL>alter table dept disable constraint pk ;

## here pk is the primary key of the column

SQL> alter table dept enable constraint nt;

## here nt is the not null of the column

16. Display of data with spaces in the alias names

select deptno "Dept no " from dept1;

17. To obtain the retirement date for any person

select add_months(last_day('&dob'),58*12)
"Retirement Date" from dual;/* not correct*/

select to_char(hiredate,'dd-mon-yyyy'),
to_char(add_months(last_day(hiredate),58*12),'dd-mon-yyyy')
from emp;

##the above query works correctly dealing with year 2000.

15
18. To obtain the manager's name for a person in the emp table
(REFER JOINS-’OUTER JOIN)

select a.empno,a.ename,a.mgr,b.ename from emp a, emp b where


b.ename = (select b.ename from emp b where a.mgr = b.empno) ;
or
select a.empno,a.ename,a.mgr,b.ename from emp a, emp b where
where a.mgr = b.empno ;

19. To find the no. of months left in service

select months_between(add_months(last_day('16-MAY-64'),58*12),
to_date(sysdate)) "No.Of months" from dual;
or
select months_between(to_date('31-MAY-2022','DD-MON-YYYY'),
to_date(sysdate)) from dual;

## here in to_date(sysdate) no need to give ‘to_date’


PSEUDOCOLUMNS

20. To select only the 1st row from any table

select * from emp where rownum =1;

## only rownum operates with the literal 1 for the 1st row for eg. "where rownum = 5" is wrong

21. To display the hierarchial order of emp table

select level, ename EMPNAME, job


from emp start with job = 'PRESIDENT' connect by prior empno=mgr;

22. To select from table with subquieries

select * from emp where ename =


(select ename from emp where empno = '7566');

## here the subquery returns only one value ( or row) if the SQ returns more than one row then an
error message occurs

23. To select from table where the SQ returns more than one row

SQL>select * from emp where sal in


(select sal from emp where deptno = 20);

16
## for multiple rows returned the SQ must be preceded with the clause "IN" or "NOT IN" or
"ANY" or "ALL"

## the "=" clause will yield error

SQL> select * from emp where exists (select deptno from deptno)

## if the SQ following 'exists' returns atleast one row then the main query returns all the rows

SQL> select dept.deptno,dept.dname from dept where


dept.deptno = (select emp.deptno,dept.dname from emp,dept
where emp.deptno!=dept.deptno);

24. Select with more than one column

select * from emp where (job,deptno) in


(select job,deptno from emp where sal>1000);

25. Select with Multiple sqs

select * from emp where empno in


(select empno from emp where deptno =
(select deptno from dept where dname='SALES'));

26. To select 1st 'm' rows and last 'n' rows from a table

select * from emp where rownum <= &m union


(select * from emp minus
(select * from emp where rownum <=
(select count(*) - &n from emp)));

27. To select a range of rows from a table say starting from rownum =
's' and say 'r' rows

??? select * from emp where rownum <=(s+r) intersect


(select * from emp minus
(select * from emp where rownum <=
(select count(*)-(s+r) from emp))) ;

## say rownum 5 to 8

select * from emp where rownum <=8 intersect


(select * from emp minus
(select * from emp where rownum <=

17
(select count(*)-13 from emp))) ;

28a. Select with group by and having clause - where clause is also
applicable

select sum(sal),deptno,nvl(sum(comm),0) from emp1 group by deptno ;

28b. Selecting avg(sal) individual average sal of various departments

select a.deptno,dname,avg(sal) from emp a, dept b


where a.deptno=b.deptno
group by a.deptno,dname;

28c. Conditional group by

select a.deptno,dname,avg(sal) from emp a, dept b


where a.deptno=b.deptno
group by a.deptno,dname having avg(sal) >2000;

## in a group by clause always the "selected columns" should be listed in the columns following
the group by clause

29. Order by clause with ascending/descending

select * from emp where sal > 2000 order by sal desc, ename asc;

30. EQUI JOIN conditions i.e. '=' operator

select empno,ename,dname from emp,dept where emp.deptno=dept.deptno;

31. OUTER JOIN conditions (REFER JOINS-OUTER JOINS)

##to select all departments

select b.deptno,dname,empno,ename from dept b,emp a where


b.deptno=a.deptno(+) and b.deptno in (select b.deptno from dept);

##to select only departments where no employees are present

select b.deptno,dname,empno,ename from dept b,emp a where


b.deptno=a.deptno(+) and b.deptno in (select b.deptno from dept)
and empno is null;

18
or
select deptno,dname from dept where deptno not in (select deptno
from emp);
or
select deptno,dname from dept a where not exists
(select * from emp b where a.deptno = b.deptno)

32. SELF JOIN conditions applicable only for common tables

SQL> select a.ename,a.job, b.ename from emp a, emp b where a.empno=b.empno;

SQL> select a.empno "Emp No",a.ename "Emp Name", a.mgr "Mgr No",
b.ename "Mgr Name" from emp a, emp b where
b.ename = (select ename from emp where a.mgr=empno);

33. NON_EQUI join conditions

select sal,empno from emp,dept where dept.deptno=emp.deptno


and sal between 1000 and 3000;

****INDEXES****

## information on indexes is available in the table called USER_INDEXES

Creating indexes on tables

create index empndx on emp (empno asc, sal desc, mgr desc);

## Only a maximum of 16 columns can be included in the index column this is same as primary
key where only 16 columns can be included at a time

create an UNIQUE INDEXES

create unique index empuniq on emp (empno,mgr,sal);

To drop an index

drop index empuniq;

19
How to disable an index

## actually no index can be disabled, only an index usage can be avoided through an SQL
statement

## for if there is an index table called empndx on the empno then the following SQL fools the
index

select * from emp where empno + 1 - 1 = 7902;

## here actually there is no index by name empno+1-1 and hence can be avoided

****SEQUENCES ****

To create a sequence which is also a database object

## always the 2 psuedocolumns - nextval & currval are affiliated with sequences

create sequence empno


increment by 2
start with 100
maxvalue 500
minvalue 150 - gives error
cycle
cache 25; -- here 25 is the ready memory of numbers

## START WITH <value> cannot be less than MINVALUE

To select from sequences

## always once the sequence is created to know the current val first use "select empno.nextval
from dual" and then use the command "select empno.currval/empno.nextval from dual"

SQL> select empno.nextval from dual;

SQL> select empno.currval from dual;

To alter the sequence

alter sequence empno increment by .....

How to generate a sequence without using the sequences

select max(empno)+1 from emp;

20
If the sequence is for the very first instance then

select nvl(max(empno),0)+1 from dual


update emp set empno = nvl(max(empno),0)+1 where

## information on the sequences can be obtained from the table called USER_SEQUENCES

To get the listing of sequences already created

select * from cat; -- catalogue table

## synonym table name for cat is user_catalog;

**** CREATION OF CLUSTERS ****

SQL> create cluster clust (deptno number(2));

SQL> alter cluster clust (deptno number(2));

SQL> alter table emp1 (empno number(5), ename char(10), deptno number(2))
cluster clust(deptno);

SQL> Create table emp2


(empno number(4) primary key,
ename varchar2(20) constraint ch1 check(ename = upper(ename)),
mgr number(4) references emp2(empno),
comm number(7,2),
job varchar2(15) constraint ch2 check(job = upper(job)),
deptno number(2) not null);

??? delete from emp where rowid = (select * from emp minus
(select * from emp where rownum <= (select max(rownum)-14 from emp)))

rem Purpose: Example: UPDATE/DELETE in a loop and commit very X records


rem Handy for huge tables that cause rollback segment problems
rem -----------------------------------------------------------------------

declare
i number := 0;
cursor s1 is SELECT * FROM tab1 WHERE col1 = 'value1'
FOR UPDATE;
begin
for c1 in s1 loop
update tab1 set col1 = 'value2'
where current of s1;

i := i + 1; -- Commit after every X records


if i > 1000 then
commit;

21
i := 0;
end if;

end loop;
commit;
end;
/

rem Purpose: Example: how to populate a PL/SQL table from a cursor


rem -----------------------------------------------------------------------

set serveroutput on

begin
-- Declare the PL/SQL table
type deptarr is table of dept%rowtype
index by binary_integer;
d_arr deptarr;

-- Declare cursor
type d_cur is ref cursor return dept%rowtype;
c1 d_cur;

i number := 1;
begin
-- Populate the PL/SQL table from the cursor
open c1 for select * from dept;
loop
exit when c1%NOTFOUND;
fetch c1 into d_arr(i);
i := i+1;
end loop;
close c1;

-- Display the entire PL/SQL table on screen


for i in 1..d_arr.last loop
dbms_output.put_line('DEPTNO : '||d_arr(i).deptno );
dbms_output.put_line('DNAME : '||d_arr(i).dname );
dbms_output.put_line('LOC : '||d_arr(i).loc );
dbms_output.put_line('---------------------------');
end loop;
end;
/

rem Purpose: Count the number of rows for ALL tables in current schema
rem using PL/SQL
rem -----------------------------------------------------------------------

set serveroutput on size 1000000

22
DECLARE
t_c1_tname user_tables.table_name%TYPE;
t_command varchar2(200);
t_cid integer;
t_total_records number(10);
stat integer;
row_count integer;
t_limit integer := 0; -- Only show tables with more rows
cursor c1 is select table_name from user_tables order by table_name;
BEGIN
t_limit := 0;
open c1;
loop
fetch c1 into t_c1_tname;
exit when c1%NOTFOUND;
t_command := 'SELECT COUNT(0) FROM '||t_c1_tname;
t_cid := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(t_cid,t_command,dbms_sql.native);
DBMS_SQL.DEFINE_COLUMN(t_cid,1,t_total_records);
stat := DBMS_SQL.EXECUTE(t_cid);
row_count := DBMS_SQL.FETCH_ROWS(t_cid);
DBMS_SQL.COLUMN_VALUE(t_cid,1,t_total_records);
if t_total_records > t_limit then
DBMS_OUTPUT.PUT_LINE(rpad(t_c1_tname,55,' ')||
to_char(t_total_records,'99999999')||' record(s)');

end if;
DBMS_SQL.CLOSE_CURSOR(t_cid);
end loop;
close c1;
END;
/

rem Purpose: Functions to convert Hex to Decimal and vice versa


rem -----------------------------------------------------------------------

CREATE OR REPLACE FUNCTION hex2dec (hexnum in char) RETURN number IS


i number;
digits number;
result number := 0;
current_digit char(1);
current_digit_dec number;
BEGIN
digits := length(hexnum);
for i in 1..digits loop
current_digit := SUBSTR(hexnum, i, 1);
if current_digit in ('A','B','C','D','E','F') then
current_digit_dec := ascii(current_digit) - ascii('A') + 10;
else
current_digit_dec := to_number(current_digit);
end if;
result := (result * 16) + current_digit_dec;
end loop;

23
return result;
END hex2dec;
/
show errors

CREATE OR REPLACE FUNCTION num2hex (N in number) RETURN varchar2 IS


H varchar2(64) :='';
N2 integer := N;
BEGIN
loop
select rawtohex(chr(N2))||H
into H
from dual;

N2 := trunc(N2 / 256);
exit when N2=0;
end loop;
return H;
END num2hex;
/
show errors

-- Examples:
select hex2dec('FF') from dual;

select num2hex(10) from dual;

rem Purpose: Print ASCII table


rem -----------------------------------------------------------------------

set serveroutput on size 10240

declare
i number;
j number;
k number;
begin
for i in 2..15 loop
for j in 1..16 loop
k:=i*16+j;
dbms_output.put((to_char(k,'000'))||':'||chr(k)||' ');
if k mod 8 = 0 then
dbms_output.put_line('');
end if;
end loop;
end loop;
end;
/
show errors

24
rem Purpose: Fetch LOB column values piece-wise from PL/SQL
rem -----------------------------------------------------------------------

set serveroutput on

DROP TABLE lob_table; -- Create table to hols LOBs


CREATE TABLE lob_table (
id INTEGER,
b_lob BLOB,
c_lob CLOB,
b_file BFILE );

INSERT INTO lob_table -- Create sample record


VALUES (1, EMPTY_BLOB(), 'abcde', NULL);

DECLARE
clob_locator CLOB;
charbuf VARCHAR2(20);
read_offset INTEGER;
read_amount INTEGER;
BEGIN
-- First we need to get the lob locator
SELECT c_lob INTO clob_locator FROM lob_table WHERE id = 1;

DBMS_OUTPUT.PUT_LINE('CLOB Size: ' ||


DBMS_LOB.GETLENGTH(clob_locator));

-- Read LOB field contents


read_offset := 1;
read_amount := 20;
dbms_lob.read(clob_locator, read_amount, read_offset, charbuf);
dbms_output.put_line('CLOB Value: ' || charbuf);
END;
/

rem Purpose: Fetch Long column values piece-wise from PL/SQL


rem -----------------------------------------------------------------------

set serveroutput on

-- Create test table


drop table longtable;
create table longtable (longcol long) tablespace TOOLS;
insert into longtable values ( rpad('x', 257, 'QWERTY') );

DECLARE
cur1 PLS_INTEGER := DBMS_SQL.OPEN_CURSOR;;
rc NUMBER;
long_piece VARCHAR2(256);
piece_len INTEGER := 0;
long_tab DBMS_SQL.VARCHAR2S;

25
long_len INTEGER := 0;
BEGIN
DBMS_SQL.PARSE(cur1, 'select longcol from longtable', DBMS_SQL.NATIVE);
DBMS_SQL.DEFINE_COLUMN_LONG(cur1, 1);
rc := DBMS_SQL.EXECUTE(cur1);
rc := DBMS_SQL.FETCH_ROWS(cur1); -- Get one row

-- Loop until all pieces of the long column are processed


LOOP
DBMS_SQL.COLUMN_VALUE_LONG(cur1, 1, 256, long_len, long_piece, piece_len);
EXIT WHEN piece_len = 0;
DBMS_OUTPUT.PUT_LINE('Long piece len='|| piece_len);

long_tab( NVL(long_tab.LAST, 0)+1 ) := long_piece; -- Add piece to table


long_len := long_len + piece_len;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(cur1);
DBMS_OUTPUT.PUT_LINE('Total long col fetched, len='|| long_len);
END;
/

/*
Purpose: Random number/ string generator package
------------------------------------------------------------------------------
*/

create or replace package random


is
procedure srand(new_seed in number);
procedure get_rand(r OUT number);
procedure get_rand_max(r OUT number, n IN number);
function rand return number;
function rand_max(n IN number) return number;
function rand_string(ssiz IN number) return varchar2;
function smaller(x IN number, y IN number) return number;
pragma restrict_references(rand, WNDS);
pragma restrict_references(rand_max, WNDS);
pragma restrict_references(random, WNDS, RNPS);
pragma restrict_references(rand_string, WNDS);
pragma restrict_references(smaller, WNDS);
end random;
/

create or replace package body random


is
multiplier constant number := 22695477;
increment constant number := 1;
"2^32" constant number := 2 ** 32;
"2^16" constant number := 2 ** 16;
"0x7fff" constant number := 32767;
Seed number := 1;

26
function smaller(x IN number, y IN number) return number is
begin
if x <= y then
return x;
else
return y;
end if;
end smaller;

function rand_string(ssiz IN number) return varchar2 is


i number;
m number;
c char;
result varchar2(2000) := '';
begin
m := smaller(ssiz,2000);
for i in 1..m loop
c := substr('abcdefghijklmnopqrstuvwxyz0123456789',rand_max(36),1);
result := result || c;
end loop;
return result;
end rand_string;

procedure srand(new_seed in number) is


begin
Seed := new_seed;
end srand;

function rand return number is


begin
Seed := mod(multiplier * Seed + increment, "2^32");
return bitand(Seed/"2^16", "0x7fff");
end rand;

procedure get_rand(r OUT number) is


begin
r := rand;
end get_rand;

function rand_max(n IN number) return number is


begin
return mod(rand, n) + 1;
end rand_max;

procedure get_rand_max(r OUT number, n IN number) is


begin
r := rand_max(n);
end get_rand_max;

begin
select userenv('SESSIONID')
into Seed
from dual;
end random;
/

27
-- Some examples:
select random.rand_max(10) from dual;
select random.rand_max(10) from dual;
select random.rand_string(20) from dual;
select random.rand_string(20) from dual;

rem Purpose: Demonstrate writing to a file using the UTL_FILE package


rem -----------------------------------------------------------------------

DECLARE
fileHandler UTL_FILE.FILE_TYPE;
BEGIN
fileHandler := UTL_FILE.FOPEN('/tmp', 'myoutput', 'W');
UTL_FILE.PUTF(fileHandler, 'Look ma, I''m writing to a file!!!\n');
UTL_FILE.FCLOSE(fileHandler);
EXCEPTION
WHEN utl_file.invalid_path THEN
raise_application_error(-20000, 'ERROR: Invalid path for file.');
END;
/

rem Purpose: Demonstrate Java stored procedures (available from Oracle 8i)
rem -----------------------------------------------------------------------

conn / as sysdba

-- @?/javavm/install/initjvm.sql
grant javauserpriv to scott;

conn scott/tiger

create or replace java source named "Hello" as


public class Hello { /* Pure Java Code */
static public String Msg(String tail) {
return "Hello " + tail;
}
}
/
-- SHOW ERRORS not needed

create or replace function hello (str varchar2) return varchar as


language java name 'Hello.Msg(java.lang.String) return java.lang.String';
/
show errors

select hello('Frank') from dual

28
/

rem Purpose: Send e-mail messages from PL/SQL


rem Notes: From Oracle8i release 8.1.6 one can send e-mail messages
rem directly from PL/SQL using either the UTL_TCP or UTL_SMTP
rem packages. JServer needs to be installed and configured.
rem No pipes or external procedures required.
rem -----------------------------------------------------------------------

CREATE OR REPLACE PROCEDURE SEND_MAIL (


msg_from varchar2 := 'oracle',
msg_to varchar2,
msg_subject varchar2 := 'E-Mail message from your database',
msg_text varchar2 := '' )
IS
c utl_tcp.connection;
rc integer;
BEGIN
c := utl_tcp.open_connection('127.0.0.1', 25); -- open the SMTP port 25 on local machine
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'HELO localhost');
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'MAIL FROM: '||msg_from);
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'RCPT TO: '||msg_to);
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'DATA'); -- Start message body
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'Subject: '||msg_subject);
rc := utl_tcp.write_line(c, '');
rc := utl_tcp.write_line(c, msg_text);
rc := utl_tcp.write_line(c, '.'); -- End of message body
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'QUIT');
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
utl_tcp.close_connection(c); -- Close the connection
EXCEPTION
when others then
raise_application_error(-20000,
'Unable to send e-mail message from pl/sql');
END;
/
show errors

-- Examples:
set serveroutput on

exec send_mail(msg_to =>'orafaq@orafaq.org');

29
exec send_mail(msg_to =>'orafaq@orafaq.org',
msg_text=>'Look Ma, I can send mail from plsql'
);

rem Purpose: Send e-mail messages and attachments from PL/SQL


rem Notes: From Oracle8i release 8.1.6 one can send e-mail messages
rem directly from PL/SQL using either the UTL_TCP or UTL_SMTP
rem packages. Jserver needs to be installed and configured.
rem No pipes or external procedures required.
rem ----------------------------------------------------------------------

CREATE OR REPLACE PROCEDURE SEND_MAIL (


msg_from varchar2 := 'EMAILADDRESS@DOMAIN.COM', ----- MAIL BOX SENDING THE
EMAIL
msg_to varchar2 := 'EMAILADDRESS@DOMAIN.COM', ----- MAIL BOX RECIEVING THE
EMAIL
msg_subject varchar2 := 'Output file TEST1', ----- EMAIL SUBJECT
msg_text varchar2 := 'THIS IS THE TEXT OF THE EMAIL MESSAGE.',
v_output1 varchar2 := 'THIS IS THE TEXT OF THE ATTACHMENT FILE. THIS TEXT SHOULD BE
IN A TEXT FILE ATTACHED TO THE EMAIL.')
IS
c utl_tcp.connection;
rc integer;
crlf VARCHAR2(2):= CHR(13)||CHR(10);
mesg VARCHAR2( 32767 );
BEGIN
c := utl_tcp.open_connection('196.35.140.18', 25); ----- OPEN SMTP PORT CONNECTION
rc := utl_tcp.write_line(c, 'HELO 196.35.140.18'); ----- PERFORMS HANDSHAKING WITH SMTP
SERVER
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'EHLO 196.35.140.18'); ----- PERFORMS HANDSHAKING WITH SMTP
SERVER, INCLUDING EXTRA INFORMATION
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'MAIL FROM: '||msg_from); ----- MAIL BOX SENDING THE EMAIL
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'RCPT TO: '||msg_to); ----- MAIL BOX RECIEVING THE EMAIL
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'DATA'); ----- EMAIL MESSAGE BODY START
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'Date: '||TO_CHAR( SYSDATE, 'dd Mon yy hh24:mi:ss' ));
rc := utl_tcp.write_line(c, 'From: '||msg_from||' <'||msg_from||'>');
rc := utl_tcp.write_line(c, 'MIME-Version: 1.0');
rc := utl_tcp.write_line(c, 'To: '||msg_to||' <'||msg_to||'>');
rc := utl_tcp.write_line(c, 'Subject: '||msg_subject);
rc := utl_tcp.write_line(c, 'Content-Type: multipart/mixed;'); ----- INDICATES THAT THE BODY
CONSISTS OF MORE THAN ONE PART
rc := utl_tcp.write_line(c, ' boundary="-----SECBOUND"'); ----- SEPERATOR USED TO
SEPERATE THE BODY PARTS
rc := utl_tcp.write_line(c, ''); ----- INSERTS A BLANK LINE. PART OF THE
MIME FORMAT AND NONE OF THEM SHOULD BE REMOVED.
rc := utl_tcp.write_line(c, '-------SECBOUND');

30
rc := utl_tcp.write_line(c, 'Content-Type: text/plain'); ----- 1ST BODY PART. EMAIL TEXT
MESSAGE
rc := utl_tcp.write_line(c, 'Content-Transfer-Encoding: 7bit');
rc := utl_tcp.write_line(c, '');
rc := utl_tcp.write_line(c, msg_text); ----- TEXT OF EMAIL MESSAGE
rc := utl_tcp.write_line(c, '');
rc := utl_tcp.write_line(c, '-------SECBOUND');
rc := utl_tcp.write_line(c, 'Content-Type: text/plain;'); ----- 2ND BODY PART.
rc := utl_tcp.write_line(c, ' name="Test.txt"');
rc := utl_tcp.write_line(c, 'Content-Transfer_Encoding: 8bit');
rc := utl_tcp.write_line(c, 'Content-Disposition: attachment;'); ----- INDICATES THAT THIS IS AN
ATTACHMENT
rc := utl_tcp.write_line(c, ' filename="Test.txt"'); ----- SUGGESTED FILE NAME FOR
ATTACHMENT
rc := utl_tcp.write_line(c, '');
rc := utl_tcp.write_line(c, v_output1);
rc := utl_tcp.write_line(c, '-------SECBOUND--');
rc := utl_tcp.write_line(c, '');
rc := utl_tcp.write_line(c, '.'); ----- EMAIL MESSAGE BODY END
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'QUIT'); ----- ENDS EMAIL TRANSACTION
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
utl_tcp.close_connection(c); ----- CLOSE SMTP PORT CONNECTION
EXCEPTION
when others then
raise_application_error(-20000, SQLERRM);
END;
/

rem Purpose: Access Internet Web pages from SQL or PL/SQL


rem Notes: From Oracle 8.0 one can retrieve web pages directly
rem from SQL or PL/SQL. Note you need to run utlhttp.sql as
rem SYS before this procedure will work.
rem -----------------------------------------------------------------------

set pages 50000

select utl_http.request('http://www.orafaq.com/') from dual;

rem Purpose: Example of a CROSS MATRIX report implemented using

SELECT job,
sum(decode(deptno,10,sal)) DEPT10,
sum(decode(deptno,20,sal)) DEPT20,
sum(decode(deptno,30,sal)) DEPT30,
sum(decode(deptno,40,sal)) DEPT40
FROM scott.emp
GROUP BY job
/

31
-- Sample output:
--
-- JOB DEPT10 DEPT20 DEPT30 DEPT40
-- --------- ---------- ---------- ---------- ----------
-- ANALYST 6000
-- CLERK 1300 1900 950
-- MANAGER 2450 2975 2850
-- PRESIDENT 5000
-- SALESMAN 5600
--

rem Purpose: Lookup Oracle error messages. Similar to unix "oerr" command.
rem This script is handy on platforms like NT with no OERR support

set serveroutput on
set veri off feed off

prompt Lookup Oracle error messages:


prompt
prompt Please enter error numbers as negatives. E.g. -1
prompt

exec dbms_output.put_line('==> '||sqlerrm( &errno ) );

set veri on feed on


undef errno

rem Purpose: Show database version with options intalled


rem (handy for your HELP/ABOUT menu)

set head off feed off pages 0 serveroutput on

col banner format a72 wrap

select banner
from sys.v_$version;

select ' With the '||parameter||' option'


from sys.v_$option
where value = 'TRUE';

select ' The '||parameter||' option is not installed'


from sys.v_$option
where value <> 'TRUE';

begin
dbms_output.put_line('Port String: '||dbms_utility.port_string);
end;
/

set head on feed on

32
rem Purpose: Reports information about your current database context

set termout off


store set store rep
set head off
set pause off
set termout on

select 'User: '|| user || ' on database ' || global_name,


' (term='||USERENV('TERMINAL')||
', audsid='||USERENV('SESSIONID')||')' as MYCONTEXT
from global_name;

@store
set termout on

rem Purpose: Select the Nth highest value from a table

select level, max('col_name') from my_table


where level = '&n'
connect by prior ('col_name') > 'col_name')
group by level;

-- Example :
--
-- Given a table called emp with the following columns:
-- id number
-- name varchar2(20)
-- sal number
--
-- For the second highest salary:
--
-- select level, max(sal) from emp
-- where level=2
-- connect by prior sal > sal
-- group by level
--

rem Purpose: Select the Nth lowest value from a table


rem -----------------------------------------------------------------------

select level, min('col_name') from my_table


where level = '&n'
connect by prior ('col_name') < 'col_name')
group by level;

33
-- Example:
--
-- Given a table called emp with the following columns:
-- id number
-- name varchar2(20)
-- sal number
--
-- For the second lowest salary:
--
-- select level, min(sal) from emp
-- where level=2
-- connect by prior sal < sal
-- group by level

rem Purpose: Display table and column comments for the current schema
rem Handy for getting to know the database schema
rem -----------------------------------------------------------------------

set pages 50000


set null 'No Comments'

tti 'Table Comments'


col comments format a29 wrap word

select * from user_tab_comments;

tti 'Column Comments'


col comments format a18 wrap word
break on table_name skip 1
select * from user_col_comments;
clear break

set null ''


set pages 23

rem Purpose: Example of how to pass application info through to Oracle RDBMS

-- The following code tells the database what the application is up to:

begin
dbms_application_info.set_client_info('BANCS application info');
dbms_application_info.set_module('BANCS XYZ module', 'BANCS action name');
end;

34
/

-- Retrieve application info from the database:

select module, action, client_info


from sys.v_$session where audsid = USERENV('SESSIONID')
/

select sql_text
from sys.v_$sqlarea
where module = 'BANCS XYZ module'
and action = 'BANCS action name'
/

rem Purpose: Check if a year is a leap year

select year,
decode( mod(year, 4), 0,
decode( mod(year, 400), 0, 'Leap Year',
decode( mod(year, 100), 0, 'Not a Leap Year', 'Leap Year')
), 'Not a Leap Year'
) as leap_year_indicator
from my_table
/

rem Purpose: This script will spell out numbers to words (handy for
rem cheque printing). Example '10' --> Ten

select decode( sign( &num ), -1, 'Negative ', 0, 'Zero', NULL ) ||


decode( sign( abs(&num) ), +1, to_char( to_date( abs(&num),'J'),'Jsp') )
from dual
/

rem Purpose: Demonstrate simple encoding and decoding of secret messages


rem This method can be extended to create simple password
rem -----------------------------------------------------------------------

SELECT TRANSLATE(
'HELLO WORLD', -- Message to encode
'ABCDEFGHIJKLMNOPQRSTUVWXYZ ',
'1234567890!@#$%^&*()-=_+;,.') ENCODED_MESSAGE

35
FROM DUAL
/

SELECT TRANSLATE(
'85@@%._%*@4', -- Message to decode
'1234567890!@#$%^&*()-=_+;,.',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ ') DECODED_MESSAGE
FROM DUAL
/

rem Purpose: Count the number of rows for ALL tables in the current schema
rem -----------------------------------------------------------------------

set termout off echo off feed off trimspool on head off pages 0

spool countall.tmp
select 'SELECT count(*), '''||table_name||''' from '||table_name||';'
from user_tables
/
spool off

set termout on
@@countall.tmp

set head on feed on

rem Purpose: Demonstrate Oracle database types and object tables


rem -----------------------------------------------------------------------

drop type employee_typ;

create type employee_typ as object (


empno NUMBER,
emp_name varchar2(30),
hiredate date,
member function days_at_company return NUMBER,
pragma restrict_references(days_at_company, WNDS)
)
/

create type body employee_tye is


begin
member function days_at_company return number is
begin
return (SYSDATE-hiredate);
end;
end;
/
show errors

drop type department_typ;

36
create type department_typ as object (
deptno NUMBER(5),
manager ref employee_typ
)
/

select * from user_types


where predefined = 'NO';

-- Create a object table


create table emp1 as employee_typ;

create table employee (emp_no NUMBER, emp employee_typ);

insert into employee values (1, employee_typ(1, 'Frank Naude', SYSDATE));

commit;

select * from employee;

select x.emp.emp_name from employee x;

rem Purpose: Demonstrate Oracle 8i temporary tables


rem -----------------------------------------------------------------------

drop table x
/

create global temporary table x (a date)


on commit delete rows -- Delete rows after commit
-- on commit preserve rows -- Delete rows after exit session
/

select table_name, temporary, duration


from user_tables
where table_name = 'X'
/

insert into x values (sysdate);

select * from x;

commit;

-- Inserted rows are missing after commit


select * from x;

Oracle 8.0 Features

37
Concept of Key Preserved Table:

CREATE VIEW KPT_VIEW AS SELECT


EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,
EMP.DEPTNO,DNAME,LOC FROM EMP,DEPT WHERE EMP.DEPTNO=DEPT.DEPTNO;

Instead of Trigger on a View

CREATE OR REPLACE TRIGGER TR1


INSTEAD OF
DELETE ON
KPT_VIEW
FOR EACH ROW
BEGIN
DELETE FROM EMP WHERE DEPTNO=:OLD.DEPTNO;
DELETE FROM DEPT WHERE DEPTNO=:OLD.DEPTNO;
END;
/

CREATE OR REPLACE TRIGGER TR2


INSTEAD OF
INSERT ON
KPT_VIEW
FOR EACH ROW
BEGIN
INSERT INTO DEPT VALUES (:NEW.DEPTNO,:NEW.DNAME,:NEW.LOC);
INSERT INTO EMP(ENAME,EMPNO,SAL,DEPTNO) VALUES (:NEW.ENAME,
:NEW.EMPNO,:NEW.SAL,:NEW.DEPTNO);
END;
/

Pragma Restrict References ( WNDS,WNPS,RNDS,RNPS)

CREATE OR REPLACE PACKAGE P1 AS


FUNCTION EMP_SAL(ENO NUMBER) RETURN NUMBER;
PRAGMA RESTRICT_REFERENCES( EMP_SAL,WNDS,WNPS,RNPS);
END;
/

CREATE OR REPLACE PACKAGE BODY P1 AS


FUNCTION EMP_SAL(ENO NUMBER) RETURN NUMBER IS
SALARY NUMBER(7,2);
BEGIN
SELECT SAL INTO SALARY FROM EMP WHERE EMPNO=ENO;
RETURN (SALARY);
END;
END;
/

38
Utl_File Pacakge:

DECLARE
FH UTL_FILE.FILE_TYPE;
CURSOR C1 IS SELECT * FROM EMP WHERE DEPTNO=&DNO;
VAR VARCHAR2(100);
BEGIN
FH:=UTL_FILE.FOPEN('\ORANT','EMP.TXT','A');
FOR I IN C1 LOOP
VAR:=I.ENAME || ',' || I.SAL || ',' || I.DEPTNO;
UTL_FILE.PUT_LINE(FH,VAR);
UTL_FILE.NEW_LINE(FH,1);
END LOOP;
UTL_FILE.FCLOSE(FH);
END;
/

DECLARE
FH UTL_FILE.FILE_TYPE;
VAR VARCHAR2(100);
NA VARCHAR2(10);
SU VARCHAR2(10);
MA NUMBER(2);
FC NUMBER(2);
SC NUMBER(2);
BEGIN
DELETE FROM STU;
FH:=UTL_FILE.FOPEN('\ORANT','SRI.TXT','R');
LOOP
BEGIN
UTL_FILE.GET_LINE(FH,VAR);
EXCEPTION
WHEN NO_DATA_FOUND THEN
EXIT;
END;
FC:=INSTR(VAR,',',1,1);
SC:=INSTR(VAR,',',1,2);
NA:= SUBSTR(VAR,1,FC-1);
SU:= SUBSTR(VAR,FC+1,SC-FC-1);
MA:= SUBSTR(VAR,SC+1);
INSERT INTO STU VALUES(NA,SU,MA);
END LOOP;
UTL_FILE.FCLOSE(FH);
END;
/

39
Creattion Of user defined data types and storing it as a Object

CREATE OR REPLACE TYPE ADR_TYPE AS OBJECT


( STNO NUMBER(2),
STNAME VARCHAR2(10),
CITY VARCHAR2(10),
PIN NUMBER(7)
);
/

Column Object Table

CREATE TABLE EMP_OBJ_COL


( EMPNO NUMBER(4),
ENAME VARCHAR2(10),
ADDRESS ADR_TYPE,
SAL NUMBER(7,2)
);

INSERT INTO EMP_OBJ_COL VALUES(1,'SRINATH',


ADR_TYPE(10,'SSS','BLORE',560032),2000);

INSERT INTO EMP_OBJ_COL VALUES(2,'SRINATH',


ADR_TYPE(10,'SSS','MYSORE',532),2000);

INSERT INTO EMP_OBJ_COL VALUES(3,'SRINATH',


ADR_TYPE(NULL,'SS','BLORE',56343),2000);

INSERT INTO EMP_OBJ_COL VALUES(4,'SSSSSS', NULL,2000);

SELECT X.ADDRESS.CITY,X.ADDRESS.PIN,X.EMPNO,ENAME FROM EMP_OBJ_COL


X;

DELETE FROM EMP_OBJ_COL X WHERE X.ADDRESS.PIN=2000;

UPDATE EMP_OBJ_COL X SET X.ADDRESS.STNAME='EEEEEEE' WHERE


X.ADDRESS.STNAME='SSS';

Row Object Table

CREATE TYPE DEPT_TYPE AS OBJECT


(
DEPTNO NUMBER(2),
DNAME VARCHAR2(15),
LOC VARCHAR2(15)
);
/

40
41
CREATE TABLE DEPT_OBJ_ROW OF DEPT_TYPE;

SELECT REF(E) FROM DEPT_OBJ_ROW E


/

CREATE TABLE EMP_REF


(
EMPNO NUMBER(4),
ENAME VARCHAR2(10),
JOB VARCHAR2(10),
DEPTNO NUMBER(2),
DEPT_REF REF DEPT_TYPE SCOPE IS DEPT_OBJ_ROW
);

INSERT INTO EMP_REF(EMPNO,ENAME,JOB,DEPTNO)


SELECT EMPNO,ENAME,JOB,DEPTNO FROM EMP;

UPDATE EMP_REF X SET DEPT_REF=(SELECT REF(E) FROM


DEPT_OBJ_ROW E WHERE DEPTNO=X.DEPTNO);

SELECT ENAME,EMPNO,DEPTNO,DEREF(DEPT_REF) FROM EMP_REF


/

DECLARE
D DEPT_TYPE;
BEGIN
SELECT DEREF(DEPT_REF) INTO D FROM EMP_REF WHERE
EMPNO=&EMPNO;
DBMS_OUTPUT.PUT_LINE(D.DNAME||' '||D.LOC);
END;
/

CREATE TABLE TEST


(
X DEPT_TYPE
);

INSERT INTO TEST SELECT VALUE(E) FROM DEPT_OBJ_ROW E;

CREATE OR REPLACE TYPE ADR_MAP_TYPE AS OBJECT


( STNO NUMBER(2),
STNAME VARCHAR2(10),
CITY VARCHAR2(10),
PIN NUMBER(7),
MAP MEMBER FUNCTION X RETURN VARCHAR2,
PRAGMA RESTRICT_REFERENCES(X,RNDS,WNDS,RNPS,WNPS)
);

42
Map Method on the Object

CREATE OR REPLACE TYPE BODY ADR_MAP_TYPE AS


MAP MEMBER FUNCTION X RETURN VARCHAR2 IS
BEGIN
RETURN(CITY);
END;
END;
/

CREATE TABLE EMP_OBJ_COL_MAP


( EMPNO NUMBER(4),
ENAME VARCHAR2(10),
ADDRESS ADR_MAP_TYPE,
SAL NUMBER(7,2)
);

INSERT INTO EMP_OBJ_COL_MAP VALUES(1,'SRINATH',


ADR_MAP_TYPE(10,'SSS','BLORE',560032),2000);
INSERT INTO EMP_OBJ_COL_MAP VALUES(2,'SRINATH',
ADR_MAP_TYPE(10,'SSS','MYSORE',532),2000);
INSERT INTO EMP_OBJ_COL_MAP VALUES(3,'SRINATH',
ADR_MAP_TYPE(NULL,'SSS','BLORE',563432),2000);

Creation of Member Functions inside the Object

CREATE OR REPLACE TYPE ADR_MEM_TYPE AS OBJECT


( STNO NUMBER(2),
STNAME VARCHAR2(10),
CITY VARCHAR2(10),
PIN NUMBER(7),
MEMBER FUNCTION UCITY(CITY VARCHAR2) RETURN VARCHAR2,
PRAGMA RESTRICT_REFERENCES(UCITY,WNDS)
);
/
CREATE OR REPLACE TYPE BODY ADR_MEM_TYPE AS
MEMBER FUNCTION UCITY(CITY VARCHAR2) RETURN VARCHAR2 IS
BEGIN
RETURN(UPPER(CITY));
END;
END;
/

CREATE TABLE ADR_TAB OF ADR_MEM_TYPE;

INSERT INTO ADR_TAB VALUES(10,'SSS','BANG',567);


INSERT INTO ADR_TAB VALUES(12,'SSS','MYSORE',567);
INSERT INTO ADR_TAB VALUES(13,'SSS','MAD',567);

SELECT STNO,STNAME,X.UCITY(STNAME),CITY,X.UCITY(CITY) FROM ADR_TAB X;

43
Altering the User defined Data Type

ALTER TYPE ADR_MEM_TYPE REPLACE AS OBJECT


( STNO NUMBER(2),
STNAME VARCHAR2(10),
CITY VARCHAR2(10),
PIN NUMBER(7),
MEMBER FUNCTION UCITY(CITY VARCHAR2) RETURN VARCHAR2,
PRAGMA RESTRICT_REFERENCES(UCITY,WNDS),
MEMBER FUNCTION UNAME(STNAME VARCHAR2) RETURN VARCHAR2,
PRAGMA RESTRICT_REFERENCES(UNAME,WNDS)
);
/
CREATE OR REPLACE TYPE BODY ADR_MEM_TYPE AS
MEMBER FUNCTION UCITY(CITY VARCHAR2) RETURN VARCHAR2 IS
BEGIN
RETURN(UPPER(CITY));
END;
MEMBER FUNCTION UNAME(STNAME VARCHAR2) RETURN VARCHAR2 IS
BEGIN
RETURN(UPPER(STNAME));
END;
END;
/

Object Views

CREATE TYPE EMP_VIEW_TYPE AS OBJECT


(
EMPNO NUMBER(4),
ENAME VARCHAR2(10),
JOB VARCHAR2(10),
MGR NUMBER(4),
HIREDATE DATE,
SAL NUMBER(7,2),
COMM NUMBER(7,2),
DEPTNO NUMBER(2)
);
/
CREATE VIEW EMP_OBJ_VIEW(DETAILS) AS SELECT
EMP_VIEW_TYPE(EMPNO,ENAME
,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) FROM EMP;

INSERT INTO EMP_OBJ_VIEW VALUES(EMP_VIEW_TYPE(12,'AAA','AAA',7788,


NULL,223,12,10));

SELECT * FROM EMP_OBJ_VIEW;

Data Partition or Partition Tables

44
CREATE TABLE EMP_PART
( EMPNO NUMBER(2),
ENAME VARCHAR2(10),
JOB VARCHAR2(10),
DEPTNO NUMBER(2),
SAL NUMBER(7,2)
)
PARTITION BY RANGE(EMPNO)
(
PARTITION PART1 VALUES LESS THAN (20)
TABLESPACE USER_DATA,
PARTITION PART2 VALUES LESS THAN (50)
TABLESPACE SYSTEM,
PARTITION PART3 VALUES LESS THAN (MAXVALUE)
TABLESPACE ROLLBACK_DATA
);

SELECT TABLE_NAME,PARTITION_NAME,HIGH_VALUE,TABLESPACE_NAME
FROM USER_TAB_PARTITIONS;

INSERT INTO EMP_PART VALUES(1,'SSS','SSS',10,1000);


INSERT INTO EMP_PART VALUES(13,'SSS','SSS',10,1000);
INSERT INTO EMP_PART VALUES(19,'SSS','SSS',10,1000);
INSERT INTO EMP_PART VALUES(22,'SSS','SSS',10,1000);
INSERT INTO EMP_PART VALUES(34,'SSS','SSS',10,1000);
INSERT INTO EMP_PART VALUES(49,'SSS','SSS',10,1000);
INSERT INTO EMP_PART VALUES(50,'SSS','SSS',10,1000);
INSERT INTO EMP_PART VALUES(99,'SSS','SSS',10,1000);
INSERT INTO EMP_PART VALUES(NULL,'SSS','SSS',10,1000);

SELECT * FROM EMP_PART PARTITION(PART1);


SELECT * FROM EMP_PART PARTITION(PART2);
SELECT * FROM EMP_PART PARTITION(PART3);

Global Partition Index

CREATE INDEX X ON EMP_PART(EMPNO)


GLOBAL
PARTITION BY RANGE(EMPNO)
(
PARTITION PART1 VALUES LESS THAN (20)
TABLESPACE USER_DATA,
PARTITION PART2 VALUES LESS THAN (50)
TABLESPACE SYSTEM,
PARTITION PART3 VALUES LESS THAN (MAXVALUE)
TABLESPACE ROLLBACK_DATA
);

45
Local Partition Index

CREATE INDEX X ON EMP_PART(EMPNO)


LOCAL
(
PARTITION PART1 TABLESPACE USER_DATA,
PARTITION PART2 TABLESPACE SYSTEM,
PARTITION PART3 TABLESPACE ROLLBACK_DATA
);

Collections Data Types

Nested Table

CREATE TYPE BOOKS_TYPE AS OBJECT


(BOOK_NO NUMBER(2),
TITLE VARCHAR2(10),
AUTHOR VARCHAR2(10)
);
/

CREATE TYPE BOOKS AS TABLE OF BOOKS_TYPE;


/

CREATE TABLE STUDENT


( STUDENT_NO NUMBER(1) PRIMARY KEY,
NAME VARCHAR2(10),
BOOKS_ISSUED BOOKS
)NESTED TABLE BOOKS_ISSUED STORE AS BK_NAME;

INSERT INTO STUDENT VALUES(1,'SRINATH',


BOOKS(BOOKS_TYPE(1,'VB','SS'),
BOOKS_TYPE(2,'VC','AA'),
BOOKS_TYPE(3,'C++','DD')
)
);

INSERT INTO STUDENT VALUES(2,'MAHESH',


BOOKS(BOOKS_TYPE(3,'PASCAL','SS'),
BOOKS_TYPE(5,'VC++','AA'),
BOOKS_TYPE(7,'VB','DD')
)
);

UPDATE THE
(SELECT BOOKS_ISSUED FROM STUDENT WHERE STUDENT_NO=1)

46
SET TITLE='VC++' WHERE BOOK_NO=2;
INSERT INTO THE
(SELECT BOOKS_ISSUED FROM STUDENT WHERE STUDENT_NO=1)
VALUES(4,'ORACLE','SSS');

DELETE FROM THE


(SELECT BOOKS_ISSUED FROM STUDENT WHERE STUDENT_NO=1)
WHERE BOOK_NO=2;

SELECT TITLE,AUTHOR FROM THE


(SELECT BOOKS_ISSUED FROM STUDENT WHERE STUDENT_NO=1)
WHERE BOOK_NO=1;

DECLARE
CURSOR C1 IS SELECT NAME,BOOKS_ISSUED FROM STUDENT;
S_NAME VARCHAR2(10);
MBOOK BOOKS;
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO S_NAME,MBOOK;
EXIT WHEN C1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('THE STUDENT NAME IS : '|| S_NAME);
DBMS_OUTPUT.PUT_LINE('THE NO OF BOOKS TAKEN IS :' || MBOOK.COUNT);
FOR I IN 1 .. MBOOK.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(I||'.'|| MBOOK(I).TITLE
||' '||MBOOK(I).AUTHOR);
END LOOP;
END LOOP;
CLOSE C1;
END;
/

Varrays

CREATE TYPE BOOKS_TYPE AS OBJECT


(BOOK_NO NUMBER(2),
TITLE VARCHAR2(10),
AUTHOR VARCHAR2(10)
);
/

CREATE TYPE BOOKS_ARRAY AS VARRAY(10) OF BOOKS_TYPE;


/

CREATE TABLE STUDENTS


( STUDENT_NO NUMBER(1) PRIMARY KEY,
NAME VARCHAR2(10),
BOOKS_ISSUED BOOKS_ARRAY

47
);

INSERT INTO STUDENTS VALUES(1,'SRINATH',


BOOKS_ARRAY(BOOKS_TYPE(1,'VB','SS'),
BOOKS_TYPE(2,'VC','AA'),
BOOKS_TYPE(3,'C++','DD')
)
);

INSERT INTO STUDENTS VALUES(2,'MAHESH',


BOOKS_ARRAY(BOOKS_TYPE(3,'PASCAL','SS'),
BOOKS_TYPE(5,'VC++','AA'),
BOOKS_TYPE(7,'VB','DD')
)
);

UPDATE STUDENTS SET BOOKS_ISSUED =


BOOKS_ARRAY(BOOKS_TYPE(3,'PASCAL','SS'),
BOOKS_TYPE(5,'VC++','AA'),
BOOKS_TYPE(7,'VB','DD'),
BOOKS_TYPE(8,'SS','SS')
)
WHERE STUDENT_NO=2;

DECLARE
BOOK BOOKS_ARRAY;
STU_NO NUMBER(3) := &STU_NO;
BEGIN
SELECT BOOKS_ISSUED INTO BOOK FROM STUDENTS WHERE
STUDENT_NO=STU_NO;
DBMS_OUTPUT.PUT_LINE(' THE NO OF BOOKS ISSUED ARE: ' || BOOK.COUNT);
DBMS_OUTPUT.PUT_LINE(' THE LIMIT OF THE ARRAY IS '|| BOOK.LIMIT);
BOOK.EXTEND;
DBMS_OUTPUT.PUT_LINE(' THE RECORD NO IS '|| BOOK.COUNT);
BOOK(BOOK.COUNT):=BOOKS_TYPE(7,'WWW','WWW');
UPDATE STUDENTS SET BOOKS_ISSUED = BOOK WHERE STUDENT_NO=STU_NO;
END;
/

Large Object Data Types ( BLOB, CLOB, BFILE, NCLOB )

CREATE TABLE PROPOSAL


(
PROPOSAL_ID NUMBER(1) PRIMARY KEY,
CLIENT_NAME VARCHAR2(10),
PROPOSAL_TEXT CLOB,
COVER_LETTER BFILE
);

48
CREATE DIRECTORY SRI AS '\ORANT';

INSERT INTO PROPOSAL VALUES(1,'AAAA','THIS IS A DEMO FOR ORACLE 8.0',


BFILENAME('SRI','SRI.TXT'));

INSERT INTO PROPOSAL VALUES(2,'BBBB','THIS IS A DEMO FOR CLOB DATA TYPE',


BFILENAME('SRI','EMP.TXT'));

INSERT INTO PROPOSAL VALUES(3,'AAAA','THIS IS A DEMO FOR ORACLE 8.0',


BFILENAME('SRI','TEMP.TXT'));

LOB PACKAGE

DECLARE
PROP_ID NUMBER(1) := &PROP_ID;
VCLOB CLOB;
VAR VARCHAR2(100) := '&VAR';
BEGIN
SELECT PROPOSAL_TEXT INTO VCLOB FROM PROPOSAL WHERE
PROPOSAL_ID=PROP_ID FOR UPDATE;

DBMS_LOB.WRITE(VCLOB,LENGTH(VAR),DBMS_LOB.GETLENGTH(VCLOB)
+2,VAR);
COMMIT;
END;

DECLARE
PROP_ID1 NUMBER(1) := &PROP_ID;
DCLOB CLOB;
PROP_ID2 NUMBER(1) := &PROP_ID;
SCLOB CLOB;

BEGIN
SELECT PROPOSAL_TEXT INTO DCLOB FROM PROPOSAL WHERE
PROPOSAL_ID=PROP_ID1 FOR UPDATE;
SELECT PROPOSAL_TEXT INTO SCLOB FROM PROPOSAL WHERE
PROPOSAL_ID=PROP_ID2;

DBMS_LOB.APPEND(DCLOB,SCLOB);

COMMIT;

END;
/

DECLARE

49
PROP_ID1 NUMBER(1) := &PROP_ID;
FCLOB CLOB;
PROP_ID2 NUMBER(1) := &PROP_ID;
SCLOB CLOB;
X NUMBER(2);
BEGIN
SELECT PROPOSAL_TEXT INTO FCLOB FROM PROPOSAL WHERE
PROPOSAL_ID=PROP_ID1;
SELECT PROPOSAL_TEXT INTO SCLOB FROM PROPOSAL WHERE
PROPOSAL_ID=PROP_ID2;
X:=DBMS_LOB.COMPARE(FCLOB,SCLOB,3,6,6);
IF X=0 THEN
DBMS_OUTPUT.PUT_LINE(' THE CLOBS ARE SIMILAR ');
ELSE
DBMS_OUTPUT.PUT_LINE(' THE CLOBS ARE NOT SIMILAR ');
END IF;
END;
/

DECLARE
PROP_ID1 NUMBER(1) := &PROP_ID;
DCLOB CLOB;
PROP_ID2 NUMBER(1) := &PROP_ID;
SCLOB CLOB;

BEGIN
SELECT PROPOSAL_TEXT INTO DCLOB FROM PROPOSAL WHERE
PROPOSAL_ID=PROP_ID1 FOR UPDATE;
SELECT PROPOSAL_TEXT INTO SCLOB FROM PROPOSAL WHERE
PROPOSAL_ID=PROP_ID2;
DBMS_LOB.COPY(DCLOB,SCLOB,10,6,1);
COMMIT;
END;
/

DECLARE
PROP_ID1 NUMBER(1) := &PROP_ID;
DCLOB CLOB;
AMT NUMBER(2):= &NUM;
BEGIN
SELECT PROPOSAL_TEXT INTO DCLOB FROM PROPOSAL WHERE
PROPOSAL_ID=PROP_ID1 FOR UPDATE;
DBMS_LOB.ERASE(DCLOB,AMT,1);
COMMIT;
END;
/

DECLARE
PROP_ID1 NUMBER(1) := &PROP_ID;
DCLOB CLOB;
AMT NUMBER(2):= &NUM;

50
VAR VARCHAR2(20);
BEGIN
SELECT PROPOSAL_TEXT INTO DCLOB FROM PROPOSAL WHERE
PROPOSAL_ID=PROP_ID1;
VAR:=DBMS_LOB.SUBSTR(DCLOB,AMT,1);
DBMS_OUTPUT.PUT_LINE(VAR);
END;
/

DECLARE
PROP_ID1 NUMBER(1) := &PROP_ID;
VCLOB CLOB;
VBFILE BFILE;
FNAME VARCHAR2(30);
DNAME VARCHAR2(30);

BEGIN
SELECT COVER_LETTER INTO VBFILE FROM PROPOSAL WHERE
PROPOSAL_ID=PROP_ID1;

DBMS_LOB.FILEGETNAME(VBFILE,DNAME,FNAME);

DBMS_OUTPUT.PUT_LINE('THE FILENAME IS '|| FNAME);


DBMS_OUTPUT.PUT_LINE('THE DIRECTORY NAME IS '|| DNAME);
END;
/

DECLARE
PROP_ID1 NUMBER(1) := &PROP_ID;
VCLOB CLOB;
VBFILE BFILE;
FNAME VARCHAR2(30);
DNAME VARCHAR2(30);
VAR VARCHAR2(100);
BEGIN
SELECT COVER_LETTER INTO VBFILE FROM PROPOSAL WHERE
PROPOSAL_ID=PROP_ID1;
SELECT PROPOSAL_TEXT INTO VCLOB FROM PROPOSAL WHERE
PROPOSAL_ID=PROP_ID1 FOR UPDATE;

DBMS_LOB.FILEGETNAME(VBFILE,DNAME,FNAME);
DBMS_OUTPUT.PUT_LINE('THE FILENAME IS '|| FNAME);
DBMS_OUTPUT.PUT_LINE('THE DIRECTORY NAME IS '|| DNAME);

DBMS_LOB.FILEOPEN(VBFILE,DBMS_LOB.FILE_READONLY);

DBMS_LOB.LOADFROMFILE(VCLOB,VBFILE,DBMS_LOB.GETLENGTH(VBFILE),1,1);
VAR:=DBMS_LOB.SUBSTR(VCLOB,100,1);

DBMS_OUTPUT.PUT_LINE('THE CONTENTS OF THE FILE IS ');


DBMS_OUTPUT.PUT_LINE(VAR);

51
DBMS_LOB.FILECLOSEALL;
COMMIT;
END;
/

Order Method on the Object

CREATE OR REPLACE TYPE ADR_ORD_TYPE AS OBJECT


( STNO NUMBER(2),
STNAME VARCHAR2(10),
CITY VARCHAR2(10),
PIN NUMBER(7),
ORDER MEMBER FUNCTION X( P ADR_ORD_TYPE) RETURN INTEGER,
PRAGMA RESTRICT_REFERENCES(X,RNDS,WNDS,RNPS,WNPS)
);

CREATE OR REPLACE TYPE BODY ADR_ORD_TYPE AS


ORDER MEMBER FUNCTION X(P ADR_ORD_TYPE) RETURN INTEGER IS
BEGIN
IF (P.CITY > CITY) THEN
RETURN(1);
ELSIF (P.CITY < CITY) THEN
RETURN(-1);
ELSE
RETURN(0);
END IF;
END;
END;
/

CREATE TABLE EMP_OBJ_COL_ORD


( EMPNO NUMBER(4),
ENAME VARCHAR2(10),
ADDRESS ADR_ORD_TYPE,
SAL NUMBER(7,2)
);

INSERT INTO EMP_OBJ_COL_ORD VALUES(1,'SRINATH',


ADR_ORD_TYPE(10,'SSS','BLORE',560032),2000);
INSERT INTO EMP_OBJ_COL_ORD VALUES(2,'SRINATH',`
ADR_ORD_TYPE(10,'SSS','MYSORE',532),2000);
INSERT INTO EMP_OBJ_COL_ORD VALUES(3,'SRINATH',
ADR_ORD_TYPE(NULL,'SSS','BLORE',563432),2000);

PL/SQL Built-In Packages:

52
The Oracle Gift You Really Should Unwrap
Patrick Callahan
TUSC (www.tusc.com or 1.800.755.TUSC)

Introduction
Since the inception of Oracle7, we have been provided with several built-in packages to
supplement our PL/SQL development efforts. And still today, many applications are not
implementing them. This paper will provide participants the knowledge and necessary steps
needed to implement these effective built-in packages.

The numerous built-in packages that Oracle provides are widely under-utilized. These packages
are powerful supplements to the common PL/SQL functionality. The packages offer the ability to
alert other sessions that an event occurred, read and write files, dynamically create DML and
DDL statements, and much more. These powerful supplements should be implemented.

This paper included the following topics:

 DBMS_ALERT Built-In Package

 DBMS_PIPE Built-In Package

 UTL_FILE Built-In Package

 DBMS_SQL Built-In Package

DBMS_ALERT Built-In Package


The DBMS_ALERT built-in package provides easy communication to several database sessions.
The basis is that each user must register to be able to receive an alert. And when an alert is
signaled, all “registered” database sessions are sent the alert message. However, DBMS_ALERT
is transaction-based, meaning the signal does not occur until a commit happens. This is due the
fact that the alert information, as well as the registration list is kept in internal database tables and
the DML resulting from the alert changing or being signaled needs to be committed. So, when a
database event occurs, with alert functionality attached (like a database trigger), each registered
user receives notification.

53
Registering Sessions

The REGISTER procedure adds current session into the “registration list” for the specified alert.
A database session can register for multiple alerts at any time. Also, the REGISTER procedure
performs an implicit commit, since the session is being added to the internal registration table.

Procedure Syntax:

DBMS_ALERT.REGISTER( name IN VARCHAR2 );

Signaling an Alert

The SIGNAL procedure implements the “signaling” functionality, where the alert name and
message are specified and sent to all sessions registered for the alert. Remember, the alert
message is only “signaled” after a commit occurs. A rollback voids any uncommitted alert
signals. Also, alert names are not case-sensitive and have a maximum of 30 bytes. In addition,
the maximum message length is 1800 bytes.

Procedure Syntax:

DBMS_ALERT.SIGNAL( name IN VARCHAR2, message IN VARCHAR2);

Waiting for Alerts

The WAITONE procedure implements the message capturing functionality. For a specified alert,
an alert message is requested for. The procedure will wait for alert to be signaled for a specified
amount of time. If no alert message is captured within the specified time span, the procedure
times out. In any case, the WAITONE procedure returns the status of alert request and message,
if any. The status codes ( 0 - Alert Occurred, 1 - Timed Out) are used to determine whether or not
the message was successfully captured. Also, the “time-out” parameter values are in seconds.

The WAITANY procedure implements generic alert processing, waiting for all alerts that the
sessions are currently registered for. Different than the WAITONE procedure, the alert name of

54
the message that was captured is an OUT parameter. All other functionality is identical to the
WAITONE procedure.

Procedure Syntaxes:

DBMS_ALERT.WAITONE( name IN VARCHAR2, message OUT VARCHAR2, status OUT NUMBER, timeout IN
NUMBER );

DBMS_ALERT.WAITANY( name OUT VARCHAR2, message OUT VARCHAR2, status OUT NUMBER, timeout IN
NUMBER );

Removing user session from Registry

The REMOVE procedure removes the current session from the specified alerts registration lists.
The REMOVEALL procedure removes the current session from all alert registration lists. Both
the REMOVE and REMOVEALL procedures perform implicit commits.

Procedure Syntaxes:

DBMS_ALERT.REMOVE( name IN VARCHAR2 );

DBMS_ALERT.REMOVEALL;

Example of DBMS_ALERT Being Implemented in a Database Trigger


CREATE OR REPLACE TRIGGER bu_order

BEFORE UPDATE of order_filled

ON s_order

REFERENCING OLD AS OLD NEW AS NEW

FOR EACH ROW

WHEN ( NEW.order_filled = 'Y' )

BEGIN

-- If an order has been filled, notify all sessions with ORDER_ID

DBMS_ALERT.signal('ORDER_FILLED', TO_CHAR(:NEW.order_id));

END;

DBMS_PIPE Built-In Package


The DBMS_PIPE built-in package provides means for communication between different sessions
through a piped message. Messages are packed into a pipe to be received and unpacked at

55
another time, usually by another database session. So, multiple sessions can read or write to
public pipes. Each user session must receive and unpack the message to be read.

Packaging a Piped Message

The PACK_MESSAGE procedure packs data into the message buffer. Also, packing of a series
of data items, even with different data types, into the message buffer is allowed. This procedure
is overloaded for VARCHAR2, DATE and NUMBER data types.

Procedure Syntaxes:

DBMS_PIPE.PACK_MESSAGE( item IN VARCHAR2);

DBMS_PIPE.PACK_MESSAGE( item IN NUMBER);

DBMS_PIPE.PACK_MESSAGE( item IN DATE);

Sending a Piped Message

The SEND_MESSAGE function sends the contents of the message buffer into the specified pipe.
If the pipe does not yet exist, it will be implicitly created as a public pipe. The function returns
status code of the sent pipe ( 0 – Successful, 1 - Timed Out trying to send pipe, 3 - Pipe was
interrupted). Like DBMS_ALERT, the “time-out” parameter is in seconds.

Function Syntax:

DBMS_PIPE.SEND_MESSAGE( pipename IN VARCHAR2, timeout IN NUMBER, maxpipesize IN NUMBER )


RETURN NUMBER;

Additional Code For Reading a Piped Message

The NEXT_ITEM_TYPE function returns data type of next item in message buffer. The return
codes are as follows: ( 0 - No more items, 6 - number, 9 - varchar2, 11 - rowid, 12 - date,
23 - raw)

Function Syntax:

DBMS_PIPE.NEXT_ITEM_TYPE RETURN NUMBER;

56
Receiving a Piped Message

The RECEIVE_MESSAGE function receives a message from the named pipe and loads the
message into a local message buffer. It returns status code of the pipe being received (0 –
Successful, 1 - Timed Out, 3 - Pipe was interrupted). As before, the time-out parameter is in
seconds.

Function Syntax:

DBMS_PIPE.RECEIVE_MESSAGE( pipename IN VARCHAR2, timeout IN NUMBER ) RETURN INTEGER;

Unpacking and Reading a Piped Message

The UNPACK_MESSAGE procedure unpacks data from message buffer. This procedure is
overloaded for VARCHAR2, DATE and NUMBER data types. In addition, errors occur when
there are no more items in buffer or when data types do not match.

Procedure Syntaxes:

DBMS_PIPE.UNPACK_MESSAGE( item OUT VARCHAR2 );

DBMS_PIPE.UNPACK_MESSAGE( item OUT NUMBER );

DBMS_PIPE.UNPACK_MESSAGE( item OUT DATE );

Managing Pipes

The PURGE procedure clears all data from the pipe. However, the pipe itself will still exist, until
a point where it is pushed out of the SGA.

Procedure Syntax:

DBMS_PIPE.PURGE( pipename IN VARCHAR2 );

Example of DBMS_PIPE Being Implemented in a Database Trigger


CREATE OR REPLACE TRIGGER bu_order

BEFORE UPDATE of order_filled

ON s_order

REFERENCING OLD AS OLD NEW AS NEW

FOR EACH ROW

57
WHEN ( NEW.order_filled = 'Y' )

DECLARE

CURSOR user_cursor IS

SELECT DISTINCT username

FROM v$session

WHERE NVL(username,'*') NOT IN ( 'SYS', 'SYSTEM', '*');

pipe_status INTEGER;

BEGIN

-- If an order has been filled, notify all sessions with ORDER_ID and TOTAL

-- For all non-system users, send the piped message

FOR user_rec IN user_cursor

LOOP

DBMS_PIPE.purge ( user_rec.username );

DBMS_PIPE.pack_message ( :NEW.order_id );

DBMS_PIPE.pack_message ( :NEW.total );

pipe_status := DBMS_PIPE.send_message ( user_rec.username );

END LOOP;

END;

UTL_FILE Built-In Package ( Release 2.3 + )


Finally, Oracle allows for file I/O from within the database. Now, files can be read and written to
on the database server. Files can be opened as read-only, overwrite or append. And when reading
lines from a file, the process follows from top to bottom. For writing data to a file, partial and
entire lines can be written. After all processing, the files must be closed.

INIT.ORA Parameter

For the UTL_FILE built-in package to function, the UTL_FILE_DIR parameter must be specified
in the INIT.ORA parameter file. Each directory to be accessed on the server must be specified
separately or the directory permissions can be disabled. The permissions are disabled by simply
setting the UTL_FILE_DIR parameter to an asterisk (*). If directories are specified here, later
directory comparisons are always case sensitive. In any case, all file I/O operations are

58
performed by ORACLE user and therefore, the ORACLE user must have operating system
permissions on the specified directories.

UNIX Examples:

UTL_FILE_DIR = /temp

UTL_FILE_DIR = /home/oracle/errors

UTL_FILE_DIR = /home/oracle/output

UTL_FILE_DIR = /loaders

Windows NT Examples:

UTL_FILE_DIR = c:\temp

UTL_FILE_DIR = c:\app\errors

UTL_FILE_DIR = c:\app\output

UTL_FILE_DIR = d:\loaders

To disable database permissions in either UNIX or Windows NT:

UTL_FILE_DIR = *

FILE_TYPE Usage

UTL_FILE.FILE_TYPE is used as data type of file handle for all functions and procedures of the
UTL_FILE built-in package. A variable of FILE_TYPE is necessary for every implementation of
UTL_FILE. In a stored procedure or package in which the UTL_FILE package is to be
implemented, file handles must be declared.

Opening a File

The FOPEN function opens file for input or output. The function returns the “file ID” with a
UTL_FILE.FILE_TYPE data type. The specified files can be opened in Read(R), Write(W), or
Append(A) modes. The IS_OPEN function also available for special processing. This function
returns a boolean whether or not file is currently open.

Module Syntaxes:

UTL_FILE.FOPEN( location IN VARCHAR2, filename IN VARCHAR2, open_mode IN VARCHAR2);

UTL_FILE.IS_OPEN( file IN UTL_FILE.FILE_TYPE) RETURN BOOLEAN;

59
Reading From A File

The GET_LINE procedure reads one line of text from the specified file at a time. The new line
character not included in return string. When the end of the file is reached, the
NO_DATA_FOUND exception occurs. Also, if the file line does not fit into buffer variable
supplied, the VALUE_ERROR exception occurs. If the file was not opened in Read Mode(R),
the UTL_FILE.INVALID_OPERATION exception will be raised when the GET_LINE procedure
is called. And if an operating system error occurs while reading from the file, the
UTL_FILE.READ_ERROR exception will be raised.

Procedure Syntax:

UTL_FILE.GET_LINE( file IN UTL_FILE.FILE_TYPE, buffer OUT VARCHAR2 );

Writing to a File

In order to write to a file, the file must be open for with the W(overwrite) or A(append) modes.
The PUT procedure outputs a specified string to the specified file, without a new line character.
The PUT_LINE procedure, however, outputs a specified string to the specified file, along with
new line character following. This is equivalent to call to the PUT procedure followed by call to
the NEW_LINE procedure. If the file was not opened in W (overwrite) mode or the A (Append)
mode, the UTL_FILE.INVALID_OPERATION exception will be raised when the PUT or
PUT_LINE procedures are called. Also, if an operating system error occurs while writing to the
file, the UTL_FILE.WRITE_ERROR exception will be raised.

Procedure Syntaxes:

UTL_FILE.PUT( file IN UTL_FILE.FILE_TYPE, buffer IN VARCHAR2 );

UTL_FILE.PUT_LINE( file IN UTL_FILE.FILE_TYPE, buffer IN VARCHAR2 );

Closing a File

The FCLOSE procedure closes the specified file. The FCLOSE_ALL procedure closes all open
files. This procedure should be used for cleanup within error handling procedures. If the

60
specified file was not open, the UTL_FILE.INVALID_FILEHANDLE exception will be raised
when the FCLOSE procedure is called. Also, if an operating system error occurs while closing
files, the UTL_FILE.WRITE_ERROR exception will be raised. These procedures free resources
used by UTL_FILE to operate on the file.

Procedure Syntaxes:

UTL_FILE.FCLOSE( file IN UTL_FILE.FILE_TYPE );

UTL_FILE.FCLOSE_ALL;

Handling Exceptions

When incorporating the UTL_FILE package into processes, proper error handling is
recommended, if not for “good” PL/SQL programming techniques, for ease of debugging
processes. There are numerous pre-defined exceptions found in the UTL_FILE package:
UTL_FILE.INVALID_PATH, UTL_FILE.INVALID_MODE,
UTL_FILE.INVALID_FILEHANDLE, UTL_FILE.INVALID_OPERATION,
UTL_FILE.READ_ERROR, UTL_FILE.WRITE_ERROR, and
UTL_FILE.INTERNAL_ERROR. Each UTL_FILE defined exception should be included into
the exception block if possible to be raised. Use RAISE_APPLICATION_ERROR to assign an
error number and descriptive message to these exceptions. Otherwise, if these errors occur, an
unexplained exception while occur, providing no description of the error.

Example of a Procedure to Search a Text File for a Character String


CREATE OR REPLACE PACKAGE search IS

/*

DESCRIPTION: This package provides the functionality of searching a given file

for a character string, then provides output on the lines the string was found.

Finally, if the string was not found, a message is provided.

*/

-- Package Variables

lv_file_id UTL_FILE.file_type;

lv_filename VARCHAR2(30);

lv_error_desc VARCHAR2(50);

61
PROCEDURE string_line ( string VARCHAR2,

directory VARCHAR2 := 'c:\temp',

filename VARCHAR2 := 'prodlist.dat' );

END search;

CREATE OR REPLACE PACKAGE BODY search IS

PROCEDURE string_line ( string VARCHAR2,

directory VARCHAR2 := 'c:\temp',

filename VARCHAR2 := 'prodlist.dat' ) IS

-- This procedure will read the product data file and load/update

-- the information in the correct tables.

lv_line_cnt NUMBER := 0;

lv_buffer VARCHAR2(2000);

lv_found BOOLEAN := FALSE;

BEGIN

-- Open the data load file on the server so it can be read only.

lv_file_id := UTL_FILE.fopen( directory, filename, 'R');

LOOP

lv_buffer := NULL;

lv_line_cnt := lv_line_cnt + 1;

BEGIN << get_next_line >>

UTL_FILE.get_line(lv_file_id, lv_buffer);

EXCEPTION

WHEN no_data_found

THEN

Exit;

END get_next_line;

IF ( INSTR(UPPER(lv_buffer), UPPER(string)) > 0 )

THEN

DBMS_OUTPUT.PUT_LINE('Line ' || lv_line_cnt || ': ' || lv_buffer);

lv_found := TRUE;

END IF;

END LOOP;

62
IF NOT ( lv_found )

THEN

DBMS_OUTPUT.PUT_LINE('The string was not found in the file.');

END IF;

UTL_FILE.fclose_all;

EXCEPTION

WHEN UTL_FILE.internal_error

THEN

UTL_FILE.fclose_all;

RAISE_APPLICATION_ERROR(-20111, 'UTL_FILE.INTERNAL_ERROR encountered');

WHEN UTL_FILE.invalid_filehandle

THEN

UTL_FILE.fclose_all;

RAISE_APPLICATION_ERROR(-20112, 'UTL_FILE.INVALID_FILEHANDLE encountered');

WHEN UTL_FILE.invalid_mode

THEN

UTL_FILE.fclose_all;

RAISE_APPLICATION_ERROR(-20113, 'UTL_FILE.INVALID_MODE encountered');

WHEN UTL_FILE.invalid_operation

THEN

UTL_FILE.fclose_all;

RAISE_APPLICATION_ERROR(-20114, 'UTL_FILE.INVALID_OPERATION encountered');

WHEN UTL_FILE.invalid_path

THEN

UTL_FILE.fclose_all;

RAISE_APPLICATION_ERROR(-20115, 'UTL_FILE.INVALID_PATH encountered');

WHEN UTL_FILE.read_error

THEN

UTL_FILE.fclose_all;

RAISE_APPLICATION_ERROR(-20117, 'UTL_FILE.READ_ERROR encountered');

WHEN others

THEN

UTL_FILE.fclose_all;

RAISE_APPLICATION_ERROR(-20103, 'Unhandled exception occurred '||

'while reading data file (DATA_LOAD.load_products).');

END string_line;

END search;

63
DBMS_SQL Built-In Package ( Release 2.1 + )
The DBMS_SQL built-in package allows for SQL statements to be dynamically created. The
SQL statements created as character strings, return columns and variables are defined, the SQL
statement string is explicitly parsed and executed, and the SQL statement results are processed.
There are several different types of statements that can be processed by the DBMS_SQL built-in
package: non-query DML statements (i.e. Inserts, Deletes and Updates), DDL statements (i.e.
Drop Table, Create Table, etc. ), query statements, and PL/SQL blocks. However, for this
discussion, only the DDL and query statements will be covered, since they are most commonly
implemented.

Performing DDL Statements

To perform DDL statements, the steps for using DBMS_SQL are quite simple: open the cursor,
parse the DDL statement, and close the cursor. For DDL statements, the EXECUTE function is
not necessary; the PARSE statement actually performs the statement. Please note that the user
performing the DDL statement must be granted the system privilege outside of the default
database roles, like RESOURCE. So, to use DBMS_SQL to create a temporary table, the
CREATE ANY TABLE privilege must be granted to the user.

Opening a Cursor

The OPEN_CURSOR function returns a cursor ID that will be used in subsequent procedure
calls. A call to the OPEN_CURSOR function should have accompanying CLOSE_CURSOR call
to free system resources.

Function Syntax:

DBMS_SQL.OPEN_CURSOR RETURN NUMBER;

Parse Statement

The PARSE procedure sends the statement to the server. In doing so, it checks syntax and
semantics of the statement and returns errors if they occur. Through this, an execution plan is

64
determined. Also, a database version/language must be specified (V6, V7 or NATIVE).
However, Oracle8 behaves the same way as Oracle7. In creating the statement, all trailing semi-
colons are excluded for DML and DDL statements.

Procedure Syntax:

DBMS_SQL.PARSE( c IN INTEGER, statement IN VARCHAR2, language_flag IN NUMBER );

Close Cursor

The CLOSE_CURSOR procedure simply frees resources allocated to the cursor. This procedure
should be used with all open cursors.

Procedure Syntax:

DBMS_SQL.CLOSE_CURSOR( c IN OUT NUMBER );

Executing Queries

For dynamically creating query statements with DBMS_SQL, there are a few more steps. After
the statement is parsed, bind variables can be declared and columns must be defined. Then the
statement needs to be executed and rows fetched. Lastly, the column values must be accepted and
processed.

Bind Any Input Variables

The BIND_VARIABLE procedure associates place holders with variables. The placeholders
often identified by colon, but this syntax is optional. If no place holders are present, no bind
variables are necessary. This procedure is overloaded for different data types and lengths. Using
the out_value_size parameter is recommended for VARCHAR2 data types, so that the bind
variable is not initialized as having no length, which would happen if the value sent in was null.
Please note that bind variables are illegal for DDL statements.

Procedure Syntaxes:

DBMS_SQL.BIND_VARIABLE( c IN NUMBER, name IN VARCHAR2, value IN VARCHAR2 );

65
DBMS_SQL.BIND_VARIABLE( c IN NUMBER, name IN VARCHAR2, value IN VARCHAR2, out_value_size
IN NUMBER );

Execute Statement

The EXECUTE function returns the number of rows processed. Again, the call to the EXECUTE
procedure is unnecessary for DDL statements, but causes no harm.

Function Syntax:

DBMS_SQL.EXECUTE( c IN NUMBER ) RETURN NUMBER;

Defining Output Variables

The DEFINE_COLUMN procedure is necessary for every column in the select statement. The
data type and length can be specified for a column by the column position on the select statement.

Procedure Syntaxes:

DBMS_SQL.DEFINE_COLUMN( c IN NUMBER, position IN NUMBER, column IN VARCHAR2 );

DBMS_SQL.DEFINE_COLUMN( c IN NUMBER, position IN NUMBER, column IN VARCHAR2, column_size


IN NUMBER );

Fetching Rows

The FETCH_ROWS function fetches rows associated with defined query. It returns the number
of rows fetched so far. Also, the function returns zero (0) when all rows have been fetched.
Often, the FETCH_ROWS function is used in loops to process one row at a time. If used within
a loop to fetch all records, the exit condition should be when the return value of FETCH_ROWS
is equal to zero, not when the NO_DATA_FOUND exception is raised or when the
%NOTFOUND cursor attribute is TRUE.

Function Syntax:

DBMS_SQL.FETCH_ROWS( c IN NUMBER ) RETURN NUMBER;

Alternative to EXECUTE & FETCH_ROWS Functions

66
The EXECUTE_AND_FETCH function can replace the EXECUTE function call and the first
call to the FETCH_ROWS function, using only one network trip. This can improve performance
on remote databases. This function returns the number of rows fetched so far, like the
FETCH_ROWS function. Also, one can specify if an error should occur if more than one row
returned. If the exact parameter is sent in as TRUE, the TOO_MANY_ROWS exception will be
raised if the query returns more than one row. If sent in as FALSE, the exception is never raised.

Function Syntax:

DBMS_SQL.EXECUTE_AND_FETCH( c IN NUMBER, exact IN BOOLEAN ) RETURN NUMBER;

Return Results into Variables

The COLUMN_VALUE procedure moves the buffered data into PL/SQL variables. Typically,
these are the same variables that were used with DEFINE_COLUMN procedure. A
COLUMN_VALUE procedure call should match every DEFINE_COLUMN procedure call. This
procedure provides column level errors and is overloaded for different data types.

Procedure Syntax:

DBMS_SQL.COLUMN_VALUE( c IN NUMBER, position IN NUMBER, value OUT VARCHAR2 );

DBMS_SQL.COLUMN_VALUE( c IN NUMBER, position IN NUMBER, value OUT VARCHAR2, column_error


OUT NUMBER,actual_length OUT NUMBER );

Available Error Functions

 LAST_ERROR_POSITION - Returns byte offset within statement where error occurred

 LAST_ROW_COUNT - Returns cumulative count of number of rows fetched

 LAST_ROW_ID - Returns rowid of last row processed

 LAST_SQL_FUNCTION_CODE - Returns function code for SQL statement currently being


executed

 IS_OPEN - Returns TRUE if cursor is already open, otherwise FALSE

67
Function Syntaxes:

DBMS_SQL.LAST_ERROR_POSITION RETURN NUMBER;

DBMS_SQL.LAST_ROW_COUNT RETURN NUMBER;

DBMS_SQL.LAST_ROW_ID RETURN ROWID;

DBMS_SQL.LAST_SQL_FUNCTION_CODE RETURN NUMBER;

DBMS_SQL.IS_OPEN ( c IN NUMBER ) RETURN BOOLEAN;

Example of a Procedure To Be Used For Performing DDL Statements


CREATE OR REPLACE PROCEDURE exec_ddl ( p_statement VARCHAR2 )

IS

/*

DESCRIPTION: This procedure provides a way to dynamically perform any

DDL statements from within your normal PL/SQL processing.

*/

exec_cursor INTEGER := DBMS_SQL.OPEN_CURSOR;

rows_processed NUMBER := 0;

lv_statement VARCHAR2(30000);

begin

lv_statement := p_statement;

DBMS_SQL.PARSE ( exec_cursor, lv_statement, DBMS_SQL.V7 );

rows_processed := DBMS_SQL.EXECUTE ( exec_cursor );

DBMS_SQL.CLOSE_CURSOR ( exec_cursor );

EXCEPTION

WHEN others THEN

IF ( DBMS_SQL.IS_OPEN ( exec_cursor ) )

THEN

DBMS_SQL.CLOSE_CURSOR ( exec_cursor );

END IF;

RAISE;

END exec_ddl;

Summary

68
The numerous built-in packages that Oracle provides should be utilized. These packages are
powerful supplements to the common PL/SQL functionality. These packages offer the ability to
schedule a database job, alert other sessions that an event occurred, read and write files,
dynamically create DML and DDL statements, and much more. These powerful supplements
need to be implemented.

References
Oracle8 PL/SQL Programming, Scott Urman, Osborne McGraw-Hill

Oracle PL/SQL Programming, Steven Feuerstein, O’Reilly & Assoc.

PL/SQL Users Guide, Oracle Documentation, Primary Author: Tom Portfolio, Oracle Corporation

Special Thanks To
Joe Trezzo, Felip Lacap, Brett Feldmann, Tony Catalano, Greg Pucka, Jake Van der Vort, David
Ventura, Alain Campos, Matthew Malcheski, and every member of the TUSC team that helped
out in any way.

About the Author


Patrick Callahan is a Senior Consultant at TUSC’s Chicago office. He actively participates in
developing and implementing PL/SQL in stored code and within the Oracle Development tools.
He also is an instructor for several TUSC training classes, including Intro. and Advanced Oracle
Forms and PL/SQL and Advanced PL/SQL. Patrick can be reached at callahanp@tusc.com.

------------------------------------------------------------------------
24/06/97 PL/SQL PROGRAMS
------------------------------------------------------------------------

** 1. PROGRAM TO FIND ODD & EVEN NUMBERS

BEGIN
for i in 1..10 loop
if mod(i,2) = 0 then

69
dbms_output.put_line(i||' is an even number');
else
dbms_output.put_line(i||' is an odd number');
end if;
end loop;
END;

** 2. PROGRAM TO REVERSE A GIVEN STRING

DECLARE
len number;
str varchar2(20) := '&str';
rev_str varchar2(20);
conchar char(1);

BEGIN
len := length(str);
for i in REVERSE 1..len loop
conchar := substr(str,i,1);
rev_str := rev_str||conchar;
end loop;
dbms_output.put_line('REVERSED STRING IS : '||rev_str);
END;

** 3. PROGRAM TO CHECK A STRING FOR PALINDROME

DECLARE
len number;
palstr varchar2(20) := '&palstr';
chkstr varchar2(20);

BEGIN
len := length(palstr);
for i in REVERSE 1..len loop
chkstr := chkstr||substr(palstr,i,1);
end loop;
if chkstr = palstr then
dbms_output.put_line(palstr||' is a PALINDROME');
else
dbms_output.put_line(palstr||' is not a PALINDROME');
end if;
END;

** 4. PROGRAM TO GENERATE FIBONACCI SERIES UPTO 'n' NUMBERS

DECLARE
n number := &n;
t1 number := 0;
t2 number := 1;
t3 number;
BEGIN

70
dbms_output.put_line(t1);
dbms_output.put_line(t2);
for i in 3..n loop
t3 := t1 + t2;
dbms_output.put_line(t3);
t1 := t2;
t2 := t3;
end loop;
END;

** 5. PROGRAM TO GENERATE MULTIPLICATION TABLE FOR A NUMBER 'n'

DECLARE
n number := &n;
prod number;
BEGIN
for i in 1..10 loop
prod := n * i;
dbms_output.put_line(n||' * '||lpad(i,2,' ')
||' = '||lpad(prod,3,' '));
end loop;
END;

------------------------------------------------------------------------
-
26/06/97 CURSORS
------------------------------------------------------------------------
-

** 6. SIMPLE PROGRAM IMPLEMENT EXPLICIT CURSORS

DECLARE
name emp.ename%type;
dept emp.deptno%type;

cursor c1 is select ename,deptno from emp where sal >2000;

BEGIN
open c1;
dbms_output.put_line('NAME DEPARTMENT');
loop
fetch c1 into name, dept;
exit when c1%notfound;
dbms_output.put_line(name||' '||dept);
end loop;
close c1;
END;

** 7. SIMPLE PROGRAM USING PARAMETERIZED CURSOR

DECLARE
name emp.ename%type;
dept emp.deptno%type;
gsal emp.sal%type;

71
cursor c1(salary number) is select ename,deptno,sal
from emp where sal >salary;

BEGIN
open c1(3000);
dbms_output.put_line('NAME DEPARTMENT salary');
loop
fetch c1 into name,dept,gsal;
exit when c1%notfound;
dbms_output.put_line(name||' '||dept||' '||gsal);
end loop;
close c1;
END;

** 8. MASTER TABLE - 'INVENTORY' AND TRANSACTION TABLE - 'INVNT_TRANS'


UPDATION DEPENDING ON THE ORDER PLACED BY THE CUSTOMER

DECLARE
ipname inventory.prod_name%type := '&ipname'; --name of prod
ordered
pid inventory.prod_id%type;
ipqty inventory.stk_qty%type := &ipqty; -- qty of prod ordered
cpqty inventory.stk_qty%type; -- qty or stock of prod available

cursor c1 is select stk_qty from inventory where


prod_name = ipname;
BEGIN
open c1;
loop
fetch c1 into cpqty;
exit when c1%notfound;
if cpqty >= ipqty then
update inventory set stk_qty=cpqty - ipqty
where prod_name = ipname;
insert into invnt_trans values(sysdate,ipname,cpqty,ipqty);
dbms_output.put_line(ipname||' order complete');
else
dbms_output.put_line(ipname||' no stock....');
end if;
end loop;
close c1;
END;

** 9. MASTER TABLE - 'ACC_MASTER' AND TRANSACITON TABLE - 'ACC_TRANS'


UPDATION ONLY FOR CUSTOMER'S WITHDRAWAL

DECLARE
acno acc_master.acc_id%type := &acno;

72
wthdrw acc_master.balance%type := &wthdrw;
bal acc_master.balance%type;

cursor c1 is select balance from acc_master


where acc_id = acno;

min_bal exception;
lar_amt exception;

BEGIN
open c1;
loop
fetch c1 into bal;
exit when c1%notfound;
if bal = 1000 then
raise min_bal;
elsif bal - wthdrw <1000 then
raise lar_amt;
else -- if (bal - wthdrw >= 1000)
dbms_output.put_line('MASTER AND TRANS TABLES
UPDATED!!!!!!');
update acc_master set
balance = bal - wthdrw where acc_id = acno;
insert into acc_trans values(acno,sysdate,wthdrw,0,bal-
wthdrw);
end if;
end loop;
close c1;
EXCEPTION
when min_bal then
dbms_output.put_line('SORRY!!! BALANCE MINIMUM - CANNOT
WITHDRAW...');
when lar_amt then
dbms_output.put_line('MINIMUM BALANCE NOT BEING
MAINTAINED !!!');
END;

--------------------------------------------------------------------

73
01/07/97 DATABASE TRIGGERS
--------------------------------------------------------------------
** 1. TO PUT A CHECK ON EMP TABLE ON (SATURDAY & SUNDAYS), (BETWEEN
8 AM AND 6PM), AND ON (GENERAL HOLIDAYS)

[PREREQUISITE : TO CREATE TABLE - 'GEN_HOLS' ]

create or replace trigger emp_chk


before
insert or delete or update on emp

DECLARE
v_count number;

BEGIN
if (to_char(sysdate,'DY') = 'SAT' or to_char(sysdate,'DY') = 'SUN')
then
raise_application_error(-20001,'DML OPERATIONS NOT ALLOWED ON
WEEKENDS');
end if;

/* if (to_char(sysdate,'HH24') < 8 or to_char(sysdate,'HH24') > 18)


then
raise_application_error(-20002,'TIME OUT');
end if;
*/
select count(*) into v_count from gen_hols where
trunc(hol_date) = trunc(sysdate);
if v_count > 0 then
raise_application_error(-20003,'HAPPY HOLIDAY!!!!!!!');
end if;
END;

** 2. a)TO REPLICATE A RECORD WHEN A RECORD IS DELETED IN DEPT TABLE AND


b)TO DELETE ALL RECORDS FROM EMP (CHILD) TABLE WHEN A RECORD IS
DELETED IN THE DEPT (MASTER) TABLE FOR A PARTICULAR DEPTNO.
(similar to delete on cascade)

create or replace trigger replica_delcas


after
delete or update on dept
for each row

BEGIN
insert into dept_rep values(:old.deptno, :old.dname, :old.loc); /*
replica */

delete from emp where deptno = :old.deptno; /* delcas */


END;

74
** 3. TO DELETE A CUSTOMER FROM THE CUSTOMER TABLE A CHECK SHOULD
BE MADE ON THE ORD TABLE IF HE HAS PLACED AN ORDER, IF SO AN
APPLICATION ERROR HAS TO BE RAISED

create or replace trigger cust_del


before
delete on customer
for each row

DECLARE
v_custno ord.custid%type;

cursor c1(custno number) is select custid from ord


where custid = custno;
/*rowid = (select min(rowid) from ord where
custid = custno);*/
BEGIN
open c1(:old.custid);
fetch c1 into v_custno;
if c1%found then
raise_application_error(-20003,'ORDER PLACED AGAINST THIS
CUSTOMER!!!');
/* else
null;*/
end if;
close c1;
END;

** 4. TO IMPLEMENT FOREIGN KEY ON THE ORD(CHILD) AND CUSTOMER (MASTER)


TABLE FOR INSERTING RECORDS

create or replace trigger fornkey_chk


before
insert on ord
for each row

DECLARE
custno number;
cursor c1(custno number) is select custid from customer
where custid = custno;

BEGIN
open c1(:new.custid);
fetch c1 into custno;
if c1%found then
null;
else
raise_application_error(-20004,' SORRY CUSTOMER NOT A
MEMBER....');
end if;
close c1;
END;

75
** 5. TO KEEP TRACK OF ALL EVENTS GOING ON FOR A TABLE AND STORING
THE EVENTS IN A TABLE CALLED 'LOG_REC'

create or replace trigger event_log


after
insert or delete or update on emp
for each row

BEGIN
if inserting then
insert into event_log values(sysdate, 'insertion');
elsif deleting then
insert into event_log values(sysdate, 'deletion');
elsif updating then
insert into event_log values(sysdate, 'updation');
end if;
END;

---------------------------------------------------------------------
03/07/97 SUBPROGRAMS
---------------------------------------------------------------------

** 1. TO INCREMENT SALARY OF AN EMPLOYEE - (PROCEDURE)

DECLARE
empid number := 7369;
amount number(7,2) := 1000;

Procedure inc_sal(empnum number, increment number) is


curr_sal number(7,2);
salary_missing exception;
Begin
select sal into curr_sal from emp where empno = empnum;
if curr_sal is null then
raise salary_missing;
else
update emp set sal = sal + increment where
empno = empnum;
dbms_output.put_line('salary updated!!!!!');
end if;
Exception
when no_data_found then
dbms_output.put_line('no such employee exists!!!!');
when salary_missing then
dbms_output.put_line('salary is missing-please update..');
End inc_sal;

BEGIN
inc_sal(empid,amount);
END;

76
** 2. TO CHECK IF ALLOTED SALARY IS IN THE ALLOWABLE RANGE
DEPENDING ON THE JOB - (FUNCTION)

DECLARE
sal_alotd number(7,2) := 2000;
job_alotd emp.job%type := 'ANALYST';

Function sal_ok(salary number, title varchar2) return boolean is


losal number(7,2);
hisal number(7,2);
Begin
select min(sal), max(sal) into losal,hisal
from emp group by job having job = title;
return((salary >= losal) and (salary <= hisal));
End;

BEGIN
if sal_ok(sal_alotd,job_alotd) then
dbms_output.put_line('salary in the allowable range...');
else
dbms_output.put_line('salary not in the allowable range...');
end if;
END;

** TO CREATE RANDOM NUMBERS(AMSOFT)

create or replace procedure randnum is


init number(3) := 001;
noq number(2) := &noq;
num number(3);
maxi number(3) := &maxi;
cnt number(2) := 1;
x number(2);
Begin
for I in 1 .. maxi loop
x := max - I;
num := x * I;
num := mod(num,maxi);
dbms_output.put_line(num);
cnt := cnt + 1;
if cnt >= noq then
exit;
end if;
end loop;
End;

77
************************************************************************
**
shuba
************************************************************************
**

1. ********** Program to print reverse of values *************

declare
a number(2);
b number(2);
begin
dbms_output.put_line('PROGRAM TO PRINT REVERSE VALUES');

a:= &a;
b:= &b;
a:= a+b;
b:= a - b;
a:= a-b;
dbms_output.put_line('New value of a is '||a);
dbms_output.put_line('New value of b is '||b);
end;

2. ************ Stripping part Numbers of unwanted characters **********

declare
ptno char(22);

begin
dbms_output.put_line ('Enter the unstripped part Number ');
ptno := '&ptno';
select replace(ptno,' ') into ptno from dual;
select replace(ptno,'.') into ptno from dual;
select replace(ptno,'*') into ptno from dual;
select replace(ptno,'-') into ptno from dual;
dbms_output.put_line ('The stripped part Number is '||ptno);
end;

3. *********** Program to generate Prime Numbers ***********

declare
i number := &num;
flag number(1) :=0;

begin
for l in 2..i loop
for m in 2..l-1 loop
if mod(l,m) = 0 then
flag := 1;
exit;
end if;
end loop;

78
if flag = 0 then
dbms_output.put_line('* '||l);
end if;
flag := 0;
end loop;
end;

4. ************ Program for Fibonacci Series ******************

declare
num1 number := -1;
num2 number := 1;
tot number;
n number:=&num; -- number of terms
begin
for l in 1..n loop
tot := num1 + num2;
-- dbms_output.put_line(tot);
dbms_output.put(tot);
num1 := num2;
num2 := tot;
end loop;
dbms_output.new_line;
end;

5. *********** create user defined datatype (arrays) ************

declare
type arrnum is table of number --here "number" is the datatype
index by binary_integer;

type arrchar is table of char --here "char" is the datatype


index by binary_integer;

a arrnum;
b arrchar;
k binary_integer :=0;

begin
for i in (select * from emp1) loop
-- SQL statement in a FOR LOOP
k := k+1;
a(k) := i.empno;
b(k) := i.ename;
end loop;

for m in 1..k loop


dbms_output.put_line(' '||a(m)||' '||b(m));
end loop;
end;

79
6. *********** Procedure to check for PALINDROME and
to print the reverse of the string **************

## procedure created using concatenate function ##

declare
word varchar2(15) := '&word';
r_word varchar2(15);
c char(1);
n integer(2);

begin
n := length(word);

for i in reverse 1..n loop


c :=substr(word,i,1);
r_word := concat(r_word,c);
end loop;

/* to display the reverse of the string */

dbms_output.put_line('The reverse of the string is '||r_word);

if r_word != word then


dbms_output.put_line('Not a PALINDROME word');
else
dbms_output.put_line('PALINDROME word');
end if ;
end;

7. *********** Procedure for scope of variables ************

declare
a number:=5;
begin
declare
a number:=10; -- the scope of this variable is limited
-- only to this block
b number:=6;
begin
dbms_output.put_line(a);
dbms_output.put_line(b);
dbms_output.put_line(a+b);

end;
dbms_output.put_line(a);

end;

80
8. ********** Procedure with GOTO statement ***************

declare
a number:=&num;

begin
if a >10 then
goto aa;
else
dbms_output.put_line(a);
end if;

<<aa>>
dbms_output.put_line(a+a);
end;

9. ************ Procedure with TOO_MANY_ROWS error handling ***********

declare
a number;
b number;
begin
select sal into a from emp where empno = 7900;
dbms_output.put_line ('Salary is '|| a);

select ename into b from emp where empno = 7900;


dbms_output.put_line (b);
exception
when too_many_rows then
dbms_output.put_line ('Too Many Rows... ');
when no_data_found then
dbms_output.put_line ('Invalid EMPNO... ');
when value_error then
dbms_output.put_line ('Data Mismatch... ');
end;

10. ********** Procedure with USER-DEFINED EXCEPTIONS ***********

declare
ude1 exception; -- to indicate that the exceptional errors
a number;
begin
select sal into a from emp where empno = 7900;
if a <4000 then
raise ude1;
else
dbms_output.put_line('The salary is '||a);
end if;

81
exception
when ude1 then
dbms_output.put_line('Poor salary ...');
end;

11. **************** Procedure with RECORDS **************

## This is used to process several records at the same time ####

declare
Type emprec is record
(name emp.ename%type,
salary emp.sal%type,
dob emp.hiredate%type);
a emprec;

begin
select ename,sal,hiredate into a from emp where job = 'PRESIDENT';

dbms_output.put_line ('The features are '|| a.name


|| a.salary || a.dob);
end;

12. *********** Procedure with EXCEPTIONS **************

declare
emp emp1.empno%type;
name emp1.ename%type;
sal1 emp1.sal%type;
num number;

cursor c1 is select empno,ename,sal from emp1;

begin
-- select sal into num from emp1 where mgr=7839;
open c1;
loop
fetch c1 into emp,name,sal1;
exit when c1%notfound;

end loop;
exception when too_many_rows then
declare
cursor c2 is select max(sal) from emp where mgr=7839;
begin
open c2;
update emp1 set comm = 500 where empno = 7839;
close c2;
end;
close c1;
end;

82
13. ******** Procedure to select 1st 'n' higest persons & salaries using
PL/SQL

declare

cursor c1 is select ename,sal from emp order by sal desc;

a emp.sal%type;
b emp.ename%type;
n number :=&num;

begin
open c1;
loop
fetch c1 into b,a;
exit when c1%rowcount =6;
dbms_output.put_line(b||' '||a);
end loop;
end;

14. ********* Procedure to find the nth maximum salary use PL/SQL
********

## Procedure created using parameterized cursors ##


## This procedure eliminates the highest salaries step by step
in the outer loop ##

declare
cursor c1(a number) is select sal from emp where sal <a ;

a number := 1000000; -- random high salary for comparison


b number :=0; -- initializing variable also
n number(2) :=&num;

begin
for i in 1..n
loop
for j in c1(a)
loop
if b<j.sal then
b:= j.sal;
end if;
end loop;
a := b;
b := 0;
end loop;
dbms_output.put_line('The '||n||'th maximum salary is '||a);
close c1;

83
end;

##### Above Procedure created using select statements ######

declare
n number(2) := &num;
a emp.sal%type;
b emp.sal%type;
begin
select max(sal) into a from emp;

for i in 1..n-1
loop
select max(sal) into b from emp where sal <a;
a := b;
end loop;
dbms_output.put_line('The '||n||'th maximum salary is '||b);
end;

15. ##### Procedure with parameterized cursors ######

declare
cursor c1(a number) is select * from emp where deptno = a;

b number (2) := &num ;


r emp%rowtype; -- the whole row is stored in this variable

begin
open c1(b);
loop
fetch c1 into r;
exit when c1%notfound;
dbms_output.put_line('Name :'||r.ename||'; Salary is '||r.sal);
end loop;
close c1;
end;

16. *********** parameterized cursor with DEFAULT OPTION ************

## useful when no value is returned while accepting values from user ##

declare
cursor c1(a number default 30 ) is select * from emp where deptno =
a;

r emp%rowtype; -- the whole row is stored in this variable

begin
open c1; -- no parameter need be given unlike in the previous one

84
loop
fetch c1 into r;
exit when c1%notfound;
dbms_output.put_line('Name :'||r.ename||'; Salary is '||r.sal);
end loop;
close c1;
end;

17. ###### another example for the above ###########


declare
cursor c1(a number default 7900, b char) is
select * from emp where empno=a and job = b;
r emp%rowtype;
begin
open c1(7839,'PRESIDENT');
loop
fetch c1 into r;
exit when c1%notfound;
dbms_output.put_line('Emp number is '|| r.empno||' and job is '||
r.job);
end loop;
close c1;
end;

18. ******** Procedure using WHERE CURRENT OF CURSOR ********

## to use the "WHERE CURRENT OF CURSOR", the "FOR UPDATE"


is a must in the cursor " as in the example below ######
NOTE:- the method of declaration of the variable ####
## this is one more method of declaration of cursors ####

declare
cursor c1 is select sal from emp for update;
-- a c1%rowtype; the variable "a" is of type cursor c1;
begin
for i in c1 loop
update emp set comm = i.sal * 0.10 where current of c1;
end loop;
end;

19. ****** Procedure to delete rownum 6 to 9 and update 10 to end


using the WHERE CURRENT OF CURSOR ************

declare

cursor c1 is select * from emp for update;


a c1%rowtype;

begin
open c1;
loop
fetch c1 into a ;
exit when c1%notfound;

85
if c1%rowcount <= 6 then
null;
elsif c1%rowcount between 6 and 9 then
delete from emp where current of c1;
else
update emp set comm = a.sal * 0.10 where current of c1;
end if ;
end loop;
close c1;
end;

20. ********* PROCEDURES AS NON-STORED PROGRAM UNITS ********

declare

b number := &num;

Procedure p1(a number) is


begin -- procedure body
dbms_output.put_line(a);
end;

BEGIN -- main procedure body


p1(b);
END;

21. ********** Procedure Non-Stored Program Units


with the "IN PARAMETER" ***************

## IN parameter data cannot be manipulated


## IN parameter can only receive a constant value from the main
program
## IN parameter does not return any value to the calling program

declare

b number :=&num;

Procedure p1(a IN number) is


begin -- procedure body
dbms_output.put(a);
end;

BEGIN -- main procedure body


p1(1001);
p1(b);
dbms_output.new_line;
END;

22. ********** Procedure Non-Stored Program Units


with the "OUT PARAMETER" ***************
declare

b number ; -- := num;

86
Procedure p1(a OUT number) is
begin -- procedure body
a:=1000;
end;

BEGIN -- main procedure body


-- p1(1001);
-- dbms_output.put_line(b);
p1(b);
dbms_output.put_line(b);
END;

23. ********** Procedure Non-Stored Program Units


with the "IN OUT PARAMETER" ***************

declare

b number := &num;

Procedure p1(a IN OUT number) is


begin -- procedure body
a:=a+1000;
end;

BEGIN -- main procedure body


p1(b);
dbms_output.put_line(b);
END;

24. ********* Procedures with FUNCTIONS *************

declare

n number := &empno;
b number;

function f1 (a number) return number is


s number;
begin
select sal into s from emp where empno = n;
return (s*0.10);
end;

BEGIN -- Main procedure

b := f1(n);
dbms_output.put_line(b);
End;

25. ************* PROCEDURES AS STORED PROGRAM UNITS *************

create or replace procedure p1 (emp number) is

begin

87
update emp1 set sal = sal*1.10 where empno = emp;
end;

26. *********** Calling a stored procedure within the main procedure


********

declare
e = emp.empno%type :=&empno ;

begin
p1(e);
end;

27. ********** FUNCTIONS AS STORED PROGRAM UNITS ***********

?? insert a record into the table emp1 for job = 'MGR'


and sal >5000 - if sal <5000 return false and don't insert
******* PART 1 ******
create or replace function f1 ( a number) return boolean is
sal1 number;
begin
select sal into sal1 from emp1 where empno = a;
if sal1 < 5000 then
return false;
else
return true;
end if;
end;

28. ******** PART 2 ****************

declare
tf boolean;
begin
tf := f1(7900);

if tf then
dbms_output.put_line('Sal > 5000'|| ' - Record inserted');
-- record inserted/not inserted is a dummy statement
else
dbms_output.put_line('Sal < 5000' || ' - Record not inserted');
end if ;
end;

************************************************************************
D A T A B A S E T R I G G E R S
************************************************************************
29. create table engine
(partno varchar2(20) constraint ch_up check(partno=upper(partno)),
strip_partno varchar(20));
create or replace trigger t1
before insert on engine
for each row
declare
ptno varchar2(20) := :old.partno;
strip_ptno varchar2(20);

88
begin
select upper(ptno) into ptno from dual;
select replace(ptno,' ') into strip_ptno from dual;
select replace(ptno,'.') into strip_ptno from dual;
select replace(ptno,'*') into strip_ptno from dual;
select replace(ptno,'-') into strip_ptno from dual;
exception
when no_data_found then null;
insert into engine values (ptno,strip_ptno);
-- update engine set strip_partno = upper(strip_ptno)
-- where :old.partno=ptno;
end;

24/09/98
** PROCEDURE TO PICK RANDOM SET OF NUMBERS FROM A GIVEN RANGE OF
NUMBERS
create or replace procedure randnum is
init number(3) := 001;
noq number(2) := &noq;
num number(3);
maxi number(3) := &maxi;
cnt number(2) := 01;
x number(2);
Begin
for I in 1 .. maxi loop
x := max - I;
num := x * I;
num := mod(num,maxi);
dbms_output.put_line(num);
cnt := cnt + 1;
if cnt >= noq then
exit;
end if;
end loop;
End;

89

You might also like