Sie sind auf Seite 1von 163

Database normalization

-----------------------
It is useful for several reasons:
It helps to build a structure that is logical and easy to maintain.
Normalized databases are the industry standard.
Retrieving data will be easier.
First Normal Form means that the database doesn't contain any repeating attribut
es.
Violations of Second Normal Form occur when the table contains attributes that d
epend on a portion of the primary key.
Second Normal Form violations can exist only when you have a multi-column primar
y key.
Third Normal Form violations occur when a transitive dependency exists.
All attributes in entities (columns in tables) must be dependent upon the primar
y key or one of the candidate keys and not on other attributes.
==========================================================================
Simple Queries:
--------------
We can not use group function with where clause.
The WHERE clause first filters the rows,then the remaining rows are grouped into
blocks by the GROUP BY clause,
and finally the row groups are filtered by the HAVING clause.
For example, the following query uses
A WHERE clause to filter the rows from those whose salary is less than $50000
A GROUP BY clause to group the remaining rows by the city column
A HAVING clause to filter the row groups to those whose average salary is greate
r than $30000

select sal from emp


where sal > ( select avg(sal) from emp);
this is the way by which we can get the sal > than avg sal.
SQL> SELECT city, AVG(salary)
2 FROM employee
3 WHERE salary < 50000
4 GROUP BY city
5 HAVING AVG(salary) > 30000;

SQL> SELECT DISTINCT city, state


2 FROM employees;
CITY ST
--------------- --
San Francisco CA
Palo Alto CA
Sarasota FL
New York NY
Boston NY
Boston CO

SQL> select job, ename


2 , case
3 when msal <= 2500
4 then 'cheap'
5 else 'expensive'
6 end as class
7 from employees
8 where bdate < date '1964-01-01'
9 order by case job
10 when 'DIRECTOR' then 1
11 when 'MANAGER' then 2
12 else 3
13 end;
SELECT CASE
WHEN SAL IS NULL THEN TO_CHAR('NO SAL')
ELSE TO_CHAR(SAL)
END
FROM EMP
SELECT count(*),
CASE NVL(city,'x')
WHEN 'x' then 'Null'
ELSE city
END CITY
FROM employee
GROUP BY city;
select
case when salary between 6 and 8 then '6-8'
when salary in (9,10) then '9-10'
when exists (select null from avg_sal where avg_sal = salary)
then 'EXISTS'
when to_char(salary) like '2%' then 'Like2'
when salary is null then 'Null'
else 'ELSE Empno: '|| emp_no
end
AS case_test
from emp
/
select *
from registrations
where evaluation not in (1,2,3,NULL);
no rows selected
SELECT empno, ename
FROM employee
WHERE empno NOT IN
(SELECT NVL(empno, 0)
FROM employee);
SELECT * FROM employee WHERE id NOT BETWEEN 1 AND 3;

select *
from dept
where deptno in
(select deptno from emp);
DEPTNO DNAME LOC
------ -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO

select *
from dept
where exists
(select deptno from emp)
/
DEPTNO DNAME LOC
----- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON

select *
from dept
a where exists
(select deptno from emp b
where a.deptno=b.deptno);
DEPTNO DNAME LOC
------ -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
select *
from dept
where deptno not in
(select deptno
from emp)
/
DEPTNO DNAME LOC
------ -------------- -------------
40 OPERATIONS BOSTON
select *
from dept
where not exists
(select deptno
from emp)
/
no rows selected

select *
from dept a
where not exists
(select deptno
from emp b
where a.deptno = b.deptno)
/
DEPTNO DNAME LOC
------ -------------- -------------
40 OPERATIONS BOSTON

==> Exists and not exists give only whether the condition is true or false.

SELECT * FROM employee WHERE first_name NOT LIKE '_o%';


--------------------------------------------------------------------------
1 select level, substr(lpad(' ', level-1)||ename,1,10)
2 from emp
3 start with ename = 'KING'
4 connect by prior empno = mgr
5 and ename !='BLAKE'
6* order by level
SQL> /
LEVEL SUBSTR(LPA
---------- ----------
1 KING
2 CLARK
2 JONES
3 SCOTT
3 MILLER
3 FORD
4 SMITH
4 ADAMS

1 select a.empno, a.ename employee, b.ename manager


2 from emp a, emp b
3* where a.mgr=b.empno(+)
SQL> /
EMPNO EMPLOYEE MANAGER
---------- ---------- ----------
7902 FORD JONES
7788 SCOTT JONES
7900 JAMES BLAKE
7844 TURNER BLAKE
7654 MARTIN BLAKE
7521 WARD BLAKE
7499 ALLEN BLAKE
7934 MILLER CLARK
7876 ADAMS SCOTT
7782 CLARK KING
7698 BLAKE KING
EMPNO EMPLOYEE MANAGER
---------- ---------- ----------
7566 JONES KING
7369 SMITH FORD
7839 KING
1 select level, substr(lpad(' ', level-1)||ename,1,10)
2 from emp
3 start with ename = 'KING'
4 connect by prior empno = mgr
5* order by level
SQL> /
LEVEL SUBSTR(LPA
---------- ----------
1 KING
2 JONES
2 BLAKE
2 CLARK
3 FORD
3 WARD
3 JAMES
3 MILLER
3 ALLEN
3 SCOTT
3 MARTIN
3 TURNER
4 ADAMS
4 SMITH

SELECT empno, ename, sal


FROM emp
WHERE sal >
(SELECT aVG(sal)
FROM emp
order by empno )
/
SQL> /
order by empno )
*
ERROR at line 6:
ORA-00907: missing right parenthesis

There is nothing like > or < null


select ename, empno
from emp
where hiredate > null
/
no rows selected
================================================================================
========
Set Operations
--------------

Operator Description
---------------------------
UNION ALL Returns all the rows retrieved by the queries, including duplica
te rows.
UNION Returns all non-duplicate rows retrieved by the queries.
INTERSECT Returns rows that are retrieved by both queries.
MINUS Returns the remaining rows when the rows retrieved by the second
query are subtracted from the rows retrieved by the first query.

select sal, ename from emp where sal > 25000


union
select sal as salu, ename as name from emp where sal < 20000;
(select sal, ename from emp where sal > 25000
union
select sal as salu, ename as name from emp where sal < 20000)
intersect
select sal as ss, ename as ee from emp where sal = 16400
/
SAL ENAME
------ ----------
16400 TOM

select empno, sal


from emp
where empno in (10, 7369, 7499)
minus
select empno, sal
from emp
where empno in (10, 7369, 7499, 7934)
order by empno;
no rows selected

select empno EmpNum, sal Salary


from emp
where empno in (10, 7369, 7499, 7934)
minus
select empno, sal
from emp
where empno in (10, 7369, 7499)
order by 1
/
EMPNUM SALARY
----- ----------
7934 8000
SELECT empno, ename
FROM emp where empno > 5000
UNION ALL
SELECT null, ename
FROM emp
ORDER BY 1
SELECT count(*), deptno
FROM emp
GROUP BY deptno
UNION
SELECT count(*), null deptnumber
FROM emp
COUNT(*) DEPTNO
--------- ----------
1
3 10
5 20
6 30
15
===================================================================
insert update delete
INSERT INTO myTable (value) VALUES (q'{32 O'Hara Avenue}');
INSERT INTO myTable (value) VALUES ('32 O''Neal Drive');
insert into t values ( 'A''s table' );
UPDATE employee
SET salary = salary * 0.75
RETURNING AVG(salary) INTO :average_salary ;
8 rows updated.
SQL>
SQL> PRINT average_salary;
AVERAGE_SALARY
--------------
3053.81875
===============================================================================
Sequences:
---------
SQL> CREATE TABLE Department (
DepartmentID INT NOT NULL PRIMARY KEY,
Name VARCHAR(50) NOT NULL,
Description VARCHAR(200) NULL);
SQL> CREATE SEQUENCE DepartmentIDSeq;
SQL> CREATE OR REPLACE TRIGGER DepartmentAutonumberTrigger
BEFORE INSERT ON Department
FOR EACH ROW
BEGIN
SELECT DepartmentIDSeq.NEXTVAL
INTO :NEW.DepartmentID FROM DUAL;
END;
/
SQL> INSERT INTO Department (Name, Description)
2 VALUES ('Software', 'Coding');

select *
2 from (select empno, sal
3 from history
4 order by sal desc)
5 where rownum <= 3;
DESC keeps null in the begining and asc keeps null in the end.
--------------------------------------------------------------------------
Multi column
select *
from myTable myTable1, ( select owner, max(last_ddl_time) max_time from my
Table group by owner ) myTable2
where myTable1.owner = myTable2.owner
SELECT * FROM product_order
WHERE (product_name, order_date)
IN (SELECT product_name, MAX(order_date) FROM product_order GROUP BY pro
duct_name);

==========================================================================
insert into Employee(ID, First_Name) values('08','J"a"mes')
08 J"a"mes
INSERT INTO employee (start_date) VALUES (trunc(sysdate));

INSERT INTO product_order_log (


SELECT purc.order_date,
prod.product_name,
prod.product_price,
purc.quantity,
pers.first_name,
pers.last_name
FROM product prod,
person pers,
product_order purc
WHERE prod.product_name = purc.product_name
AND
pers.person_code = purc.salesperson
);
===========================================================================
CREATE TABLE Professor (
ProfessorID INT NOT NULL PRIMARY KEY,
Name VARCHAR(50) NOT NULL);

INSERT INTO Professor (ProfessorID, Name)


SELECT StudentID + 7, Name
FROM Student;
INSERT INTO room(whn,wht,who)
SELECT DATE '2006-07-13','Ballroom','Miss. Scarlet'
FROM dual
WHERE NOT EXISTS (SELECT who FROM room
WHERE whn = DATE '2006-07-13'
AND wht='Ballroom');
INSERT INTO employee (empno)
SELECT empno
FROM job;
update employees
set job = 'SALESREP'
, msal = msal - 500
, comm = 0
, deptno = 30
UPDATE emp e1
SET salary = 1.1 * (SELECT avg(salary) from emp e2 where e1.dept_no = e2.dept_no
);
UPDATE emp
SET sal = (SELECT MAX(sal)
FROM emp e
WHERE e.deptno = emp.deptno);
insert into t values ( 'A''s table' );
INSERT INTO myTable (value) VALUES ('32 O''Neal Drive');
SQL> VARIABLE average_salary NUMBER;
SQL> UPDATE employee
SET salary = salary * 0.75
RETURNING AVG(salary) INTO :average_salary;
==========================================================================
inserting random values
-----------------------
create or replace procedure gen_emp is
v_new_cid emp.id%type;
begin
insert into emp values (cust_seq.nextval*100000+ round(dbms_random.value(10
0000,999999)));
end;
dbms_random.value(min_value, max_value);
=============================================================================
Sequences:
---------
CREATE SEQUENCE test_seq
START WITH 10 INCREMENT BY 5
MINVALUE 10 MAXVALUE 20
CYCLE CACHE 2 ORDER;
ALTER SEQUENCE test_seq MAXVALUE 10;
CREATE SEQUENCE test_seq
START WITH 10 INCREMENT BY -1
MINVALUE 1 MAXVALUE 10
CYCLE CACHE 5;
SELECT test_seq.nextval FROM dual;
SELECT test_seq.currval FROM dual;
changing current value of sequence:
--------------------------------
create sequence rohit_seq
start with 100 increment by 1
minvalue 1 maxvalue 200
cycle order
/
select rohit_seq.nextval from dual;
NEXTVAL
-------
100
SQL> alter sequence rohit_seq increment by -50 nocache;
Sequence altered.
SQL> select rohit_seq.nextval from dual;
NEXTVAL
----------
50
SQL> alter sequence rohit_seq increment by 1 nocache;
SQL> select rohit_seq.nextval from dual;
NEXTVAL
----------
51

create or replace trigger biu_myTable


before insert or update on myTable
for each row
begin
if :new.id is null then
select myTable_seq.nextval into :new.id from dual;
end if;
end;
/
You modify a sequence using the ALTER SEQUENCE statement.
You cannot change the start value of a sequence.
The minimum value cannot be more than the current value of the sequence ( currva
l ).
The maximum value cannot be less than the current value of the sequence ( currva
l ).
INSERT INTO myTable (id, status) VALUES (my_seq.nextval, 'PLACED');
SELECT MAX_VALUE FROM USER_SEQUENCES
WHERE SEQUENCE_NAME 'NAME_OF_SEQUENCE'
CREATE SEQUENCE sequence_name
[START WITH start_num]
[INCREMENT BY increment_num]
[ { MAXVALUE maximum_num | NOMAXVALUE } ]
[ { MINVALUE minimum_num | NOMINVALUE } ]
[ { CYCLE | NOCYCLE } ]
[ { CACHE cache_num | NOCACHE } ]
[ { ORDER | NOORDER } ];
where
The default start_num is 1.
The default increment number is 1.
The absolute value of increment_num must be less than the difference between max
imum_num and minimum_num.
minimum_num must be less than or equal to start_num, and minimum_num must be les
s than maximum_num.
NOMINVALUE specifies the maximum is 1 for an ascending sequence or -10^26 for a
descending sequence.
NOMINVALUE is the default.
maximum_num must be greater than or equal to start_num, and maximum_num must be
greater than minimum_num.
Specify NOMAXVALUE to indicate a maximum value of 10^27 for an ascending sequenc
e or -1 for a descending sequence. This is the default.
NOMAXVALUE is the default.
CYCLE specifies the sequence generates integers even after reaching its maximum
or minimum value.
When an ascending sequence reaches its maximum value, the next value generated i
s the minimum.
When a descending sequence reaches its minimum value, the next value generated i
s the maximum.
NOCYCLE specifies the sequence cannot generate any more integers after reaching
its maximum or minimum value.
NOCYCLE is the default.
CACHE cache_num specifies the number of integers to keep in memory.
The default number of integers to cache is 20.
The minimum number of integers that may be cached is 2.
The maximum integers that may be cached is determined by the formula CEIL(maximu
m_num - minimum_num)/ABS(increment_num).
NOCACHE specifies no integers are to be stored.
ORDER guarantees the integers are generated in the order of the request.
You typically use ORDER when using Real Application Clusters.
NOORDER doesn't guarantee the integers are generated in the order of the request
.
NOORDER is the default.
Sequence information:
select * from user_sequences where sequence_name='TEST_SEQ';
==========================================================================24 Aug
================================================
Tables:
------
CREATE [GLOBAL TEMPORARY] TABLE table_name (
column_name type [CONSTRAINT constraint_def DEFAULT default_exp]
[, column_name type [CONSTRAINT constraint_def DEFAULT default_exp]...]
)
[ON COMMIT {DELETE | PRESERVE} ROWS]
TABLESPACE table_space;
where
GLOBAL TEMPORARY specifies that the table's rows are temporary and such tables a
re known as temporary tables.
The duration of the contents are specified by the ON COMMIT clause.
A temporary table is visible to all sessions, but rows are specific to a session
.
type specifies the type of a column.
constraint_def specifies the definition of a constraint on a column.
default_exp specifies the expression used to assign a default value to a column.
ON COMMIT controls the duration of the rows in a temporary table.
DELETE specifies the rows are deleted at the end of a transaction.
PRESERVE specifies the rows are deleted at the end of a session.
If you omit ON COMMIT for a temporary table, the default is DELETE.
IMP
create table my_hash_table (
2 name varchar2(30),
3 value varchar2(4000) )
4 tablespace users
5 storage (
6 initial 1M
7 next 512K
8 pctincrease 0
9 minextents 2
10 maxextents unlimited )
11 /
create table emp_copy as
select *
from employee;
CREATE TABLE product_order_log2 AS
SELECT purc.order_date,
prod.product_name,
prod.product_price,
purc.quantity,
pers.first_name,
pers.last_name
FROM product prod,
person pers,
product_order purc
WHERE prod.product_name = purc.product_name
AND
pers.person_code = purc.salesperson;
SQL> RENAME product TO log;
create table departments
( dname VARCHAR2(10) constraint D_DNAME_NN
not null
constraint D_DNAME_UN
unique
constraint D_DNAME_CHK
check (dname = upper(dname))
) ;
create table addresses
( empno number(4) references emp(empno) on delete cascade,
addr_type varchar2(10),
street varchar2(20),
city varchar2(20),
state varchar2(2),
zip number,
primary key (empno,addr_type)
)
SQL> desc Employee;
Name Null? Type

You alter a table using the ALTER TABLE statement.


You can use ALTER TABLE to perform tasks such as:
Add, modify, or drop a column
Add or drop a constraint
Enable or disable a constraint
The following list shows some of the aspects of a column you can modify using AL
TER TABLE:
Change the size of a sizable column, such as CHAR or VARCHAR2.
Change the precision of a numeric column.
Change the data type of a column.
Change the default value of a column.
SQL> ALTER TABLE employee
ADD modified_by INTEGER;
SQL> ALTER TABLE employee
ADD initially_created DATE DEFAULT SYSDATE NOT NULL;
SQL> ALTER TABLE employee
MODIFY first_name VARCHAR2(30);
You can only decrease the length of a column if there are no rows in the table o
r all the columns contain null values.
ALTER TABLE employee
MODIFY first_name VARCHAR2(3);
SQL> ALTER TABLE employee
MODIFY salary NUMBER(8,4);
You can only decrease the precision of a numeric column if there are no rows in
the table or the column contains null values.
SQL> ALTER TABLE employee
MODIFY salary NUMBER(2,2);
If the table is empty or the column contains null values, you can change the col
umn to any data type.
Otherwise you can only change the data type of a column to a compatible data typ
e.
SQL> ALTER TABLE employee
MODIFY first_Name CHAR(15);
SQL> alter table employee
add (
gender varchar(10)
);

SQL> alter table employee


add constraint ck_gender
check (gender in ('MALE', 'FEMALE'));
SQL> update employee
set gender = 'MALE'
where mod(id,2) = 0
/
CREATE TABLE myTable (
id INTEGER,
status VARCHAR2(20) DEFAULT 'Order placed' NOT NULL,
last_modified DATE DEFAULT SYSDATE
);
ALTER TABLE myTable
MODIFY last_modified DEFAULT SYSDATE - 1;
ALTER TABLE employee
ADD CONSTRAINT reasonable_stock_date CHECK(
start_date >= to_date('19000917','YYYYMMDD')
);
SQL> ALTER TABLE employee DISABLE CONSTRAINT reasonable_stock_date;
SQL> ALTER TABLE employee
ADD PRIMARY KEY (id);
ALTER TABLE employee
ADD PRIMARY KEY (id,
first_name,
last_name
);
SQL> ALTER TABLE employee
DROP COLUMN last_name;
New added columns are empty
Specifying tablespace for index
IMP
SQL> alter table employee_job add
2 constraint employee_job_PK
3 primary key ( id )
4 using index
5 tablespace USERS pctfree 20
6 storage (initial 10K next 10K pctincrease 0);
Specifying tablespace for index
SQL> alter table employee_job add
2 constraint employee_job_UK
3 unique (
4 worker_id,
5 active_date )
6 using index
7 tablespace USERS pctfree 20
8 storage (initial 10K next 10K pctincrease 0);
SQL> insert /*+ APPEND */ into big_table select * from all_objects where rownu
m < 50;
SQL> create table lookup_heap(
2 key_col primary key,
3 key_val
4 )as
5 select object_name, max( owner||'_'||object_id )from all_objects group by o
bject_name
6 /
SQL> CREATE TABLE My_myTables AS SELECT * FROM myTable
2 WHERE 1=0;
SQL> merge into myTable2 m
2 using myTable d
3 on (m.pid = d.pid)
4 when matched
5 then update set m.sales = m.sales+d.sales
6 , m.status = d.status
7 delete where m.status = 'OBS'
8 when not matched
9 then insert values (d.pid,d.sales,'NEW');

SQL> select * from myTable2;


PID SALES STATUS
---------- ---------- ------
1 12 CURR
2 13 NEW
3 15 CURR
SQL> select * from myTable;
PID SALES STATUS
---------- ---------- ------
2 24 CURR
3 0 OBS
4 42 CURR
SQL> select * from myTable2;
PID SALES STATUS
---------- ---------- ------
1 12 CURR
2 37 CURR
4 42 NEW
SQL> MERGE INTO employee e
2 USING new_employee ne ON (
3 e.id = ne.id
4 )
5 WHEN MATCHED THEN
6 UPDATE
7 SET
8 e.salary = ne.salary;

SQL> comment on table salgrades


2 is 'Salary grades and net bonuses';
SQL> comment on table salgrades
2 is 'Salary grades and net bonuses';
sql> COMMENT ON COLUMN myTable.id IS
2 'id stores the id';
SQL> select comments
2 from user_tab_comments
3 where table_name = 'SALGRADES';
SQL> select comments
2 from user_col_comments
3 where table_name = 'SALGRADES'
4 and column_name = 'COMM';

SQL> alter table registrations


2 add (entered_by number(4) default 7839 not null);
Table altered.
SQL>
SQL> alter table registrations
2 drop column entered_by;
==========================================================================
SQL> create global temporary table sess_event
2 on commit preserve rows
3 as
4 select * from v$waitstat
5 where 1=0
6 /
SQL> insert into sess_event
2 select * from v$waitstat
3 /
SQL> create global temporary table transaction_tab
2 on commit delete rows
3 as select * from employee;
SQL> select tablespace_name, table_name
2 from user_tables
3 where table_name in ('EMPLOYEE')
4 order by 1, 2;
SQL> select segment_name, tablespace_name
2 from user_segments
3 where segment_name = 'EMPLOYEE';
SQL> alter table employee move
2 tablespace users;
SQL> create table foo (
2 a int )
3 tablespace users;

SQL> CREATE TABLE cd_keywords (


2 cd_id INTEGER NOT NULL REFERENCES compact_discs (id),
3 keyword VARCHAR2(60) NOT NULL,
4 PRIMARY KEY (cd_id, keyword)
5 );

SQL> ALTER TABLE account


2 ADD CONSTRAINT fk
3 customerid REFERENCES customer(id);
ON DELETE CASCADE clause with a FOREIGN KEY constraint specifies that when a r
ow
in the parent table is deleted, any matching rows in the child table are also
deleted.
SQL> ALTER TABLE account
2 ADD CONSTRAINT fk
3 customerid REFERENCES customer(id) ON DELETE CASCADE;
ON DELETE SET NULL
Specify that when a row in the parent table is deleted, the foreign key column f
or the row (or rows) in the child table is set to null.
SQL> ALTER TABLE account
2 ADD CONSTRAINT fk
3 customerid REFERENCES customer(id) ON DELETE SET NULL;
SQL> ALTER TABLE product_order
2 ADD CONSTRAINT product_order_fk_product FOREIGN KEY
3 (product_name) REFERENCES product;
SQL> create table employees
2 ( empno NUMBER(4) constraint E_PK
3 primary key
4 constraint E_EMPNO_CHK
5 check (empno > 7000)
6 , mgr NUMBER(4) constraint E_MGR_FK
7 references employees) ;
ORA-02270: no matching unique or primary key for this column-list
SQL> ALTER TABLE employee_evaluation ADD
2 CONSTRAINT employee_evaluation_fk1
3 FOREIGN KEY (id)
4 REFERENCES employee (id);
SQL> alter table it add constraint it_fk foreign key (c2) references emp disab
le;
SQL> CREATE TABLE Enrolls_in
2 (course_id VARCHAR2(10) NOT NULL,
3 stud_id VARCHAR2(10) NOT NULL,
4 PRIMARY KEY (course_id, stud_id),
5 FOREIGN KEY (course_id) REFERENCES Course (course_id)
6 ON DELETE CASCADE,
7 FOREIGN KEY (stud_id) REFERENCES emp (stud_id)
8 ON DELETE CASCADE);

alter table a disable constraint a_fk1;

SQL> select status


2 from user_constraints
3 where upper(constraint_name)='A_FK1';
STATUS
--------
DISABLED
--------------------------------------------------------------------------
SQL> CREATE TABLE department (
2 dept_id INTEGER,
3 dept_name VARCHAR2(32));

SQL>
SQL> ALTER TABLE department
2 ADD CONSTRAINT pk_dept PRIMARY KEY (dept_id);
SQL> create table myTable(
2 id number(6) not null,
3 salary number(8,2),
4 hire_date date default sysdate,
5 termination_date date,
6 termination_desc varchar2(4000),
7 constraint emphistory_pk primary key (id, hire_date)
8 );

SQL> create table keywords


2 ( word varchar2(50),
3 position int,
4 doc_id int,
5 primary key(word,position,doc_id)
6 );
SQL> CREATE TABLE Office
2 (office_id VARCHAR2(10) NOT NULL,
3 building_name VARCHAR2(20),
4 PRIMARY KEY (office_id));
Table created.
SQL>
SQL> CREATE TABLE Programmer
2 (lect_id VARCHAR2(10) NOT NULL,
3 lect_name VARCHAR2(20),
4 office_id VARCHAR2(10),
5 PRIMARY KEY (lect_id),
6 FOREIGN KEY (office_id) REFERENCES Office (office_id)
7 ON DELETE CASCADE);
Table created.
ALTER TABLE employee
2 ADD CONSTRAINT status_ck
3 CHECK (city IN ('Vancouver', 'Toronto', 'New York'));
SQL> create table employees
2 ( empno NUMBER(4) constraint E_PK
3 primary key
4 constraint E_EMPNO_CHK
5 check (empno > 7000)) ;
==========================================================================
SQL> CREATE TABLE myTable (
2 id INTEGER,
3 status VARCHAR2(20) DEFAULT 'Order placed' NOT NULL,
4 last_modified DATE DEFAULT SYSDATE
5 );
insert into mytable(id) values(2);
SQL> CREATE TABLE myTable (
2 id INTEGER,
3 status VARCHAR2(20) DEFAULT 'Order placed' NOT NULL,
4 last_modified DATE DEFAULT SYSDATE
5 );
Update a column and set it back to the default using the DEFAULT keyword in an U
PDATE statement
SQL> UPDATE myTable
SET status = DEFAULT;
SQL> alter table registrations
2 add (entered_by number(4) default 7839 not null);

SQL> create table employees


2 ( empno NUMBER(4) constraint E_PK
3 primary key
4 constraint E_EMPNO_CHK
5 check (empno > 7000)) ;
Table created.
ALTER TABLE employee
2 ADD CONSTRAINT status_ck
3 CHECK (city IN ('Vancouver', 'Toronto', 'New York'));
SQL> create table employees
2 ( empno NUMBER(4) constraint E_PK
3 primary key
4 constraint E_EMPNO_CHK
5 check (empno > 7000)
6 , job VARCHAR2(8)
7 , comm NUMBER(6,2)
8 , constraint E_SALES_CHK check
9 (decode(job,'SALESREP',0,1)
10 + nvl2(comm, 1,0) = 1)

SQL> ALTER TABLE myTable


2 ADD CONSTRAINT uq UNIQUE (id);
SQL> ALTER TABLE employee
2 ADD CONSTRAINT emp_unique UNIQUE (
3 first_name,
4 last_name,
5 start_date
6 );
SQL> alter table employee
2 modify (
3 ssn number(9) not null
4 );
SQL> ALTER TABLE product MODIFY data_load_date NOT NULL;
SQL> alter table emp modify deptno not null;
Table altered.
SQL>
SQL>
SQL> alter table emp modify deptno null;
Table altered.
SQL> ALTER TABLE myTable
2 ADD CONSTRAINT uq UNIQUE (id) DISABLE;
Table altered.
SQL>
SQL>
SQL> ALTER TABLE myTable
2 DISABLE CONSTRAINT uq CASCADE;
SQL> ALTER TABLE myTable
2 DISABLE CONSTRAINT uq;
Table altered.
SQL>
SQL>
SQL> ALTER TABLE myTable
2 ENABLE CONSTRAINT uq;

You can also choose to apply a constraint to new data only by specifying ENABLE
NOVALIDATE.
The default is ENABLE VALIDATE.
SQL> ALTER TABLE myTable
2 ADD CONSTRAINT uq UNIQUE (id) DISABLE;
Table altered.
SQL>
SQL>
SQL> ALTER TABLE myTable
2 DISABLE CONSTRAINT uq;
Table altered.

SQL> ALTER TABLE myTable


2 ENABLE NOVALIDATE CONSTRAINT uq;
Table altered.
SQL> alter table emp
2 modify ename not null;
Table altered.
alter table emp
disable constraint SYS_C009869;
SQL> alter table emp
2 modify ename not null;
modify ename not null
*
ERROR at line 2:
ORA-01442: column to be modified to NOT NULL is already NOT NULL

A deferred constraint is one that is enforced when a transaction is committed.


A deferrable constraint is specified by using DEFERRABLE clause.
Once you've added a constraint, you cannot change it to DEFERRABLE. You must dro
p and recreate the constraint.
When you add a DEFERRABLE constraint, you can mark it as INITIALLY IMMEDIATE or
INITIALLY DEFERRED.
INITIALLY IMMEDIATE means that the constraint is checked whenever you add, updat
e, or delete rows from a table.
INITIALLY DEFERRED means that the constraint is only checked when a transaction
is committed.
SQL> ALTER TABLE myTable
2 ADD CONSTRAINT uq UNIQUE (id)
3 DEFERRABLE INITIALLY DEFERRED;

SQL> RENAME myTable TO myNewTable;


======================================8sept2010=================================
=======================
Join
Joins can be used to connect any number of tables.
The number of joins you will need in your WHERE clause is total_number_of_tables
- 1
There are two types of join conditions, which are based on the operator.
Equijoins - You use the equality operator (=) in the join.
Non-equijoins - You use an operator other than equals in the join, such as <, >,
BETWEEN, and so on.
Three different types of joins:
Inner joins - Return a row only when the columns in the join contain values that
satisfy the join condition.
This means that if a row has a null value in one of the columns in the join cond
ition, that row isn't returned.
Outer joins - Can return a row even when one of the columns in the join conditio
n contains a null value.
Self joins - Return rows joined on the same table.
A non-equijoin uses an operator other than the equality operator in the join.
Examples of non-equality operators are:
not-equal (<>),
less than (<),
greater than (>),
less than or equal to (<=),
greater than or equal to (>=),
LIKE,
IN, and
BETWEEN
NON_EQUIJOIN
SELECT e.first_name, e.last_name, e.title, e.salary, sg.salary_grade_id
FROM employees e, salary_grades sg
WHERE e.salary BETWEEN sg.low_salary AND sg.high_salary
ORDER BY salary_grade_id;
OUTER JOIN
SELECT p.name, pt.name
FROM products p, product_types pt
WHERE p.product_type_id = pt.product_type_id (+)
ORDER BY p.name;
You can place the outer join operator on either side of the join
operator, but you always place it on the opposite side of the column
that contains no value for the corresponding column in other table.
eg. products table contain no value ofr null.
IT IS ON THE OPPOSITE SIDE OF THE TABLE WHICH IS DEFICENT.
NAME NAME
------------------------------ ----------
2412: The Return Video
Chemistry Book
Classical Music CD
Creative Yell CD
From Another Planet DVD
Modern Science Book
My Front Line
Pop 3 CD
Space Force 9 DVD
LEFT OUETER JOIN
WHERE table1.column1 = table2.column2 (+);
RIGHT OUTER JOIN
WHERE table1.column1 (+) = table2.column2;
SELECT p.name, pt.name
FROM products p, product_types pt
WHERE p.product_type_id (+) = pt.product_type_id
ORDER BY p.name;
NAME NAME
------------------------------ ----------
2412: The Return Video
Chemistry Book
Z Files Video
Magazine
LIMITATIONS:
SQL> SELECT p.name, pt.name
2 FROM products p, product_types pt
3 WHERE p.product_type_id (+) = pt.product_type_id (+);
WHERE p.product_type_id (+) = pt.product_type_id (+)
*
ERROR at line 3:
ORA-01468: a predicate may reference only one outer-joined table
You cannot use an outer join condition with the IN operator:
SQL> SELECT p.name, pt.name
2 FROM products p, product_types pt
3 WHERE p.product_type_id (+) IN (1, 2, 3, 4);
WHERE p.product_type_id (+) IN (1, 2, 3, 4)
*
ERROR at line 3:
ORA-01719: outer join operator (+) not allowed in operand of OR or IN

SQL> SELECT p.name, pt.name


2 FROM products p, product_types pt
3 WHERE p.product_type_id (+) = pt.product_type_id
4 OR p.product_type_id = 1;
WHERE p.product_type_id (+) = pt.product_type_id
*
ERROR at line 3:
ORA-01719: outer join operator (+) not allowed in operand of OR or IN
Self Joins:
select a.ename ||' works for '||nvl(b.ename, 'GOD')
from emp a, emp b
where a.mgr=b.empno(+)
/
A.ENAME||'WORKSFOR'||NVL(B.ENAM
-------------------------------
FORD works for JONES
SCOTT works for JONES
JAMES works for BLAKE
TURNER works for BLAKE
MARTIN works for BLAKE
WARD works for BLAKE
ALLEN works for BLAKE
MILLER works for CLARK
ADAMS works for SCOTT
CLARK works for KING
BLAKE works for KING
A.ENAME||'WORKSFOR'||NVL(B.ENAM
-------------------------------
JONES works for KING
TOM works for FORD
KING works for GOD
14 rows selected.
Performing Full Outer Joins:
SELECT p.name, pt.name
FROM products p FULL OUTER JOIN product_types pt
USING (product_type_id)
ORDER BY p.name;
=========================================================================
VIEWS:
A view is a predefined query on one or more tables.
Retrieving information from a view is done in the same manner as retrieving from
a table.
With some views you can also perform DML operations (delete, insert, update) on
the base tables.
Views don't store data, they only access rows in the base tables.
user_tables, user_sequences, and user_indexes are all views.
View Only allows a user to retrieve data.
view can hide the underlying base tables.
By writing complex queries as a view, we can hide complexity from an end user.
View only allows a user to access certain rows in the base tables.
CREATE [OR REPLACE] VIEW [{FORCE | NOFORCE}] VIEW view_name
[(alias_name[, alias_name...])] AS subquery
[WITH {CHECK OPTION | READ ONLY} CONSTRAINT constraint_name];
where
OR REPLACE specifies the view is to replace an existing view if present.
FORCE specifies the view is to be created even if the base tables don't exist.
NOFORCE specifies the view is not to be created if the base tables don't exist;
NOFORCE is the default.
alias_name specifies the name of an alias for an expression in the subquery.
There must be the same number of aliases as there are expressions in the subquer
y.
subquery specifies the subquery that retrieves from the base tables.
If you've supplied aliases, you can use those aliases in the list after the SELE
CT clause.
WITH CHECK OPTION specifies that only the rows that would be retrieved by the su
bquery can be inserted, updated, or deleted.
By default, rows are not checked that they are retrievable by the subquery befor
e they are inserted, updated, or deleted.
constraint_name specifies the name of the WITH CHECK OPTION or READ ONLY constra
int.
WITH READ ONLY specifies that rows may only read from the base tables.
There are two basic types of views:
Simple views, which contain a subquery that retrieves from one base table
Complex views, which contain a subquery that:
Retrieves from multiple base tables
Groups rows using a GROUP BY or DISTINCT clause
Contains a function call

SQL> CREATE VIEW employee_view AS


2 SELECT id, first_name, last_name
3 FROM employee;
SQL> CREATE VIEW myView AS
2 SELECT *
3 FROM employee
4 WHERE id < 5
5 WITH CHECK OPTION CONSTRAINT myView;
INSERT INTO myView (id) VALUES (7);
INSERT INTO myView (id) VALUES (7)
*
ERROR at line 1:
ORA-01402: view WITH CHECK OPTION where-clause violation

SQL> CREATE VIEW myView AS


2 SELECT *
3 FROM employee
4 WITH READ ONLY CONSTRAINT my_view_read_only;
View created.
SQL>
SQL> INSERT INTO myView (id) VALUES (1);
INSERT INTO myView (id) VALUES (1)
*
ERROR at line 1:
ORA-01733: virtual column not allowed here
Create view based on user-defined function:
SQL> create or replace function f_emp_dsp (i_empNo VARCHAR)
2 return VARCHAR2 is v_out VARCHAR2 (256);
3 begin
4 DBMS_OUTPUT.put_line('Inside of F_EMP_DSP');
5 select initcap(first_name)||': '||initcap(last_name)
6 into v_out from employee where id = i_empNo;
7 return v_out;
8 end f_emp_dsp;
9 /
Function created.
SQL>
SQL> create or replace view v_emp as
2 select f_emp_dsp(id) emp_dsp
3 from employee;
View created.
SQL> create or replace view crs_course_schedule as
2 select o.course as course_code, c.description, o.begindate
3 from course_schedule o
4 join
5 courses c
6 on (o.course = c.code);
Because employee_view didn't use WITH CHECK OPTION, you can insert, update, and
delete rows that aren't retrievable by the subquery.
SQL> CREATE VIEW employee_view AS
2 SELECT id, first_name, last_name
3 FROM employee
4 where id< 5;
SQL> INSERT INTO employee_view (id, first_name, last_name) VALUES (
2 13, 'New', 'Westerrn'
3 );
1 row created.
SQL> create or replace view dept20_v as
2 select * from employees where deptno=20;
SQL> insert into dept20_v
2 values ( 9999,'BOS','D', null, null
3 , date '1939-01-01'
4 , '10', null, 30);
1 row created.
SQL>
SQL> create or replace view dept20_v as
2 select * from employees where deptno=20 with check option;
View created.
SQL>
SQL>
SQL>
SQL> insert into dept20_v
2 values ( 9999,'BOS','D', null, null
3 , date '1939-01-01'
4 , '10', null, 30);
insert into dept20_v
*
ERROR at line 1:
ORA-01402: view WITH CHECK OPTION where-clause violation

SQL> CREATE VIEW myView AS


2 SELECT *
3 FROM employee
4 WITH READ ONLY CONSTRAINT my_view_read_only;
View created.
ALTER VIEW:
SQL> ALTER VIEW myview
2 DROP CONSTRAINT my_view_read_only;
View altered.
SQL>
SQL>
SQL> drop view myView;
View dropped.
Top 'N' analysis
----------------
SQL> CREATE OR REPLACE VIEW overstocked_items AS
2 SELECT product_name, quantity_on_hand
3 FROM product
4 WHERE rownum <= 3
5 ORDER BY quantity_on_hand;

SQL> update dept20_v


2 set deptno = 30
3 where job = 'TRAINER';

Creating FORCE VIEWS


A view can be created even if the defining query of the view cannot be executed,
as long as the CREATE VIEW command has no syntax errors. We call such a view a
view with errors. For example, if a view refers to a non-existent table or an in
valid column of an existing table, or if the owner of the view does not have the
required privileges, then the view can still be created and entered into the da
ta dictionary.
You can only create a view with errors by using the FORCE option of the CREATE V
IEW command:
CREATE FORCE VIEW AS ...;
When a view is created with errors, Oracle returns a message and leaves the stat
us of the view as INVALID. If conditions later change so that the query of an in
valid view can be executed, then the view can be recompiled and become valid. Or
acle dynamically compiles the invalid view if you attempt to use it.
SQL> create view invalid_view as
2 select * from table_that_does_not_exist;
select * from table_that_does_not_exist
*
ERROR at line 2:
ORA-00942: table or view does not exist

SQL>
SQL> create force view invalid_view as
2 select * from table_that_does_not_exist;
Warning: View created with compilation errors.
SQL>
SQL> drop view invalid_view;
Materialized Views in Oracle:
----------------------------
A materialized view is a database object that contains the results of a query.
They are local copies of data located remotely, or are used to create summary ta
bles based on aggregations of a table's data.
Materialized views, which store data based on remote tables are also, know as sn
apshots.
A materialized view can query tables, views, and other materialized views.
Collectively these are called master tables (a replication term) or detail table
s (a data warehouse term).
For replication purposes, materialized views allow you to maintain copies of rem
ote data on your local node. These copies are read-only.
If you want to update the local copies, you have to use the Advanced Replication
feature.
You can select data from a materialized view as you would from a table or view
For data warehousing purposes, the materialized views commonly created are aggre
gate views, single-table aggregate views, and join views.
In this article, we shall see how to create a Materialized View and discuss Refr
esh Option of the view.
In replication environments, the materialized views commonly created are primary
key, rowid, and subquery materialized views.
Primary Key Materialized Views
The following statement creates the primary-key materialized view on the table e
mp located on a remote database.
CREATE MATERIALIZED VIEW mv_emp_pk
REFRESH FAST
START WITH SYSDATE
NEXT SYSDATE + 2
WITH PRIMARY KEY
AS SELECT * FROM emp@remote_db;
[refresh [fast|complete|force]
[on demand | commit]
[start with date] [next date]
[with {primary key|rowid}]]
Refresh Method - COMPLETE Clause
The complete refresh re-creates the entire materialized view. If you request a c
omplete refresh, Oracle performs a complete refresh even if a fast refresh is po
ssible.
Refresh Method - FORCE Clause
When you specify a FORCE clause, Oracle will perform a fast refresh if one is po
ssible or a complete refresh otherwise.
If you do not specify a refresh method (FAST, COMPLETE, or FORCE), FORCE is the
default.
PRIMARY KEY and ROWID Clause
WITH PRIMARY KEY is used to create a primary key materialized view i.e. the mate
rialized view is based on the primary key of the master table instead of
ROWID (for ROWID clause). PRIMARY KEY is the default option.
To use the PRIMARY KEY clause you should have defined PRIMARY KEY on the master
table or else you should use ROWID based materialized views.
Rowid materialized views should have a single master table and cannot contain an
y of the following:
Distinct or aggregate functions
GROUP BY Subqueries , Joins & Set operations
Note: When you create a materialized view using the FAST option you will need to
create a view log on the master tables(s) as shown below:
SQL> CREATE MATERIALIZED VIEW LOG ON emp;

Rowid Materialized Views


The following statement creates the rowid materialized view on table emp located
on a remote database:
SQL> CREATE MATERIALIZED VIEW mv_emp_rowid
REFRESH WITH ROWID
AS SELECT * FROM emp@remote_db;
Materialized view log created.
Subquery Materialized Views
The following statement creates a subquery materialized view based on the emp an
d dept tables located on the remote database:
SQL> CREATE MATERIALIZED VIEW mv_empdept
AS SELECT * FROM emp@remote_db e
WHERE EXISTS
(SELECT * FROM dept@remote_db d
WHERE e.dept_no = d.dept_no)
===============================================================
INDEX
-----
An index for a database table is similar in concept to a book index.
When a row is added to the table, additional time is required to update the inde
x for the new row.
Oracle database automatically creates an index for the primary key of a table an
d for columns included in a unique constraint.
You create an index using CREATE INDEX, which has the following simplified synta
x:
CREATE [UNIQUE] INDEX index_name ON
table_name(column_name[, column_name...])
TABLESPACE table_space;
where
UNIQUE specifies the values in the indexed columns must be unique.
You can create an index on multiple columns; such an index is known as a composi
te index.
If you don't provide a tablespace, the index is stored in the user's default tab
lespace.
For performance reasons you should typically store indexes in a different tables
pace from tables.
SQL> CREATE UNIQUE INDEX employee_id_idx ON employee(id);
SQL> drop index employee_id_idx;
SQL> CREATE INDEX person_name_index ON person(last_name, first_name);
SQL> CREATE INDEX employee_first_last_name_idx ON
2 employee (first_name, last_name);
Creating a Function-Based Index:
You must set the initialization parameter QUERY_REWRITE_ENABLED to true in order
to take advantage of function-based indexes.
SQL> ALTER SYSTEM SET QUERY_REWRITE_ENABLED=TRUE;
SQL> CREATE INDEX employee_last_name_func_idx
2 ON employee(UPPER(last_name));
Index created.
SQL> SELECT first_name, last_name
2 FROM employee
3 WHERE last_name = UPPER('PRICE');
no rows selected
SQL>
SQL>drop index employee_last_name_func_idx;
SQL> ALTER TABLE employee ADD employee_dup_id VARCHAR2(7);
Table altered.
SQL>
SQL> -- Updates the new column with the value of the employee_id column
SQL> UPDATE employee
2 SET employee_dup_id = employee_id;
10 rows updated.
SQL> -- Creates an index on the new column
SQL> CREATE UNIQUE INDEX employee_test_idx2
2 ON employee(employee_dup_id);
SQL> CREATE TABLE emp2
2 (emp_id NUMBER PRIMARY KEY
3 USING INDEX
4 (CREATE INDEX pk_idx ON emp2(emp_id) TABLESPACE users)
5 ,lastname VARCHAR2(20) CONSTRAINT lastname_create_nn NOT NULL
6 ,firstname VARCHAR2(15) CONSTRAINT firstname_create_nn NOT NULL
7 ,phone VARCHAR2(12)
8 ,company_name VARCHAR2(50)
9 ,CONSTRAINT unique_emp_phone UNIQUE (phone)
10 USING INDEX
11 (CREATE INDEX phone_idx ON emp2 (phone) TABLESPACE users)
12 );
Table created.
Create unique index and check it in user_ind_columns and user_cons_columns:
SELECT *
FROM USER_IND_COLUMNS
WHERE TABLE_NAME= 'EMP';
GIVES ALL INDEXES INCLUDING PRIMARY KEY AND UNIQUE KEY.
SELECT *
FROM USER_CONS_COLUMNS
WHERE TABLE_NAME = 'EMP';
GIVE ALL CONSTRAINTS.
SQL> create unique index oau_reg on registrations
( case course when 'OAU' then attendee else null end
, case course when 'OAU' then course else null end );
SQL> create index CITY_ST_ZIP_NDX
2 on emp(EmpNo, DeptNo, Soc_Sec_Num)
3 tablespace INDEXES;
SQL> drop index employee_last_name_idx;
SQL> CREATE UNIQUE INDEX unique_upper
2 ON employee (
3 UPPER(first_name),
4 UPPER(last_name),
5 start_date
6 );

SQL> create index year_sal_idx


2 on employees (12*msal + coalesce(comm,0));

Cluster Based Index:


create cluster emp_dept_cluster ( deptno number(2) ) size 1024
2 /
create index emp_dept_cluster_idx on cluster emp_dept_cluster
2 /

create table dept


2 ( deptno number(2) primary key,
3 dname varchar2(14),
4 loc varchar2(13)
5 )
6 cluster emp_dept_cluster(deptno)
7 /

create table emp


2 ( empno number primary key,
3 ename varchar2(10),
4 job varchar2(9),
5 mgr number,
6 hiredate date,
7 sal number,
8 comm number,
9 deptno number(2)
10 )
11 cluster emp_dept_cluster(deptno)
12 /
Altering Oracle Indexes:
If we create indexes, there may be times that we will want to change some attrib
ute of that index, such as where it is stored.
Also, sometimes an index needs to be rebuilt to help with performance. For cases
like these, the alter index command is what we want.
Let’s look at an example of the use of the alter index command:
ALTER INDEX ix_emp_01 REBUILD TABLESPACE new_index;
In this example we use the alter index command to rebuild an index. The rebuild
keyword is what tells Oracle to rebuild the index.
When we use the tablespace keyword, followed by a tablespace name, we are tellin
g Oracle which tablespace to recreate the rebuilt index in.
By default Oracle will create the rebuilt index in the same tablespace.
The alter index command allows you to rename a tablespace using the rename to ke
yword as seen in this example:
ALTER INDEX ix_emp_01 RENAME TO ix_emp_01_old;
The syntax for renaming an index is:
ALTER INDEX index_name
RENAME TO new_index_name;
ALTER INDEX supplier_idx
RENAME TO supplier_index_name;
In scientific applications (clinical, laboratory) where large datasets are added
and removed, the need to rebuild indexes is "common".
Conversely, in system that never update or delete rows, index rebuilding rarely
improves performance.
In systems that do batch DML jobs, index rebuilding "often" improves SQL perform
ance.
Oracle provides a TIMING command for measuring the running time of SQL commands.
To activate this feature, type
set timing on;
set timing off;
Reverse Index:
------------
Reverse Key Indexes are designed to resolve a specific issue, that being index b
lock contention. Many indexes in busy database environments
with lots of concurrent inserts (and in some scenarios updates and deletes as we
ll) can suffer from index block contention
(as highlighted by high levels of “buffer busy waits” and “read by other session” wait e
vents for the index segments).
Monotonically increasing indexes, such as Primary Keys generated by a sequence,
are especially prone to contention as
all inserts need to access the maximum “right-most” leaf block.
A solution is make the index a Reverse Key Index.
CREATE INDEX bowie_reverse_idx ON bowie(id) REVERSE;
A Reverse Key Index simply takes the index column values and reverses them befor
e inserting into the index. “Conceptually”, say the next generated ID is 123456,
Oracle will reverse it to 654321 before inserting into the index. It will then t
ake the next generated ID 123457 and reverse it to 754321 and
insert it into the index and so on. By doing this, inserts are spread across the
whole index structure,
ensuring the right most block is no longer the only index leaf block being hamme
red. Index contention is dramatically reduced or eliminated entirely.
Reverse Key Indexes address a specific problem but may in turn introduce a numbe
r of problems themselves.
One problem is the simple fact index entries are no longer sorted in their natur
al order. Value 123456 is no longer adjacent to value 123457 in the
index structure, they’re likely to be found in completely different leaf blocks. T
herefore a range predicate (such as BETWEEN 123450 and 123460) can no
longer be found by a single index probe,Oracle would be forced to search for eac
h specific index value separately as each value in the range is likely
to be in differing leaf blocks.
To change an existing index as a reverse key index you can use the alter index s
tatement.
alter index indexname rebuild reverse;
Bitmap Index vs. B-tree Index: Which and When?
Conventional wisdom holds that bitmap indexes are most appropriate for columns h
aving low distinct values—such as GENDER,
MARITAL_STATUS, and RELATION. This assumption is not completely accurate, howeve
r.
In reality, a bitmap index is always advisable for systems in which data is not
frequently updated by many concurrent systems.
There are several disadvantages to using a bitmap index on a unique column—one bei
ng the need for sufficient space (and Oracle does not recommend it).
However, the size of the bitmap index depends on the cardinality of the column o
n which it is created as well as the data distribution.
Consequently, a bitmap index on the GENDER column will be smaller than a B-tree
index on the same column.
In contrast, a bitmap index on EMPNO (a candidate for primary key) will be much
larger than a B-tree index on this column.
SQL> create bitmap index normal_empno_bmx on test_normal(empno);
Identifier Gender Bitmaps
F M
1 Female 1 0
2 Male 0 1
3 Male 0 1
4 Unsp 0 0
5 Female 1 0
B-TREE INDEX
Index Items
The fundamental unit of an index is the index item. An index item contains a key
value that represents the value of the indexed column for a particular row.
An index item also contains rowid information that the database server uses to l
ocate the row in a data page.
Logical Storage of Indexes
This section presents an overview of how the database server creates and fills a
n index.
Creation of Root and Leaf Nodes
When you create an index for an empty table, the database server allocates a sin
gle index page. This page represents the root node and
remains empty until you insert data in the table.
At first, the root node functions in the same way as a leaf node. For each row t
hat you insert into the table,
the database server creates and inserts an index item in the root node. Figure i
llustrates how a root node appears before it fills.
ROOT NODE
KEY VALUE ROWID
When the root node becomes full of index items, the database server splits the r
oot node by performing the following steps:
Creates two leaf nodes
Moves approximately half of the root-node entries to each of the newly created l
eaf nodes
Puts pointers to leaf nodes in the root node
As you add new rows to a table, the database server adds index items to the leaf
nodes. When a leaf node fills, the database server creates a new
leaf node, moves part of the contents of the full index node to the new node, an
d adds a node pointer to the new leaf node in the root node.
The first item in the left branch node contains the same key value as the larges
t item in the leftmost leaf node and a node pointer to it.
The second item contains the largest item in the next leaf node and a node point
er to it. The third item in the branch node contains only a pointer to the next
higher leaf node.
===============================================================
Single-Row Functions
Single-row functions are used to manipulate data items. They accept one or more
arguments and
return one value for each row returned by the query. An argument can be one of t
he following:
• User-supplied constant
• Variable value
• Column name
• Expression
Features of single-row functions include:
• Acting on each row returned in the query
• Returning one result per row
• Possibly returning a data value of a different type than that referenced
• Possibly expecting one or more arguments
• Can be used in SELECT, WHERE, and ORDER BY clauses; can be nested
• Character functions accept character input and can return both character and num
ber values
• Number functions Accept numeric input and return numeric values
• Date functions Operate on values of the DATE data type (All date functions retur
n a value of
DATE data type except the MONTHS_BETWEEN function, which returns a numbe
r.)
• Conversion functions Convert a value from one data type to another
• General functions:
– NVL
– NVL2
– NULLIF
– COALSECE
– CASE
– DECODE
Function Purpose
SUBSTR(column|expression,m [,n])
Returns specified characters from character value starting at
character position m, n characters long (If m is negative, the
count starts from the end of the character value. If n is
omitted, all characters to the end of the string are returned.)
INSTR(column|expression,
’string’, [,m], [n] )
Returns the numeric position of a named string. Optionally,
you can provide a position m to start searching, and the
occurrence n of the string. m and n default to 1, meaning
start the search a
TRIM(leading|trailing|both
, trim_character FROM
trim_source)
Enables you to trim heading or trailing characters (or both)
from a character string. If trim_character or
trim_source is a character literal, you must enclose it in
single quotes.
Q
CONCAT(’Hello’, ’World’)
SUBSTR(’HelloWorld’,1,5)
LENGTH(’HelloWorld’)
INSTR(’HelloWorld’, ’W’)
LPAD(salary,10,’*’)
RPAD(salary, 10, ’*’)
TRIM(’H’ FROM ’HelloWorld’)
A
HelloWorld
Hello
10
6
*****24000
24000*****
elloWorld
SELECT employee_id, CONCAT(first_name, last_name) NAME,
LENGTH (last_name), INSTR(last_name, ’a’) "Contains ’a’?"
FROM employees
WHERE SUBSTR(last_name, -1, 1) = ’n’;
.ROUND: Rounds value to specified decimal
ROUND(45.926, 2) 45.93
• TRUNC: Truncates value to specified decimal
TRUNC(45.926, 2) 45.92
• MOD: Returns remainder of division
MOD(1600, 300) 100
SELECT ROUND(45.923,2), ROUND(45.923,0),
ROUND(45.923,-1)
FROM DUAL;
45.92 46 50
SELECT TRUNC(45.923,2), TRUNC(45.923),
TRUNC(45.923,-2)
FROM DUAL;
45.92 45 0
Date and Time:
-------------
Operation Result Description
date + number Date Adds a number of days to a date
date - number Date Subtracts a number of days from a date
date - date Number of days Subtracts one date from another
date + number/24 Date Adds a number of hours to a date
SELECT last_name, (SYSDATE-hire_date)/7 AS WEEKS
FROM employees
WHERE department_id = 90;

MONTHS_BETWEEN
ADD_MONTHS
NEXT_DAY
LAST_DAY
ROUND
TRUNC
Number of months
between two dates
Add calendar months to
date
Next day of the date
specified
Last day of the month
Round date
Truncate date

. MONTHS_BETWEEN (’01-SEP-95’,’11-JAN-94’)
• ADD_MONTHS (’11-JAN-94’,6)
• NEXT_DAY (’01-SEP-95’,’FRIDAY’)
• LAST_DAY(’01-FEB-95’)
19.6774194
’11-JUL-94’
’08-SEP-95’
’28-FEB-95’

• ROUND(SYSDATE,’MONTH’) 01-AUG-95
• ROUND(SYSDATE ,’YEAR’) 01-JAN-96
• TRUNC(SYSDATE ,’MONTH’) 01-JUL-95
• TRUNC(SYSDATE ,’YEAR’) 01-JAN-95

Function Purpose
TO_CHAR(number|date,[ fmt],
[nlsparams])
Date Conversion: The nlsparams parameter
specifies the language in which month and day names
and abbreviations are returned. If this parameter is
omitted, this function uses the default date languages
for the session.
TO_NUMBER(char,[fmt],
[nlsparams])
Converts a character string containing digits to a
number in the format specified by the optional
format model fmt.
The nlsparams parameter has the same purpose
in this function as in the TO_CHAR function for
number conversion.
TO_DATE(char,[fmt],[nlsparams]) Converts a character string representing a date
to a
date value according to the fmt specified. If fmt is omitted, the format is DD-M
ON-YY.
The nlsparams parameter has the same purpose in this function as in the TO_CHAR
function for date conversion.
Using the TO_CHAR Function with Dates
The format model:
• Must be enclosed in single quotation marks and is case sensitive
• Can include any valid date format element
• Has an fm element to remove padded blanks or suppress leading zeros
• Is separated from the date value by a comma
Elements of the Date Format Model:
YYYY
YEAR
MM
MONTH
DY
DAY
DD
Full year in numbers
Year spelled out
Two-digit value for month
Three-letter abbreviation of the
day of the week
Full name of the day of the week
Full name of the month
MON
Three-letter abbreviation of the
month
Numeric day of the month
Current Year
1995
1995
2001
2001
Specified Date
27-OCT-95
27-OCT-17
27-OCT-17
27-OCT-95
RR Format
1995
2017
2017
1995
YY Format
1995
1917
2017
2095
General Functions
These functions work with any data type and pertain
to using nulls.
• NVL (expr1, expr2)
• NVL2 (expr1, expr2, expr3)
• NULLIF (expr1, expr2)
• COALESCE (expr1, expr2, ..., exprn)
NVL Converts a null value to an actual value
NVL2 If expr1 is not null, NVL2 returns expr2. If expr1 is null, NVL2
returns expr3. The argument expr1 can have any data type.
NULLIF Compares two expressions and returns null if they are equal, or the first
expression if they are not equal
COALESCE Returns the first non-null expression in the expression list
DECODE:
SELECT last_name, job_id, salary, DECODE(job_id, ’IT_PROG’, 1.10*salary, ’ST_CLERK’, 1.1
5*salary, ’SA_REP’, 1.20*salary, salary)
REVISED_SALARY
FROM employees;
The Analytical Functions in Oracle (Analytical Functions I)
The analytical functions fall into categories:
------------------------------------------------
ranking,
aggregate,
row comparison,
statistical.
The function has this syntax:
function() OVER()
For example, SELECT RANK() OVER(ORDER BY salary) FROM employee
The part may be empty, as it is in the above example: "RANK()."
The contains an ordering, partitioning, or windowing clause.
The ordering clause in the above example is "OVER(ORDER BY salary)."
An analytical function that uses an ordering may also partition the result set b
ased on some attribute value.
The Row-numbering and Ranking Functions
ROW_NUMBER
RANK
DENSE_RANK
PERCENT_RANK
CUME_DIST
NTILE

You use the ranking functions to calculate ranks, percentiles, and n-tiles.
RANK() returns the rank of items in a group.
RANK() leaves a gap in the sequence of rankings in the event of a tie.
DENSE_RANK() returns the rank of items in a group.
DENSE_RANK() doesn t leave a gap in the sequence of rankings in the event of a t
ie.
CUME_DIST() returns the position of a specified value relative to a group of val
ues.
CUME_DIST() is short for cumulative distribution.
PERCENT_RANK() returns the percent rank of a value relative to a group of values
.
NTILE() returns n-tiles: tertiles, quartiles, and so on.
ROW_NUMBER() returns a number with each row in a group.
select rownum, ename, empno
from (select ename, empno from emp)
/
select empno, ename, sal, rownum
from (select empno, ename, sal from emp order by sal desc)
order by ename
/
The format of an analytical function: function() OVER()
where contains ordering, partitioning, windowing, or some combination.
select rownum, empno, ename, sal
from emp order by sal desc
/
ROWNUM EMPNO ENAME SAL
------ ---------- ---------- ----------
1 7369 TOM
13 7902 FORD
10 7844 TURNER
14 7934 MILLER 21000
12 7900 JAMES 19000
11 7876 ADAMS 18000
9 7839 KING 16000
8 7788 SCOTT 15000
7 7782 CLARK 14000
6 7698 BLAKE 13000
5 7654 MARTIN 12000
ROWNUM EMPNO ENAME SAL
------ ---------- ---------- ----------
4 7566 JONES 11000
3 7521 WARD 10000
2 7499 ALLEN 9000

ROW_NUMBER():
select row_number() over (order by sal desc) row_num, empno, ename, sal
from emp
/
ROW_NUM EMPNO ENAME SAL
--------- ---------- ---------- ----------
1 7369 TOM
2 7902 FORD
3 7844 TURNER
4 7934 MILLER 21000
5 7900 JAMES 19000
6 7876 ADAMS 18000
7 7839 KING 16000
8 7788 SCOTT 15000
9 7782 CLARK 14000
10 7698 BLAKE 13000
11 7654 MARTIN 12000
ROW_NUM EMPNO ENAME SAL
--------- ---------- ---------- ----------
12 7566 JONES 11000
13 7521 WARD 10000
14 7499 ALLEN 9000
select row_number() over (order by sal desc nulls last) row_num, empno, ename, s
al
from emp
/

ROW_NUM EMPNO ENAME SAL


-------- ---------- ---------- ----------
1 7934 MILLER 21000
2 7900 JAMES 19000
3 7876 ADAMS 18000
4 7839 KING 16000
5 7788 SCOTT 15000
6 7782 CLARK 14000
7 7698 BLAKE 13000
8 7654 MARTIN 12000
9 7566 JONES 11000
10 7521 WARD 10000
11 7499 ALLEN 9000
ROW_NUM EMPNO ENAME SAL
-------- ---------- ---------- ----------
12 7844 TURNER
13 7369 TOM
14 7902 FORD

select deptno, sum(sal), row_number()


over (order by sum(sal) desc)
as row_number from emp group by deptno
/
DEPTNO SUM(SAL) ROW_NUMBER
------- ---------- ----------
30 63000 1
10 51000 2
20 44000 3

SELECT ename, deptno, sal,


ROW_NUMBER() OVER(PARTITION BY deptno ORDER BY sal desc) "Test#"
FROM emp
ENAME DEPTNO SAL Test#
---------- ---------- ---------- ----------
MILLER 10 21000 1
KING 10 16000 2
CLARK 10 14000 3
TOM 20 1
FORD 20 2
ADAMS 20 18000 3
SCOTT 20 15000 4
JONES 20 11000 5
TURNER 30 1
JAMES 30 19000 2
BLAKE 30 13000 3
ENAME DEPTNO SAL Test#
---------- ---------- ---------- ----------
MARTIN 30 12000 4
WARD 30 10000 5
ALLEN 30 9000 6
14 rows selected.
RANK() and DENSE_RANK() rank items in a group.
RANK() leaves a gap in the sequence when there is a tie.
DENSE_RANK() leaves no gaps when there is a tie.
select deptno, sum(sal), rank()
over (order by sum(sal) desc) as rank,
dense_rank() over (order by sum(sal) desc) as dense_rank
from emp group by deptno
/
DEPTNO SUM(SAL) RANK DENSE_RANK
------ ---------- ---------- ----------
30 63000 1 1
10 51000 2 2
20 44000 3 3
select deptno, sal, rank()
over (order by sal desc) as rank,
dense_rank() over (order by sal desc) as dense_rank
from emp
/
DEPTNO SAL RANK DENSE_RANK
------ ---------- ---------- ----------
20 1 1
20 1 1
30 1 1
10 21000 4 2
30 19000 5 3
20 18000 6 4
10 16000 7 5
20 15000 8 6
10 14000 9 7
30 13000 10 8
30 12000 11 9
DEPTNO SAL RANK DENSE_RANK
------ ---------- ---------- ----------
20 11000 12 10
30 10000 13 11
30 9000 14 12
select deptno, sal, rank()
over (order by sal desc nulls first) as rank
, dense_rank() over (order by sal desc nulls last) as dense_rank
from emp
/
DEPTNO SAL RANK DENSE_RANK
------ ---------- ---------- ----------
10 21000 4 1
30 19000 5 2
20 18000 6 3
10 16000 7 4
20 15000 8 5
10 14000 9 6
30 13000 10 7
30 12000 11 8
20 11000 12 9
30 10000 13 10
30 9000 14 11
DEPTNO SAL RANK DENSE_RANK
------ ---------- ---------- ----------
20 1 12
30 1 12
20 1 12

select deptno, sal


, rank() over (partition by deptno order by sal desc) as rank
, dense_rank() over (partition by deptno order by sal desc ) as dense_rank
from emp
/
DEPTNO SAL RANK DENSE_RANK
------- ---------- ---------- ----------
10 21000 1 1
10 16000 2 2
10 14000 3 3
20 1 1
20 1 1
20 18000 3 2
20 15000 4 3
20 11000 5 4
30 1 1
30 19000 2 2
30 13000 3 3
DEPTNO SAL RANK DENSE_RANK
------- ---------- ---------- ----------
30 12000 4 4
30 10000 5 5
30 9000 6 6
TOP 5 sals:
select *
from ( select empno, sal, dense_rank() over(order by sal desc) top
from emp where sal is not null) where top <=5
/
EMPNO SAL TOP
----- ---------- ----------
7934 21000 1
7900 19000 2
7876 18000 3
7839 16000 4
7788 15000 5
select *
from
( select empno, deptno, sal, dense_rank()
over(partition by deptno order by sal desc) top
from emp where sal is not null) where top <=5
/
EMPNO DEPTNO SAL TOP
----- ---------- ---------- ----------
7934 10 21000 1
7839 10 16000 2
7782 10 14000 3
7876 20 18000 1
7788 20 15000 2
7566 20 11000 3
7900 30 19000 1
7698 30 13000 2
7654 30 12000 3
7521 30 10000 4
7499 30 9000 5
Using sum as analytical function:
---------------------------------
It is giving % of sal of each emp over dept sal.
select empno,sal, deptno, round(sal/sum(sal) over (partition by deptno),2) rank
from emp
group by empno, sal, deptno
/
EMPNO SAL DEPTNO RANK
----- ---------- ---------- ----------
7782 14000 10 .27
7839 16000 10 .31
7934 21000 10 .41
7566 11000 20 .25
7788 15000 20 .34
7876 18000 20 .41
7369 20
7902 20
7499 9000 30 .14
7521 10000 30 .16
7654 12000 30 .19
EMPNO SAL DEPTNO RANK
----- ---------- ---------- ----------
7698 13000 30 .21
7900 19000 30 .3
7844 30
select deptno, sum(sum(sal)) over (partition by deptno) as tot
from emp
group by deptno
/
DEPTNO TOT
------ ----------
10 51000
20 44000
30 63000

IT will give the sal of each dept.


select deptno, sum(sal) over (partition by deptno) as tot
from emp;
DEPTNO TOT
------ ----------
10 51000
10 51000
10 51000
20 44000
20 44000
20 44000
20 44000
20 44000
30 63000
30 63000
30 63000
DEPTNO TOT
------ ----------
30 63000
30 63000
30 63000
IT will give the salary of each dept and salary of each job.
select deptno, job, sum(sum(sal)) over (partition by deptno) as tot_dept,
sum(sum(sal)) over (partition by job) as tot_job
from emp
group by deptno, job
order by deptno, job
/
DEPTNO JOB TOT_DEPT TOT_JOB
------ --------- ---------- ----------
10 CLERK 51000 58000
10 MANAGER 51000 38000
10 PRESIDENT 51000 16000
20 ANALYST 44000 15000
20 CLERK 44000 58000
20 MANAGER 44000 38000
30 CLERK 63000 58000
30 MANAGER 63000 38000
30 SALESMAN 63000 31000
RATIO_TO_REPORT() function computes the ratio of a value to the sum of a set of
values.
select empno, sal, deptno, sum(sal) over (partition by deptno)as sal_dept,
sal/sum(sal) over (partition by deptno) as ratio_emp_sal_dept,
ratio_to_report(sum(sal)) over (partition by deptno) as ratio_to_report
from emp
group by empno, sal, deptno
/
EMPNO SAL DEPTNO SAL_DEPT RATIO_EMP_SAL_DEPT RATIO_TO_REPORT
----- ---------- ---------- ---------- ------------------ ---------------
7782 14000 10 51000 .274509804 .274509804
7839 16000 10 51000 .31372549 .31372549
7934 21000 10 51000 .411764706 .411764706
7566 11000 20 44000 .25 .25
7788 15000 20 44000 .340909091 .340909091
7876 18000 20 44000 .409090909 .409090909
7369 20 44000
7902 20 44000
7499 9000 30 63000 .142857143 .142857143
7521 10000 30 63000 .158730159 .158730159
7654 12000 30 63000 .19047619 .19047619
EMPNO SAL DEPTNO SAL_DEPT RATIO_EMP_SAL_DEPT RATIO_TO_REPORT
----- ---------- ---------- ---------- ------------------ ---------------
7698 13000 30 63000 .206349206 .206349206
7900 19000 30 63000 .301587302 .301587302
7844 30 63000
The CUBE clause return rows containing a subtotal for all combinations of column
s along with a total at the end.:
select deptno, job, sum(sal)
from emp
group by cube(deptno, job)
order by deptno, job
/
DEPTNO JOB SUM(SAL)
------ --------- ----------
10 CLERK 21000
10 MANAGER 14000
10 PRESIDENT 16000
10 51000
20 ANALYST 15000
20 CLERK 18000
20 MANAGER 11000
20 44000
30 CLERK 19000
30 MANAGER 13000
30 SALESMAN 31000
DEPTNO JOB SUM(SAL)
------ --------- ----------
30 63000
ANALYST 15000
CLERK 58000
MANAGER 38000
PRESIDENT 16000
SALESMAN 31000
158000
The ROLLUP clause extends GROUP BY to return a row containing a subtotal for eac
h group along with a total for all groups.
select deptno, job, sum(sal)
from emp
group by rollup(deptno, job)
order by deptno, job
/
DEPTNO JOB SUM(SAL)
------ --------- ----------
10 CLERK 21000
10 MANAGER 14000
10 PRESIDENT 16000
10 51000
20 ANALYST 15000
20 CLERK 18000
20 MANAGER 11000
20 44000
30 CLERK 19000
30 MANAGER 13000
30 SALESMAN 31000
DEPTNO JOB SUM(SAL)
------ --------- ----------
30 63000
158000
Using the GROUPING() Function
The GROUPING() function accepts a column and returns 0 or 1. GROUPING() returns
1 when
the column value is null and returns 0 when the column value is non-null. GROUPI
NG() is used
only in queries that use ROLLUP or CUBE. GROUPING() is useful when you want to d
isplay a
value when a null would otherwise be returned.
select decode(grouping(deptno), 1, ALL dept , deptno) dept,
decode(grouping(job), 1, ALL jobs , job) JOB, sum(sal)
from emp
group by rollup(deptno, job)
order by deptno
DEPT JOB SUM(SAL)
---------- --------- ----------
10 CLERK 21000
10 MANAGER 14000
10 PRESIDENT 16000
10 ALL jobs 51000
20 ANALYST 15000
20 CLERK 18000
20 MANAGER 11000
20 ALL jobs 44000
30 CLERK 19000
30 MANAGER 13000
30 SALESMAN 31000
30 ALL jobs 63000
ALL dept ALL jobs 158000
Getting the First Rows Using FIRST_VALUE():
Getting the Last Rows Using LAST_VALUE():
select empno, deptno, sal,
first_value(sal) over(order by empno
rows between 1 preceding and 1 following ) as prev_sal,
last_value(sal) over(order by empno
rows between 1 preceding and 1 following ) as prev_sal
from emp where sal is not null
order by empno
/
EMPNO DEPTNO SAL PREV_SAL PREV_SAL
----- ---------- ---------- ---------- ----------
7499 30 9000 9000 10000
7521 30 10000 9000 11000
7566 20 11000 10000 12000
7654 30 12000 11000 13000
7698 30 13000 12000 14000
7782 10 14000 13000 15000
7788 20 15000 14000 16000
7839 10 16000 15000 18000
7876 20 18000 16000 19000
7900 30 19000 18000 21000
7934 10 21000 19000 21000
LAG() and LEAD() functions return a value in a row where that row is a certain n
umber of rows away from the current row.
select
empno, (sal) as dept_sal
, lag((sal),1) over (order by deptno) as prev_sal,
lead((sal),1) over (order by deptno) as next_sal
from emp
/
EMPNO DEPT_SAL PREV_SAL NEXT_SAL
----- ---------- ---------- ----------
7782 14000 16000
7839 16000 14000 21000
7934 21000 16000 11000
7566 11000 21000
7902 11000 18000
7876 18000
7369 18000 15000
7788 15000 10000
7521 10000 15000
7844 10000 9000
7499 9000 19000
7900 19000 9000 13000
7698 13000 19000 12000
7654 12000 13000
You use the FIRST and LAST functions to get the first and last values in an orde
red group. You can
use FIRST and LAST with the following functions: MIN(), MAX(), COUNT(), SUM(), A
VG(),
STDDEV(), and VARIANCE().
e.g it will order the deptno in asc and select first dept, which is 10
obtain min and max salary from it.
select min(sal) keep (dense_rank first order by deptno) min_sal,
max(sal) keep (dense_rank first order by deptno) max_sal
from emp
* --where deptno = 20
> /
MIN_SAL MAX_SAL
------- ----------
14000 21000

SELECT LEVEL, LPAD( , 2 * LEVEL - 1) || first_name || ' ' || last_name AS empl


oyee
FROM more_employees
START WITH employee_id = 1
CONNECT BY PRIOR employee_id = manager_id
AND last_name != 'Johnson';
SELECT job_id, division_id, SUM(salary)
FROM employees2
GROUP BY ROLLUP(job_id, division_id)
ORDER BY job_id, division_id;
SELECT job_id, division_id, SUM(salary)
FROM employees2
GROUP BY CUBE(job_id, division_id)
ORDER BY job_id, division_id;
SELECT
prd_type_id, SUM(amount),
RANK() OVER (ORDER BY SUM(amount) DESC NULLS LAST) AS rank,
DENSE_RANK() OVER (ORDER BY SUM(amount) DESC NULLS LAST) AS dense_rank
FROM all_sales
WHERE year = 2003
GROUP BY prd_type_id
ORDER BY prd_type_id;
SELECT
prd_type_id, month, SUM(amount),
RANK() OVER (PARTITION BY month ORDER BY SUM(amount) DESC) AS rank
FROM all_sales
WHERE year = 2003
AND amount IS NOT NULL
GROUP BY prd_type_id, month
ORDER BY prd_type_id, month;
Window functions calculates things like
cumulative sums, moving averages within a specified range of rows,
a range of values, or an interval of time.
Window function can work with: SUM(), AVG(), MAX(), MIN(), COUNT(),
VARIANCE(), and STDDEV().
Window function can also work with FIRST_VALUE() and
LAST_VALUE(), which return the first and last values in a window.
select empno, sum(sum(sal)) over (order by empno rows between
unbounded preceding and current row) as cumm_sal
from emp
group by empno
order by empno
/
EMPNO CUMM_SAL
------ ----------
7369
7499 9000
7521 19000
7566 30000
7654 42000
7698 55000
7782 69000
7788 84000
7839 100000
7844 100000
7876 118000
EMPNO CUMM_SAL
------ ----------
7900 137000
7902 137000
7934 158000
select deptno, sum(sal) sum_sal,
avg(sum(sal)) over(order by deptno rows between 1 preceding
and current row)
as moving_avg
from emp
group by deptno
order by deptno
/

DEPTNO SUM_SAL MOVING_AVG


------ ---------- ----------
10 51000 51000
20 44000 47500
30 63000 53500
SELECT
2 city, SUM(salary) AS city_salary,
3 AVG(SUM(salary)) OVER
4 (ORDER BY city ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
5 AS moving_average
6 FROM employee
7 GROUP BY city
8 ORDER BY city;
SELECT
deptno, SUM(sal) AS emp_sal,
FIRST_VALUE(SUM(sal)) OVER
(ORDER BY deptno ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
AS previous_month_sal,
LAST_VALUE(SUM(sal)) OVER
(ORDER BY deptno ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
AS next_month_sal
FROM emp
where sal is not null
GROUP BY deptno
ORDER BY deptno
/
DEPTNO EMP_SAL PREVIOUS_MONTH_SAL NEXT_MONTH_SAL
------ ---------- ------------------ --------------
10 51000 51000 44000
20 44000 51000 63000
30 63000 44000 63000

SELECT
month, SUM(amount) AS month_amount,
LAG(SUM(amount), 1) OVER (ORDER BY month) AS previous_month_amount,
LEAD(SUM(amount), 1) OVER (ORDER BY month) AS next_month_amount
FROM all_sales
WHERE year = 2003

SELECT id "ID", value,


2 SUM(value) OVER(ORDER BY id
3 ROWS BETWEEN CURRENT ROW
4 AND UNBOUNDED FOLLOWING) "Running total"
5 FROM myTable
6 ORDER BY id;
Four highest salaries:
select sal
from emp a where 4>=( select distinct(count(sal)) from emp
b where a.sal <= b.sal)
and sal is not null
order by sal desc
select sal
from emp a where 4>=( select distinct(count(sal)) from emp
b where a.sal <= b.sal) and sal is not null
oredr by sal desc
select sal
from (select sal, dense_rank() over(order by sal desc) top_rank
from emp) where top_rank < 4
SQLERRM & SQLCODE:
-----------------
BEGIN
DBMS_OUTPUT.PUT_LINE('SQLERRM(0): ' || SQLERRM(0));
DBMS_OUTPUT.PUT_LINE('SQLERRM(100): ' || SQLERRM(100));
DBMS_OUTPUT.PUT_LINE('SQLERRM: ' || SQLERRM);
DBMS_OUTPUT.PUT_LINE('SQLERRM(-1): ' || SQLERRM(-1));
END;
SQLERRM(0): ORA-0000: normal, successful completion
SQLERRM(100): ORA-01403: no data found
SQLERRM: ORA-0000: normal, successful completion
SQLERRM(-1): ORA-00001: unique constraint (.) violated
declare
v_error varchar2(10);
begin
select empno
into v_error
from emp
where ename = 'JACK';
Exception
when others
then
dbms_output.put_line('In others queue');
dbms_output.put_line('SQLERRM: '||SQLERRM);
dbms_output.put_line('SQLCODE: '||SQLCODE);
end;
/
SQL> set serveroutput on
SQL> /
In others queue
SQLERRM: ORA-01403: no data found
SQLCODE: 100
======================================================================
SELECT *
FROM (
SELECT month, prd_type_id, amount
FROM all_sales
WHERE year = 2003
AND prd_type_id IN (1, 2, 3)
)
PIVOT (
SUM(amount) FOR month IN (1 AS JAN, 2 AS FEB, 3 AS MAR, 4 AS APR)
)
ORDER BY prd_type_id;

SELECT *
FROM pivot_sales_data
UNPIVOT (
amount FOR month IN (JAN, FEB, MAR, APR)
)
ORDER BY prd_type_id;
===============================================================
GO to statement in PL/SQL
create or replace procedure myproc
as
l_checking boolean := FALSE;
begin
null;
if l_checking
then
dbms_output.put_line('Never here....');
else
dbms_output.put_line('Always here...');
goto end_of_function;
end if;
<<end_of_function>>
dbms_output.put_line('Hello Rohit');
end;
/
================================================================
create or replace function emp_exists
(v_empno in emp.empno%type) return boolean as
v_name emp.ename%type;
begin
select ename into v_name from emp
where empno=v_empno;
if v_name in ('Rohit') then
return false;
else
return true;
end if;
end;
/

declare
cnt pls_integer :=0;
begin
for v_rec in (select empno, ename from emp)
loop
if emp_exists(v_rec.empno) then
dbms_output.put_line(v_rec.empno||' has name '||v_rec.ename);
end if;
cnt := cnt+1;
end loop;
dbms_output.put_line(cnt);
end;
DECLARE
2 test_date DATE;
3 day_of_week VARCHAR2(3);
4 years_ahead INTEGER;
5 BEGIN
6 test_date := TO_DATE('1-Jan-1997','dd-mon-yyyy');
7
8 FOR years_ahead IN 1..10 LOOP
9 day_of_week := TO_CHAR(test_date,'Dy');
10
11 IF day_of_week IN ('Sat','Sun') THEN
12 DBMS_OUTPUT.PUT_LINE(TO_CHAR(test_date,'dd-Mon-yyyy')|| ' A long
weekend!');
13 ELSE
14 DBMS_OUTPUT.PUT_LINE(TO_CHAR(test_date,'dd-Mon-yyyy')|| ' Not a long
weekend.');
15 END IF;
16 test_date := ADD_MONTHS(test_date,12);
17 END LOOP;
18 END;
19 /

create or replace function f_getDateType (in_dt DATE)


2 return VARCHAR2
3 is
4 v_out VARCHAR2(10);
5 begin
6 if to_char(in_dt,'MMDD') in ('0101','0704') then
7 v_out:='HOLIDAY';
8 elsif to_char(in_dt,'d') = 1 then
9 v_out:='SUNDAY';
10 elsif to_char(in_dt,'d') = 7 then
11 v_out:='SATURDAY';
12 else
13 v_out:='WEEKDAY';
14 end if;
15 return v_out;
16 end;
17 /
===============================================================
DECODE with NULL
SQL> select decode(sal, null, 'NULL', sal) sal
2 from emp;
SAL
----------------------------------------
NULL
9000
10000
11000
12000
13000
14000
15000
16000
NULL
18000
================================================================
CASE WITH NULL
1 select case sal
2 when null then 'NULL'
3 else to_char(sal)
4 end salary
5* from emp
SQL> /
SALARY
----------------------------------------
9000
10000
11000
12000
13000
14000
15000
16000
18000
SALARY
----------------------------------------
19000
21000

1 select case
2 when sal is null then 'NULL'
3 else to_char(sal)
4 end salary
5* from emp
SQL> /
SALARY
----------------------------------------
NULL
9000
10000
11000
12000
13000
14000
15000
16000
NULL
18000
SALARY
----------------------------------------
19000
NULL
21000

1 select case nvl(to_char(sal), 'X')


2 when 'X' then 'NULL'
3 else to_char(sal)
4 end salary
5* from emp
SQL> /
SALARY
----------------------------------------
NULL
9000
10000
11000
12000
13000
14000
15000
16000
NULL
18000
SALARY
----------------------------------------
19000
NULL
21000
SQL> select sal
2 from emp
3 where exists(select null from dept);
SAL
----------
9000
10000
11000
12000
13000
14000
15000
16000
18000
Although the following code is correct from the syntax point of view, it doesn't
work:
select
2 case when salary between 6 and 8 then '6-8'
3 when salary in (9,10) then '9-10'
4 when exists (select null from avg_sal where avg_sal = salary)
5 then 'EXISTS'
6 when to_char(salary) like '2%' then 'Like2'
7 when salary is null then 'Null'
8 else 'ELSE Empno: '|| emp_no
9 end
10 AS case_test
11 from emp
12 /
Use Case to output null value:
SELECT count(*),
2 CASE City
3 WHEN 'Vancouver' then 'No'
4 WHEN 'New York' then 'Yes'
5 WHEN null then 'Null'
6 END CITY -- CASE
7 FROM employee
8 GROUP BY city;
COUNT(*) CITY
---------- ----
2 Yes
1
5 No
create or replace function my_fun
(v_empno in emp.empno%type,
v_ename in emp.ename%type) return varchar2
is
v_myvar varchar2(20);
cnt pls_integer;
begin
insert into emp(empno, ename) values(v_empno,v_ename);
select count(*) into cnt from emp
where empno=v_empno;
if cnt > 0 then
v_myvar:='Success';
else
v_myvar:='Failure';
end if;
return v_myvar;
exception
when others then
null;
end;
/
declare
2 a number :=20;
3 b number :=-40;
4 string varchar2(50);
5 begin
6 string :=case
7 when (a>b)then 'A is greater than B'
8 when (a<b)then 'A is less than B'
9 else
10 'A is equal to B'
11 end;
12 dbms_output.put_line(string);
13 end;
14 /
A is greater than B
PL/SQL procedure successfully completed.
Case statement to call procedure:
DECLARE
2 salary NUMBER := 20000;
3 employee_id NUMBER := 36325;
4
5 PROCEDURE give_bonus (emp_id IN NUMBER, bonus_amt IN NUMBER) IS
6 BEGIN
7 DBMS_OUTPUT.PUT_LINE(emp_id);
8 DBMS_OUTPUT.PUT_LINE(bonus_amt);
9 END;
10
11 BEGIN
12 CASE
13 WHEN salary >= 10000 AND salary <=20000 THEN
14 give_bonus(employee_id, 1500);
15 WHEN salary > 20000 AND salary <= 40000 THEN
16 give_bonus(employee_id, 1000);
17 WHEN salary > 40000 THEN
18 give_bonus(employee_id, 500);
19 ELSE
20 give_bonus(employee_id, 0);
21 END CASE;
22 END;
23 /
PL/SQL procedure successfully completed.
DECLARE
2 salary NUMBER := 20000;
3 employee_id NUMBER := 36325;
4
5 PROCEDURE give_bonus (emp_id IN NUMBER, bonus_amt IN NUMBER) IS
6 BEGIN
7 DBMS_OUTPUT.PUT_LINE(emp_id);
8 DBMS_OUTPUT.PUT_LINE(bonus_amt);
9 END;
10
11 BEGIN
12 give_bonus(employee_id,
13 CASE
14 WHEN salary >= 10000 AND salary <=20000 THEN 1500
15 WHEN salary > 20000 AND salary <= 40000 THEN 1000
16 WHEN salary > 40000 THEN 500
17 ELSE 0
18 END);
19 END;
20 /
Simple Loops:
A simple loop runs until you explicitly end the loop.
The syntax for a simple loop is as follows:
LOOP
statements
END LOOP;
To end the loop, you use either an EXIT or EXIT WHEN statement.
The EXIT statement ends a loop immediately.
EXIT WHEN statement ends a loop when a specified condition occurs.
declare
2 v_ind NUMBER;
3 v_current NUMBER;
4 begin
5 v_current:=0; -- should not be null!
6 loop
7 v_ind:=0; -- reset each time
8 loop
9 v_ind:=v_ind+1;
10 DBMS_OUTPUT.put_line(v_current);
11 exit when v_ind=4;
12 end loop;
13 v_current:=v_current+5;
14 exit when v_current=25;
15 end loop;
16 end;
17 /

DECLARE
2 v_FirstName VARCHAR2(20);
3 v_LastName VARCHAR2(20);
4
5 CURSOR c_Students IS
6 SELECT first_name, last_name FROM employee;
7 BEGIN
8 -- Begin cursor processing.
9 OPEN c_Students;
10 LOOP
11 -- Retreive one row.
12 FETCH c_Students INTO v_FirstName, v_LastName;
13 EXIT WHEN c_Students%NOTFOUND;
14 END LOOP;
15 CLOSE c_Students;
16 END;
17 /
SQL> DECLARE
2
3 year_number PLS_INTEGER := 1992;
4
5 BEGIN
6
7 <<year_loop>>
8 WHILE year_number <= 1995
9 LOOP
10
11 dbms_output.put_line('year = '||year_number);
12
13 <<month_loop>>
14 FOR month_number IN 1 .. 12
15 LOOP
16 dbms_output.put_line('...and month = '||month_number);
17
18 END LOOP month_loop;
19
20 year_number := year_number + 2;
21
22 END LOOP year_loop;
23
24 END;
25 /
FOR Loops:
A FOR loop runs a predetermined number of times.
The syntax for a FOR loop is as follows:
FOR loop_variable IN [REVERSE] lower_bound..upper_bound LOOP
statements
END LOOP;
where
loop_variable specifies the loop variable.
REVERSE specifies that the loop variable value is to be decremented each time th
rough
the loop.
lower_bound specifies the loop's lower bound.
upper_bound specifies the loop's upper bound.
If REVERSE is used, the loop variable is initialized to this upper bound.
SQL> BEGIN
2 <<outerloop>>
3 FOR v_outerloopcounter IN 1..2 LOOP
4 <<innerloop>>
5 FOR v_innerloopcounter IN 1..4 LOOP
6 DBMS_OUTPUT.PUT_LINE('Outer Loop counter is ' ||
7 v_outerloopcounter ||
8 ' Inner Loop counter is ' ||
9 v_innerloopcounter);
10 END LOOP innerloop;
11 END LOOP outerloop;
12 END;
13 /
REVERSE FOR LOOP:
----------------
BEGIN
FOR v_loopcounter IN REVERSE 1..5 LOOP
DBMS_OUTPUT.PUT_LINE('Loop counter is ' || v_loopcounter);
END LOOP;
END;
/
Loop counter is 5
Loop counter is 4
Loop counter is 3
Loop counter is 2
Loop counter is 1
SQL> BEGIN
2 FOR v_loopcounter IN 1..6 LOOP
3 IF MOD(v_loopcounter,2) = 0 THEN
4 DBMS_OUTPUT.PUT_LINE('Loop counter is ' || v_loopcounter);
5 END IF; -- End execution of statements for even counter
6 END LOOP;
7 END;
8 /
Loop counter is 2
Loop counter is 4
Loop counter is 6
WHILE loops check the condition and execute a set of commands.
The loop is repeated until the loop is exited.
A WHILE loop is exactly equivalent to a regular loop with an EXIT WHEN as the fi
rst statement.
The condition is checked before the code in the loop is executed.
If the condition is false, the code in the loop will never be executed.
WHILE loop makes your code a little easier to read than with a normal loop.
declare
i pls_integer :=0;
BEGIN
LOOP
exit when i>5;
DBMS_OUTPUT.PUT_LINE('Loop counter is ' || i);
i:=i+1;
END LOOP;
END;
/
No output will be generated:
DECLARE
v_Counter BINARY_INTEGER;
BEGIN
WHILE v_Counter <= 50 LOOP
INSERT INTO employee (id) VALUES (v_Counter);
v_Counter := v_Counter + 1;
END LOOP;
END;
/
SQL> DECLARE
CURSOR cursorValue IS
SELECT h.product_description,o.company_short_name
FROM company o,product h
WHERE o.product_id =h.product_id
ORDER by 2;
v_company_rec cursorValue%ROWTYPE;
BEGIN
OPEN cursorValue;
FETCH cursorValue INTO v_company_rec;
WHILE (cursorValue%FOUND) LOOP
dbms_output.put_line(rpad(v_company_rec.product_description,20,' ')||' '|
|
rpad(v_company_rec.company_short_name,30,' '));
FETCH cursorValue INTO v_company_rec;
END LOOP;
CLOSE cursorValue;
END;
/
SQL> DECLARE
v_Radius NUMBER := 2;
BEGIN
WHILE TRUE LOOP
DBMS_OUTPUT.PUT_LINE('The Area is ' ||mypi * v_Radius * v_Radius
);
EXIT WHEN v_RADIUS = 10;
v_Radius := v_Radius + 2 ;
END LOOP;
END;
/

=======================================================rohit====================
====================================
CREATE OR REPLACE function letterCounter(myString VARCHAR2)
RETURN NUMBER IS
v_current_position INTEGER := 1;
v_counter NUMBER := 0;
BEGIN
WHILE v_current_position <= LENGTH(myString) LOOP
IF SUBSTR(myString,v_current_position,1) != ' ' THEN
v_counter := v_counter + 1;
ELSE
NULL;
END IF;
v_current_position := v_current_position + 1;
END LOOP;
RETURN v_counter ;
END letterCounter;
/
Function created.
SQL>
SQL> DECLARE
v_MYTEXT VARCHAR2(20) := 'THIS IS A TEST';
BEGIN
DBMS_OUTPUT.PUT_LINE('Total count is ' || letterCounter(v_MYTEXT));
END;
/
Total count is 11
================================================================================
=
Understanding bind variable types:
Bind variables can be of type IN, OUT, or IN OUT.
By default, all parameters are of type IN.
Using an OUT Parameter
declare
2 a NUMBER;
3 b NUMBER:=1;
4 c NUMBER:=2;
5 v_plsql_tx VARCHAR2(2000);
6 begin
7 v_plsql_tx := 'begin ' || ':1:=:2 -:3; ' || 'end;';
8 execute immediate v_plsql_tx using out a, b, c;
9 DBMS_OUTPUT.put_line('a='||a);
10 end;
11 /
a=-1
SQL> create or replace function f_getMax(i_empNo1 VARCHAR,i_empNo2 VARCHAR,i_col
umn_tx VARCHAR2)
2 return VARCHAR2
3 is
4 v_out_tx VARCHAR2(2000);
5 v_sql_tx VARCHAR2(2000);
6 begin
7 v_sql_tx := 'select max('||i_column_tx||')'
|| ' from employee ' || 'where id in (:var01, :var02)';
8 execute immediate v_sql_tx into v_out_tx using i_empNo1, i_empNo2;
9 return v_out_tx;
10 exception
11 when others then return null;
12 end f_getMax;
13 /
Function created.
SQL> declare
2 v_null_nr NUMBER:=NULL;
3 v_where_tx VARCHAR2(2000):='salary>2000';
4 v_sql_tx VARCHAR2(2000);
5 begin
6 v_sql_tx:='update employee ' || ' set salary=:1 ' || 'where '||v_where_
tx;
7 execute immediate v_sql_tx using v_null_nr;
8 end;
9 /
Building DDL on the Fly:
There is no way to place DDL statements
(drop function, create table, and so on) into PL/SQL.
But you can place DDL statements to a dynamic SQL.
Dynamic SQL allows you to do whatever you want with database objects.
All DDL statements always fire implicit commits.
In most cases, procedures and functions that generate DDL should be defined
as autonomous transactions.
SQL> create or replace function f_getEmp_tx(i_empNo VARCHAR, i_column_tx VARCHAR
2)
return VARCHAR2
is
v_out_tx VARCHAR2(2000);
v_sql_tx VARCHAR2(2000);
begin
v_sql_tx := 'select '||i_column_tx|| ' from employee '
|| 'where id='||'''||i_empNo||''';
execute immediate v_sql_tx into v_out_tx;
return v_out_tx;
exception
when others then return null;
end;
/
SQL> select f_getEmp_tx('01', 'first_name') from dual;
SQL> create or replace procedure p_removeDept(i_deptNo VARCHAR) is
2 begin
3 savepoint A;
4 update employee set salary = 2000 where id=i_deptNo;
5 delete from employee where id ='02';
6
7 execute immediate 'drop table employee';
8 exception
9 when others then
10 rollback to savepoint A;
11 raise;
12 end;
13 /
SQL> call p_removeDept('01');
Simple EXECUTE IMMEDIATE:
The EXECUTE IMMEDIATE command can be
a VARCHAR2 variable,
a literal quoted string, or
any string expression.
The code to be executed can be passed as a variable or directly as a string in t
he command.
The string cannot exceed 32K.
The code can be a single SQL command or a large block of PL/SQL.
All PL/SQL blocks passed as a string should have a semicolon at the end,
as shown here: execute immediate 'begin p_test; end;';
All SQL statements passed as a string should not have a semicolon at the end,
as shown here: execute immediate 'select 1 from dual' into a;
CREATE OR REPLACE FUNCTION value_in (varname IN VARCHAR)
2 RETURN VARCHAR2
3 IS
4 retval VARCHAR2(2000);
5 BEGIN
6 EXECUTE IMMEDIATE 'BEGIN :val := ' || varname || '; END;' USING OUT retv
al;
7 RETURN retval;
8 END;
9 /
SQL> variable b varchar2(10);
SQL> exec :b:=10;
PL/SQL procedure successfully completed.
SQL> exec :a:=value_in(:b);
PL/SQL procedure successfully completed.
SQL> print :a;
A
----------------------------------------
10
Use procedure to create an index dynamically:
SQL> CREATE OR REPLACE PROCEDURE runddl (ddl_in in VARCHAR2)
2 AUTHID CURRENT_USER
3 IS
4 BEGIN
5 EXECUTE IMMEDIATE ddl_in;
6 END;
7 /
Procedure created.
SQL>
SQL>
SQL>
SQL> CREATE OR REPLACE PROCEDURE creindx(index_in IN VARCHAR2, tab_in IN VARCHAR
2, col_in IN VARCHAR2)
2 IS
3 DDL_statement VARCHAR2(200):= 'CREATE INDEX ' || index_in || ' ON ' || t
ab_in ||' ( ' || col_in || ')';
4 BEGIN
5 runddl (DDL_statement);
6 END;
7 /
Procedure created.
BULK Collect:
declare
type array is table of varchar2(30) index by binary_integer;
l_data array;
begin
select ename BULK COLLECT into l_data from emp;
for j in 1 .. l_data.count
loop
dbms_output.put_line(l_data(j));
end loop;
end;
/
Fortunately, PL/SQL makes it easy for developers to control the
amount of memory used in a BULK COLLECT operation by using the LIMIT clause.
SQL> declare
2 type text_nt is table of VARCHAR2(256);
3 v_ename_nt text_nt;
4 cursor c_emp is select first_name from employee where id='02';
5 procedure p_print_row is
6 begin
7 if v_eName_nt.count=2 then
8 DBMS_OUTPUT.put_line(v_eName_nt(1)||' '||v_eName_nt(2));
9 elsif v_eName_nt.count=1 then
10 DBMS_OUTPUT.put_line(v_eName_nt(1));
11 end if;
12 end;
13 begin
14 open c_emp;
15 loop
16 fetch c_emp bulk collect into v_eName_nt limit 2;
17 p_print_row;
18 exit when c_emp%NOTFOUND;
19 end loop;
20 close c_emp;
21 end;
22 /

DECLARE
Type cust_rec_t is RECORD
(empl_no number, lastname varchar2(25) );
Type cust_tab_t is table of cust_rec_t
index by binary_integer;
cust_tab cust_tab_t;
BEGIN
UPDATE emp SET sal = '917'
WHERE deptno = '10'
RETURNING empno, ename
BULK COLLECT INTO cust_tab;
-- dbms_output.put_line(cust_tab(1).lastname );
dbms_output.put_line(cust_tab.COUNT );
END;
3
declare
2 type number_array is table of number;
3 type varchar2_array is table of varchar2(20);
4 v1 number_array;
5 v2 varchar2_array;
6
7 begin
8
9 select empl_no, lastname
10 bulk collect into v1, v2
11 from employee;
12
13 for i in 1..v1.LAST loop /* could use COUNT or SQL%rowcount */
14 dbms_output.put_line(v2(i) );
15 end loop;
16 end;
17 /
===============================================================================
=
The FORALL command builds a set of SQL statements and executes all of them at on
ce.
SQL> declare
2 type number_nt is table of VARCHAR(20);
3 v_deptNo_nt number_nt:=number_nt('01','02');
4 begin
5 forall i in v_deptNo_nt.first()..v_deptNo_nt.last()
6 update employee set salary = 0 where id =v_deptNo_nt(i);
7 end;
8 /
================================================================================
==
Autonomous Transactions:
Oracle can suspend a transaction and transfer execution control to an independen
t
child transaction.
This child transaction is called an autonomous transaction.
An autonomous transaction is completely independent of the calling transaction.
An autonomous transaction does not share resources, locks, or any commit
dependencies with the main transaction.
Autonomous transactions can include just as much functionality as any other
database transactions.
Autonomous transactions are useful for creating software components that can be
reused in numerous applications.
One advantage of using an autonomous transaction is that DML can be executed
and committed, even if the main transaction is rolled back.
Setting up the syntax for an autonomous transaction
Autonomous Transaction Syntax
SQL> create or replace procedure p_log_audit
2 (what_tx VARCHAR2, descr_tx VARCHAR2,
3 who_tx VARCHAR2, when_dt DATE)
4 is
5 pragma autonomous_transaction;
6 begin
7 insert into Audit_emp
8 values(audit_seq.nextval, what_tx, descr_tx,
9 who_tx, when_dt);
10 commit;
11 end;
12 /
Procedure created.
SQL>
SQL> create or replace trigger bu_emp
2 before update of salary on employee
3 referencing new as new old as old for each row
4 begin
5 p_log_audit ('update','update of emp.salary', user, SYSDATE);
6 end;
7 /
Trigger created.
Dynamic SQL
SQL> CREATE OR REPLACE PROCEDURE runddl (ddl_in in VARCHAR2)
2 IS
3 cur INTEGER:= DBMS_SQL.OPEN_CURSOR;
4 returnValue INTEGER;
5 BEGIN
6 DBMS_SQL.PARSE (cur, ddl_in, DBMS_SQL.NATIVE);
7 returnValue := DBMS_SQL.EXECUTE (cur);
8 DBMS_SQL.CLOSE_CURSOR (cur);
9 EXCEPTION
10 WHEN OTHERS
11 THEN
12 DBMS_OUTPUT.PUT_LINE (
13 'RunDDL Failure on ' || ddl_in);
14 DBMS_OUTPUT.PUT_LINE (SQLERRM);
15 DBMS_SQL.CLOSE_CURSOR (cur);
16 END;
17 /
SQL> -- Build an anonymous block that will trigger an error.
SQL> DECLARE
2
3 -- Define local exceptioon variable.
4 my_error EXCEPTION;
5
6 BEGIN
7
8
9 RAISE my_error;
10
11 EXCEPTION
12
13 WHEN others THEN
14 dbms_output.put_line('RAISE my_error'||CHR(10)
15 ||'SQLCODE ['||SQLCODE||']'||CHR(10)
16 ||'SQLERRM ['||SQLERRM||']');
17
18 END;
19 /
RAISE my_error
SQLCODE [1]
SQLERRM [User-Defined Exception]

SQL> DECLARE
2
3
4 my_error EXCEPTION;
5 PRAGMA EXCEPTION_INIT(my_error,-20001);
6
7 BEGIN
8
9 -- Raise the exception.
10 RAISE my_error;
11
12 EXCEPTION
13
14 WHEN my_error THEN
15 dbms_output.put_line('RAISE my_error'||CHR(10)
16 ||'SQLCODE ['||SQLCODE||']'||CHR(10)
17 ||'SQLERRM ['||SQLERRM
18 ||'User defined error.]');
19
20 END;
21 /
RAISE my_error
SQLCODE [-20001]
SQLERRM [ORA-20001: User defined error.]
Exceptions:
In PL/SQL, the user can catch certain runtime errors.
Exceptions can be internally defined by Oracle or the user.
Exceptions are used to handle errors that occur in your PL/SQL code.
A PL/SQL block contains an EXCEPTION block to handle exception.
There are three types of exceptions:
Predefined Oracle errors
Undefined Oracle errors
User-defined errors
The different parts of the exception.
Declare the exception.
Raise an exception.
Handle the exception.
An exception has four attributes:
Name provides a short description of the problem.
Type identifies the area of the error.
Exception Code gives a numeric representation of the exception.
Error message provides additional information about the exception.
The predefined divide-by-zero exception has the following values for the attribu
tes:
Name = ZERO_DIVIDE
Type = ORA (from the Oracle engine)
Exception Code = C01476
Error message = divisor is equal to zero
The following is the typical syntax of an exception-handling PL/SQL block.
exception
when exception_1 THEN
statements
when exception_2 THEN
statements
...
exception_1 and exception_2 are the names of the predefined exceptions.
statements is the PL/SQL code that will be executed if the exception name is sat
isfied.
Types of more commonly used Exceptions:
no_data_found Singleton SELECT statement returned no data.
too_many_rows Singleton SELECT statement returned more than one row of data.
invalid_cursor Illegal cursor operation occurred.
value_error Arithmetic, conversion, or truncation error occurred.
invalid_number Conversion of a number to a character string failed.
zero_divide Attempted to divide by zero.
dup_val_on_index Attempted to insert a duplicate value into a column that
has a unique index.
cursor_already_open Attempted to open a cursor that was previously opened.
not_logged_on A database call was made without being logged into Oracle.
transaction_backed_out Usually raised when a remote portion of a transaction is
rolled back.
login_denied Login to Oracle failed.
program_error If PL/SQL encounters an internal problem.
storage_error If PL/SQL runs out of memory or if memory is corrupted.
timeout_on_resource Timeout occurred while Oracle was waiting for a resource
.
others For all of the rest.
Exception Handling:
EXCEPTION
WHEN OTHERS THEN
<statements>
statements is one or more statements that will be processed when the exception o
ccurs.
You could always code a NULL statement if no action is to be taken.
Understanding Different Exception Types:
Error Code Prefix Indicates This Exception Type of Error
ORA Core RDBMS errors
PLS PL/SQL errors
FRM Oracle Forms errors
REP Oracle Reports errors
Note:
BEGIN
DBMS_OUTPUT.PUT_LINE(1 / 0);
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('Division by zero');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('An exception occurred');
END;
/
SQL> /
An exception occurred

BEGIN
DBMS_OUTPUT.PUT_LINE(1 / 0);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('An exception occurred');
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('Division by zero');
END;
/
SQL> /
WHEN OTHERS THEN
*
ERROR at line 4:
ORA-06550: line 4, column 8:
PLS-00370: OTHERS handler must be last among the exception handlers of a block
ORA-06550: line 0, column 0:
PL/SQL: Compilation unit analysis terminated
DECLARE
2 Num_a NUMBER := 6;
3 Num_b NUMBER;
4 BEGIN
5 Num_b := 0;
6 Num_a := Num_a / Num_b;
7 Num_b := 7;
8 dbms_output.put_line(' Value of Num_b ' || Num_b);
9 EXCEPTION
10 WHEN ZERO_DIVIDE THEN
11 DECLARE
12 err_num NUMBER := SQLCODE;
13 err_msg VARCHAR2(512) := SQLERRM;
14 BEGIN
15 dbms_output.put_line('ORA Error Number ' || err_num );
16 dbms_output.put_line('ORA Error message ' || err_msg);
17 dbms_output.put_line(' Value of Num_a is ' || Num_a);
18 dbms_output.put_line(' Value of Num_b is ' || Num_b);
19 END;
20 END;
21 /
ORA Error Number -1476
ORA Error message ORA-01476: divisor is equal to zero
Value of Num_a is 6
Value of Num_b is 0
An example showing continuing program execution after handling exception:
DECLARE
v_descr VARCHAR2(20);
BEGIN
BEGIN
SELECT sal
INTO v_descr
FROM emp
WHERE empno =12;
dbms_output.put_line(v_descr);
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('I am in 1 exception block');
END;
BEGIN
SELECT sal
INTO v_descr
FROM emp
WHERE empno =1;
dbms_output.put_line(v_descr);
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('I am in 2 exception block');
END;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('ERR:An error occurred with info :'||
TO_CHAR(SQLCODE)||' '||SQLERRM);
END;
/
SQL> /
I am in 1 exception block
I am in 2 exception block
The OTHERS Exception Handler:
SQL> DECLARE
2 e_TooManyEmployee EXCEPTION; -- Exception to indicate an error condition
3 BEGIN
4 RAISE e_TooManyEmployee;
5 EXCEPTION
6 WHEN e_TooManyEmployee THEN
7 DBMS_OUTPUT.put_line('e_TooManyEmployee');
8 WHEN OTHERS THEN
9 DBMS_OUTPUT.put_line('OTHERS');
10 END;
11 /
e_TooManyEmployee
PL/SQL procedure successfully completed.
create or replace function f_get_speed
(i_distance in number
,i_time in number)
return number
is
v_out number;
begin
v_out:=i_distance/i_time;
return v_out;
exception
when zero_divide then
return 0;
end;
SQL> select f_get_speed(2,2)
2 from dual;
F_GET_SPEED(2,2)
----------------
1
SQL> select f_get_speed(2,0)
2 from dual;
F_GET_SPEED(2,0)
----------------
0
Raising own exception:
Users can explicitly raise an exception with the RAISE command.
Steps for trapping a user-defined error include the following:
Declare the name for the user exception within the declaration section of the bl
ock.
Raise the exception explicitly within the executable portion of the block using
the RAISE command.
Reference the declared exception with an error-handling routine.

1 DECLARE
2 rohit_exception EXCEPTION;
3 PRAGMA EXCEPTION_INIT(rohit_exception, -20001);
4 BEGIN
5 raise rohit_exception;
6 EXCEPTION
7 WHEN rohit_exception then
8 DBMS_OUTPUT.put_line('sqlcode:'||' '||sqlcode);
9 DBMS_OUTPUT.put_line('sqlerrm:'||' '||sqlerrm);
10* END;
11 /
sqlcode: -20001
sqlerrm: ORA-20001:
Using RAISE_APPLICATION_ERROR:
CREATE OR REPLACE PROCEDURE Register is
2 BEGIN
3 RAISE_APPLICATION_ERROR(-20000, 'Can''t add more Employee');
4
5 EXCEPTION
6 WHEN NO_DATA_FOUND THEN
7 RAISE_APPLICATION_ERROR(-20001, ' doesn''t exist!');
8 END Register;
9 /
DECLARE
v_descr VARCHAR2(20);
BEGIN
BEGIN
SELECT sal
INTO v_descr
FROM emp
WHERE empno =12;
dbms_output.put_line(v_descr);
EXCEPTION WHEN NO_DATA_FOUND THEN
Raise_application_error(-20099, 'I am in 1 exception block');
END;
BEGIN
SELECT sal
INTO v_descr
FROM emp
WHERE empno =1;
dbms_output.put_line(v_descr);
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('I am in 2 exception block');
END;
/* EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('ERR:An error occurred with info :'||
TO_CHAR(SQLCODE)||' '||SQLERRM);
*/END;
/
DECLARE
*
ERROR at line 1:
ORA-20099: I am in 1 exception block
ORA-06512: at line 11

DECLARE
v_descr VARCHAR2(20);
BEGIN
BEGIN
SELECT sal
INTO v_descr
FROM emp
WHERE empno =12;
dbms_output.put_line(v_descr);
EXCEPTION WHEN NO_DATA_FOUND THEN
Raise_application_error(-20099, 'I am in 1 exception block');
END;
BEGIN
SELECT sal
INTO v_descr
FROM emp
WHERE empno =1;
dbms_output.put_line(v_descr);
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('I am in 2 exception block');
END;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('ERR:An error occurred with info :'||
TO_CHAR(SQLCODE)||' '||SQLERRM);
END;
/
ERR:An error occurred with info :-20099 ORA-20099: I am in 1 exception block
PL/SQL procedure successfully completed.

CREATE OR REPLACE PROCEDURE Register is


BEGIN
RAISE_APPLICATION_ERROR(-20000, 'Can''t add more Employee');
exception
when others
then
dbms_output.put_line(sqlcode||' '||sqlerrm);
end;
/
SQL> call register();
-20000 ORA-20000: Can't add more Employee
Call completed.
1 CREATE OR REPLACE PROCEDURE Register is
2 BEGIN
3 RAISE_APPLICATION_ERROR(-20000, 'Can''t add more Employee');
4* end;
SQL> /
Procedure created.
SQL> call register();
call register()
*
ERROR at line 1:
ORA-20000: Can't add more Employee
ORA-06512: at "SCOTT.REGISTER", line 3

declare
e_increaseTooLarge exception;
pragma exception_init(e_increaseTooLarge, -20001);
begin
raise e_increaseTooLarge;
exception
when e_increaseTooLarge then
DBMS_OUTPUT.put_line(sqlcode||' '||sqlerrm);
end;
/
SQL> /
-20001 ORA-20001:
PL/SQL procedure successfully completed.
===============================================================
calling a function or procedure:
SQL> call f_getDateType(null) into :a;
exec :a:=f_getDateType(sysdate);
================================================================
Use SQL%NOTFOUND after insert statement
SQL> DECLARE
2 quant NUMBER := 20;
3 BEGIN
4 INSERT INTO employee (salary)
5 VALUES (quant);
6 IF (SQL%NOTFOUND)
7 THEN
8 dbms_output.put_line('Insert error?!');
9 END IF;
10 END;
11 /
SQL> BEGIN
2 DELETE FROM employee
3 WHERE first_name = 'junk';
4 IF (SQL%NOTFOUND)
5 THEN
6 dbms_output.put_line('No such employee');
7 END IF;
8 END;
9 /
No such employee
================================================================
A record is a collection of individual fields that represents a row in a table.
These fields are unique and each has its own values.
By using records, you can group like data into one structure and then manipulate
this structure as one entity or logical unit.
You declare a record in the declaration portion of a PL/SQL block, subprogram, o
r package
DECLARE
2 TYPE hrc_company_rec IS RECORD
3 (hrc_company_id NUMBER,
4 product_description VARCHAR2(20),
5 company_short_name VARCHAR2(30));
6 v_example_rec1 hrc_company_rec;
7 v_example_rec2 hrc_company_rec;
8 BEGIN
9 v_example_rec1.hrc_company_id :=1001;
10
11 v_example_rec1.product_description :='C';
12
13 v_example_rec1.company_short_name :='O Inc.';
14
15 v_example_rec2 :=v_example_rec1;
16
17 dbms_output.put_line(to_number(v_example_rec2.hrc_company_id)||' '||
18 v_example_rec2.product_description||' '||
19
20 v_example_rec2.company_short_name);
21 END;
22 /

declare
2 type emp_ty is record (emp_tx VARCHAR2(256),deptNo employee.id%TYPE);
3 v_emp_rty emp_ty;
4 begin
5 select id ||' '||first_Name, id into v_emp_rty from employee where id=
'01';
6 DBMS_OUTPUT.put_line('Emp:'||v_emp_rty.emp_tx||'('||v_emp_rty.deptno||'
)');
7 end;
8 /
declare
2 r_emp employee%ROWTYPE;
3 begin
4 select * into r_emp from employee where id = '01';
5 DBMS_OUTPUT.put_line(r_emp.first_Name);
6 end;
===============================================================
create or replace procedure p_log_audit
2 (what_tx VARCHAR2, descr_tx VARCHAR2,
3 who_tx VARCHAR2, when_dt DATE)
4 is
5 pragma autonomous_transaction;
6 begin
7 insert into Audit_emp
8 values(audit_seq.nextval, what_tx, descr_tx,
9 who_tx, when_dt);
10 commit;
11 end;
12 /
===============================================================
You may follow five steps when using a cursor:
Declare variables to store the column values from the SELECT statement.
Declare the cursor, specifying your SELECT statement.
Open the cursor.
Fetch the rows from the cursor.
Close the cursor.
SQL> DECLARE
2 -- step 1: declare the variables
3 v_id employee. id%TYPE;
4 v_name employee.first_name%TYPE;
5 v_salary employee.salary%TYPE;
6
7 -- step 2: declare the cursor
8 CURSOR cv_employee_cursor IS
9 SELECT id, first_name, salary
10 FROM employee
11 ORDER BY id;
12
13 BEGIN
14
15 -- step 3: open the cursor
16 OPEN cv_employee_cursor;
17
18 LOOP
19
20 -- step 4: fetch the rows from the cursor
21 FETCH cv_employee_cursor
22 INTO v_id, v_name, v_salary;
23
24 -- exit the loop when there are no more rows, as indicated by
25 -- the Boolean variable NOTFOUND (= true when
26 -- there are no more rows)
27 EXIT WHEN cv_employee_cursor%NOTFOUND;
28 -- use DBMS_OUTPUT.PUT_LINE() to display the variables
29 DBMS_OUTPUT.PUT_LINE(
30 'v_id = ' || v_id || ', v_name = ' || v_name ||
31 ', v_salary = ' || v_salary
32 );
33
34 END LOOP;
35
36 -- step 5: close the cursor
37 CLOSE cv_employee_cursor;
38
39 END;
40 /
SQL> DECLARE
2 CURSOR product_cur IS
3 SELECT * FROM product
4 FOR UPDATE OF product_price;
5 BEGIN
6 FOR product_rec IN product_cur
7 LOOP
8 UPDATE product
9 SET product_price = (product_rec.product_price * 0.97)
10 WHERE CURRENT OF product_cur;
11 END LOOP;
12 END;
13 /
Note:
select *
from emp
where ename = 'TOM'
for update of sal
locks only one row where ename = 'TOM'
select *
from emp
for update of sal;
locks all the rows
declare
cursor c is select /*+ FIRST_ROWS(1) */ * from emp order by empno;
l_rec emp%rowtype;
begin
open c;
loop
fetch c into l_rec;
exit when c%notfound;
dbms_output.put_line(l_rec.empno);
end loop;
close c;
end;

solution for locking :


DECLARE
2 waitsecs CONSTANT PLS_INTEGER := 10;
3
4 CURSOR emp_cur IS SELECT ename, rowid FROM emp FOR UPDATE NOWAIT;
5 emp_rec emp_cur%ROWTYPE;
6
7 resource_busy EXCEPTION;
8 PRAGMA EXCEPTION_INIT (resource_busy, -54);
9
10 starttime PLS_INTEGER;
11 BEGIN
12 starttime := DBMS_UTILITY.GET_TIME;
13 LOOP
14 BEGIN
15 OPEN emp_cur;
16 EXIT;
17 EXCEPTION
18 WHEN resource_busy
19 THEN
20 IF DBMS_UTILITY.GET_TIME - starttime < waitsecs
21 THEN
22 DBMS_LOCK.SLEEP (1);
23 ELSE
24 RAISE;
25 END IF;
26 END;
27 END LOOP;
28
29 LOOP
30 FETCH emp_cur INTO emp_rec;
31 EXIT WHEN emp_cur%NOTFOUND;
32 UPDATE emp SET sal = sal + 1000 WHERE ROWID = emp_rec.rowid;
33 END LOOP;
34
35 CLOSE emp_cur;
36 COMMIT;
37 END;
38 /

if loop is there c will fetch all the rows.


declare
cursor c is select /*+ FIRST_ROWS(1) */ * from emp order by empno;
l_rec emp%rowtype;
begin
open c;
-- loop
fetch c into l_rec;
-- exit when c%notfound;
dbms_output.put_line(l_rec.empno);
-- end loop;
close c;
end;
/
Above will fetch only one row.

cursor example:
--------------
declare
type ridArray is table of rowid index by binary_integer;
type vcArray is table of emp.ename%type index by binary_integer;
l_rids ridArray;
l_names vcArray;
cursor c is select rowid, ename from emp;
begin
open c;
loop
fetch c bulk collect into l_rids, l_names LIMIT 100;
forall i in 1 .. l_rids.count
update emp set ename = upper(l_names(i)) where rowid = l_rids(i);
commit;
exit when c%notfound;
end loop;
close c;
end;
/
declare
type ridArray is table of rowid index by binary_integer;
type vcArray is table of emp.ename%type index by binary_integer;
l_rids ridArray;
l_names vcArray;
cursor c is select rowid, ename from emp;
begin
open c;
loop
fetch c bulk collect into l_rids, l_names LIMIT 100;
forall i in 1 .. l_rids.count
update emp set ename = upper(l_names(i)) where rowid = l_rids(i)
commit;
exit when c%notfound;
end loop;
close c;
end;
/
declare
ridarray rowid ;
vcarray emp.ename%type;
cursor c is select rowid, ename from emp;
begin
open c;
loop
fetch c into ridarray, vcarray;
update emp set ename = upper(vcarray) where rowid = ridarray;
commit;
exit when c%notfound;
end loop;
close c;
end;
/

result is same for above three.


Cursor involving varray:
DECLARE
2 CURSOR all_emps IS
3 SELECT *
4 FROM employee
5 ORDER BY first_name;
6
7 TYPE emp_array IS VARRAY(100) OF employee%ROWTYPE;
8
9 emps emp_array;
10 inx1 PLS_INTEGER;
11 inx2 PLS_INTEGER;
12 BEGIN
13 inx1 := 0;
14
15 emps := emp_array ();
16
17 FOR emp IN all_emps LOOP
18 inx1 := inx1 + 1;
19 emps.extend();
20 emps(inx1).id := emp.id;
21 emps(inx1).first_name := emp.first_name;
22 emps(inx1).salary := emp.salary;
23 END LOOP;
24
25 FOR inx2 IN 1..emps.count LOOP
26 DBMS_OUTPUT.PUT_LINE (emps(inx2).id ||' ' || emps(inx2).first_name)
;
27 END LOOP;
28 END;
29 /

DECLARE
2
3 v_employeeID employee.id%TYPE;
4 v_FirstName employee.first_name%TYPE;
5 v_LastName employee.last_name%TYPE;
6
7
8 v_city employee.city%TYPE := 'Vancouver';
9
10
11 CURSOR c_employee IS
12 SELECT id, first_name, last_name FROM employee WHERE city = v_city;
13 BEGIN
14
15 OPEN c_employee;
17 LOOP
18
19 FETCH c_employee INTO v_employeeID, v_FirstName, v_LastName;
20 DBMS_OUTPUT.put_line(v_employeeID);
21 DBMS_OUTPUT.put_line(v_FirstName);
22 DBMS_OUTPUT.put_line(v_LastName);
23
24 EXIT WHEN c_employee%NOTFOUND;
25 END LOOP;
26
27
28 CLOSE c_employee;
29 END;
30 /

declare
2 cursor c_empInDept is select * from employee for update of salary;
3 begin
4 for r_emp in c_empInDept loop
5 if r_emp.salary < 5000 then
6 update employee
7 set salary = salary * 1.1
8 where current of c_empInDept;
9 end if;
10 end loop;
11 end;
12 /
Note:
where current of <cursor name> targets only current condition or current value
in cursor and also set locks at only current condition.
CURSOR c_Registeredemployee IS
SELECT *
FROM emp
WHERE empno IN ('10')
for update of sal;
................
................
UPDATE emp
SET sal = sal + v_salary
where current of c_Registeredemployee;
This will target on sal for emp 01.
if we are using where current of we have to use for update
PLS-00404: cursor 'C_REGISTEREDEMPLOYEE' must be declared with FOR UPDATE to
use with CURRENT OF

Code can work even if cursor is not closed at all.


e.g.
DECLARE
CURSOR c_Allemployee IS
SELECT *
FROM emp
FOR UPDATE;
v_employeeInfo c_Allemployee%ROWTYPE;
BEGIN
OPEN c_Allemployee;
FETCH c_Allemployee INTO v_employeeInfo;
-- Issue a COMMIT. This will release the locks, invalidating the
-- cursor.
-- COMMIT WORK;
-- This FETCH will raise the ORA-1002 error.
dbms_output.put_line(v_employeeinfo.ename);
-- close c_allemployee;
-- OPEN c_Allemployee;
FETCH c_Allemployee INTO v_employeeInfo;
-- close c_allemployee;
dbms_output.put_line(v_employeeinfo.ename);
END;
/
Also if we are trying to open a cursor which is alredy open we will get the erro
r:
DECLARE
CURSOR c_Allemployee IS
SELECT *
FROM emp
FOR UPDATE;
v_employeeInfo c_Allemployee%ROWTYPE;
BEGIN
OPEN c_Allemployee;
FETCH c_Allemployee INTO v_employeeInfo;
-- Issue a COMMIT. This will release the locks, invalidating the
-- cursor.
-- COMMIT WORK;
-- This FETCH will raise the ORA-1002 error.
dbms_output.put_line(v_employeeinfo.ename);
-- close c_allemployee;
OPEN c_Allemployee;
FETCH c_Allemployee INTO v_employeeInfo;
-- close c_allemployee;
dbms_output.put_line(v_employeeinfo.ename);
END;
/
ERROR at line 1:
ORA-06511: PL/SQL: cursor already open
ORA-06512: at line 3
ORA-06512: at line 16
If we try to fetch from a cursor which is not open then;
DECLARE
CURSOR c_Allemployee IS
SELECT *
FROM emp
FOR UPDATE;
v_employeeInfo c_Allemployee%ROWTYPE;
BEGIN
-- OPEN c_Allemployee;
FETCH c_Allemployee INTO v_employeeInfo;
-- Issue a COMMIT. This will release the locks, invalidating the
-- cursor.
-- COMMIT WORK;
-- This FETCH will raise the ORA-1002 error.
dbms_output.put_line(v_employeeinfo.ename);
-- close c_allemployee;
-- OPEN c_Allemployee;
FETCH c_Allemployee INTO v_employeeInfo;
close c_allemployee;
dbms_output.put_line(v_employeeinfo.ename);
END;
/
DECLARE
*
ERROR at line 1:
ORA-01001: invalid cursor
ORA-06512: at line 9
Fetching Across Commits:
DECLARE
CURSOR c_Allemployee IS
SELECT *
FROM emp
FOR UPDATE;
v_employeeInfo c_Allemployee%ROWTYPE;
BEGIN
OPEN c_Allemployee;
FETCH c_Allemployee INTO v_employeeInfo;
-- Issue a COMMIT. This will release the locks, invalidating the
-- cursor.
COMMIT ;
-- This FETCH will raise the ORA-1002 error.
dbms_output.put_line(v_employeeinfo.ename);
-- close c_allemployee;
-- OPEN c_Allemployee;
FETCH c_Allemployee INTO v_employeeInfo;
close c_allemployee;
dbms_output.put_line(v_employeeinfo.ename);
END;
/
ROHIT
DECLARE
*
ERROR at line 1:
ORA-01002: fetch out of sequence
ORA-06512: at line 17
Note: If we are using commit in loop with for update statement then it will give
ORA-01002: fetch out of sequence
if we are not using for update or commit statement then it will not give any err
or.
Using records:
SQL> DECLARE
2 TYPE hrc_company_rec IS RECORD
3 (hrc_company_id NUMBER,
4 product_description VARCHAR2(20),
5 company_short_name VARCHAR2(30));
6 v_example_rec hrc_company_rec;
7 CURSOR csr_hrc_org IS
8 SELECT 1,h.product_description,o.company_short_name
9 FROM company o,product h
10 WHERE o.product_id =h.product_id
11 AND h.product_id =1;
12 BEGIN
13 OPEN csr_hrc_org;
14 LOOP
15 FETCH csr_hrc_org INTO v_example_rec;
16 EXIT WHEN csr_hrc_org%NOTFOUND;
17 dbms_output.put_line(to_number(v_example_rec.hrc_company_id)||' '||
18 v_example_rec.product_description||' '||
19 v_example_rec.company_short_name);
20 END LOOP;
21 CLOSE csr_hrc_org;
22 END;
23 /
1 Java A Inc.
1 Java B Inc.
1 Java C Inc.

declare
type ridArray is table of rowid index by binary_integer;
type vcArray is table of emp.ename%type index by binary_integer;
l_rids ridArray;
l_names vcArray;
cursor c is select rowid, ename from emp;
begin
open c;
loop
fetch c bulk collect into l_rids, l_names;-- LIMIT 2;
dbms_output.put_line('======'||l_rids.count);
dbms_output.put_line('======'||l_rids.first);
dbms_output.put_line('======'||l_rids.last);
exit when l_rids.count=0;
end loop;
close c;
end;
we can also use exit when l_rids.count=0; as closing condition.
======15
======1
======15
======0
======
======
declare
type ridArray is table of rowid index by binary_integer;
type vcArray is table of emp.ename%type index by binary_integer;
l_rids ridArray;
l_names vcArray;
cursor c is select rowid, ename from emp;
begin
open c;
loop
fetch c bulk collect into l_rids, l_names LIMIT 2;
dbms_output.put_line('======'||l_rids.count);
dbms_output.put_line('======'||l_rids.first);
dbms_output.put_line('======'||l_rids.last);
exit when l_rids.count=0;
end loop;
close c;
end;
======2
======1
======2
======2
======1
======2
======2
======1
======2
======2
======1
======2
======2
======1
======2
======2
======1
======2
======2
======1
======2
======1
======1
======1
======0
======
======
Limit limits the number of rows being fetch.
Raise no data found exception if cursor is empty:
DECLARE
2 d VARCHAR2(1);
3 no_data_found EXCEPTION;
4
5 CURSOR myCursor IS SELECT dummy FROM dual WHERE 1=2;
6 BEGIN
7 OPEN myCursor;
8 FETCH myCursor INTO d;
9
10 IF d IS NULL
11 THEN
12 RAISE no_data_found;
13 END IF;
14 EXCEPTION
15 WHEN no_data_found
16 THEN
17 DBMS_OUTPUT.PUT_LINE ('Trapped the error!?');
18 END;
19 /
Trapped the error!?
Nested cursor open:
-----------------
SQL> DECLARE
2 CURSOR empCursor IS
3 SELECT *
4 FROM employee;
5 CURSOR cur_department
6 (p_department_num department.department_id%TYPE) IS
7 SELECT department_name
8 FROM department
9 where department_id = p_department_num;
10 lv_department_txt department.department_name%TYPE;
11 BEGIN
12 FOR empCursor_rec IN empCursor LOOP
13 OPEN cur_department(empCursor_rec.department_id);
14 FETCH cur_department INTO lv_department_txt;
15 CLOSE cur_department;
16 END LOOP;
17 END;
18 /
SQL> DECLARE
2 CURSOR c_employee IS
3 SELECT id, first_name, last_name
4 FROM employee
5 WHERE id = '01';
6
7 v_employeeData c_employee%ROWTYPE;
8 BEGIN
9 -- Open the cursor and initialize the active set
10 OPEN c_employee;
11
12 -- Retrieve the first row, to set up for the WHILE loop
13 FETCH c_employee INTO v_employeeData;
14
15 -- Continue looping while there are more rows to fetch
16 WHILE c_employee%FOUND LOOP
17 DBMS_OUTPUT.put_line(v_employeeData.ID);
18 FETCH c_employee INTO v_employeeData;
19 END LOOP;
20
21 -- Free resources used by the cursor
22 CLOSE c_employee;
23
24 -- Commit our work
25 COMMIT;
26 END;
27 /
01
SQL> create or replace function f_delete_nr (i_tab_tx VARCHAR2)
2 return NUMBER
3 is
4 begin
5 execute immediate 'delete from '||i_tab_tx;
6 return SQL%ROWCOUNT;
7 end;
8 /
SQL> begin
2 DBMS_OUTPUT.put_line('Deleted:'||f_delete_nr('EMPLOYEE'));
3 end;
4 /
Deleted:8

Accessing Status Info by Using Cursor Variables:


All cursors have properties that report their state of operation.
%FOUND checks whether a fetch succeeded in bringing a record into a variable.
%FOUND returns TRUE if the fetch succeeded, FALSE otherwise.
%NOTFOUND the reverse of %FOUND.
%NOTFOUND returns FALSE if the fetch succeeded, TRUE otherwise.
%ISOPEN checks whether a cursor is open.
%ROWCOUNT returns the number of rows processed by a cursor at the time the %ROWC
OUNT statement is executed.
The variable %ROWCOUNT is a regular number variable.
The first three are Boolean variables that return a logical TRUE or FALSE.
If you use the %FOUND, %NOTFOUND, and %ROWCOUNT cursor variables before the curs
or is opened or after the cursor is closed, they will raise an exception.
Values of %FOUND, %NOTFOUND, and %ROWCOUNT are changed after every fetch.
If there are no more rows to fetch, %ROWCOUNT keeps the number of successfully f
etched records until the cursor is closed.
Declare
cursor c_emp (cin_No NUMBER) is
select ename from emp where empno=cin_No;
v_eName VARCHAR2(256);
begin
if not c_emp%ISOPEN then
DBMS_OUTPUT.put_line('Cursor is closed');
end if;
open c_emp(10);
if c_emp%ISOPEN then
DBMS_OUTPUT.put_line('Cursor is opened');
end if;
loop
fetch c_emp into v_eName;
if c_emp%NOTFOUND then
DBMS_OUTPUT.put_line('No rows to fetch!');
exit; -- the same as exit when c1%NOTFOUND;
end if;
DBMS_OUTPUT.put_line('Processed:'||c_emp%rowcount);
end loop;
DBMS_OUTPUT.put_line('Before close of c_emp!');
close c_emp;
if not c_emp%ISOPEN then
DBMS_OUTPUT.put_line('Cursor is closed');
end if;
end;
/
Cursor is closed
Cursor is opened
Processed:1
No rows to fetch!
Before close of c_emp!
Cursor is closed
Checking the status of implicit cursors:
You can use the same cursor variables for implicit cursors.
The value of a cursor variable corresponds to the last statement needing an impl
icit cursor.
Because there is no cursor name, you use SQL rather than the cursor name.
SQL%ROWCOUNT returns row count.
SQL%ISOPEN is always false because implicit cursors are opened as needed and clo
sed immediately after the statement is finished.
Both SQL%FOUND and SQL%NOTFOUND are false before any statement is executed.
Any DDL or transaction control commands (commit or rollback) will clear implicit
cursor variables.
You can check the value of your cursor variables only prior to doing a commit or
rollback.
begin
update emp
set sal=sal*1;
DBMS_OUTPUT.put_line('Processed:'||sql%rowcount);
if sql%FOUND then
DBMS_OUTPUT.put_line('Found=true');
else
DBMS_OUTPUT.put_line('Found=false');
end if;
end;
/
Processed:15
Found=false
create or replace function f_delete_nr (i_tab_tx VARCHAR2)
return NUMBER
is
begin
execute immediate 'delete from '||i_tab_tx;
return SQL%ROWCOUNT;
end;
begin
DBMS_OUTPUT.put_line('Deleted:'||f_delete_nr('EMP'));
end;
/
Deleted:15
%ISOPEN, %FOUND, %NOTFOUND variables aren't useful at all in CURSOR FOR loops
The CURSOR FOR loop is always closed before and after the loop and open inside t
he loop.
There is no reason to ever use %ISOPEN.
Inside the loop, records are always found, so %FOUND is always true inside the l
oop.
Outside the loop, %FOUND, %NOT FOUND would return an error.
declare
cursor c is
select *
from emp;
var1 integer;
begin
for cur1 in c loop
dbms_output.put_line(cur1.empno || ' is a value in EMPLOYEE');
var1:=c%rowcount;
end loop;
dbms_output.put_line(var1);
end;
/
SQL> /
10 is a value in EMPLOYEE
7369 is a value in EMPLOYEE
7499 is a value in EMPLOYEE
7521 is a value in EMPLOYEE
7566 is a value in EMPLOYEE
7654 is a value in EMPLOYEE
7698 is a value in EMPLOYEE
7782 is a value in EMPLOYEE
7788 is a value in EMPLOYEE
7839 is a value in EMPLOYEE
7844 is a value in EMPLOYEE
7876 is a value in EMPLOYEE
7900 is a value in EMPLOYEE
7902 is a value in EMPLOYEE
7934 is a value in EMPLOYEE
15
declare
cursor c is
select *
from emp;
begin
for cur1 in c loop
dbms_output.put_line(cur1.empno || ' is a value in EMPLOYEE');
end loop;
dbms_output.put_line(c%rowcount);
end;
SQL> /
10 is a value in EMPLOYEE
7369 is a value in EMPLOYEE
7499 is a value in EMPLOYEE
7521 is a value in EMPLOYEE
7566 is a value in EMPLOYEE
7654 is a value in EMPLOYEE
7698 is a value in EMPLOYEE
7782 is a value in EMPLOYEE
7788 is a value in EMPLOYEE
7839 is a value in EMPLOYEE
7844 is a value in EMPLOYEE
7876 is a value in EMPLOYEE
7900 is a value in EMPLOYEE
7902 is a value in EMPLOYEE
7934 is a value in EMPLOYEE
declare
*
ERROR at line 1:
ORA-01001: invalid cursor
ORA-06512: at line 9

Handling exceptions in implicit cursors:


SQL> create or replace function f_getdName (in_No VARCHAR)
2 return VARCHAR2 is v_out employee.first_Name%TYPE;
3 begin
4 select first_Name into v_out
5 from employee
6 where id = in_No;
7 return v_out;
8 exception
9 when no_data_found then
10 return 'NO SUCH DEPARTMENT';
11 end f_getdName;
12 /
Implicit cursor and Explicit cursor:
SQL> -- Explicit cursor
SQL>
SQL> create or replace function f_empExp_dsp (i_empNo VARCHAR)
2 return VARCHAR2 is
3 v_out_tx VARCHAR2(2000);
4 cursor c_emp (ci_empNo VARCHAR) is
5 select id||' '||first_Name||' ('|| city ||')' emp_dsp
6 from employee
7 where id=ci_empNo;
8
9 v_emp_rec c_emp%ROWTYPE;
10 begin
11 open c_emp(i_empNo);
12 fetch c_emp into v_emp_rec;
13 close c_emp;
14 return v_emp_rec.emp_dsp;
15 exception
16 when others then
17 if c_emp%ISOPEN then
18 close c_emp;
19 end if;
20 return null;
21 end f_empExp_dsp;
22 /
Function created.
SQL>
SQL> -- Implicit cursor
SQL> create or replace function f_empImp_dsp(in_empNo VARCHAR)
2 return VARCHAR2 is
3 v_out_tx VARCHAR2(2000);
4 begin
5 select id||' '||first_Name||' ('||city||')' into v_out_tx
6 from employee
7 where id=in_empNo;
8 return v_out_tx;
9 exception
10 when no_data_found then
11 return null;
12 when too_many_rows then
13 return '<Error>';
14 end f_empImp_dsp;
15 /
Function created.
Explicit cursors do not raise the exceptions NO_DATA_FOUND and TOO_MANY_ROWS.
To store a fetched value, you can reference an explicit cursor with %ROWTYPE.
SQL> declare
2 cursor c_emp (cin_No NUMBER) is select count(*) from employee where id
= cin_No;
3 v_deptNo employee.id%type:=10;
4 v_countEmp NUMBER;
5 begin
6 open c_emp (v_deptNo);
7 fetch c_emp into v_countEmp;
8 close c_emp;
9 end;
10 /
Cursor Functions:
SQL> select
2 dept.deptno, dept.dname,
3 cursor(select empno from emp where deptno = dept.deptno),
4 cursor(select fy, amount from dept_fy_budget where deptno = dept.deptno
)
5 from dept
6 where deptno = 10
7 /

The REF CURSOR datatype cannot be used outside a PL/SQL environment.


There are two kinds of REF CURSOR types: weak and strong.
A weak REF CURSOR can point to any data set, as shown here:
declare
type weak_rcty is ref cursor;
c_weak_rcty weak_rcty;
declare
c_weak sys_refcursor;
A strong REF CURSOR explicitly declares the type of data that can be referenced.
In the following example, only queries that return rows exactly as in the EMPLOY
EE table are allowed:
SQL> variable x refcursor
declare
r emp%rowtype;
begin
open :x for select *
from emp;
loop
fetch :x into r;
exit when :x%notfound;
dbms_output.put_line(r.empno);
end loop;
close :x;
end;
/
SQL> create or replace function emp_list return sys_refcursor is
2 rc sys_refcursor;
3 begin
4 open rc for select * from emp;
5 return rc;
6 end;
7 /
Function created.
SQL>
SQL> create or replace procedure list_emps is
2 e sys_refcursor;
3 r emp%rowtype;
4 begin
5 e := emp_list;
6 loop
7 fetch e into r;
8 exit when e%notfound;
9 dbms_output.put_line(r.empno||','||r.hiredate);
10 end loop;
11 close e;
12 end;
13 /
Procedure created.
SQL> CREATE OR REPLACE PROCEDURE p_print_report(p_report_no NUMBER,p_title VARCH
AR2)
2 IS
3 TYPE rc IS REF CURSOR;
4 refCursorValue rc;
5 v_product_description VARCHAR2(20);
6 v_company_short_name VARCHAR2(30);
7 BEGIN
8 IF (p_report_no =1)THEN
9 OPEN refCursorValue FOR SELECT h.product_description,o.company_short_na
me
10 FROM company o,product h
11 WHERE o.product_id =h.product_id
12 AND 1 < (SELECT count(os.site_no)
13 FROM org_company_site os
14 WHERE os.company_id =o.company_id);
15 ELSIF (p_report_no =2)THEN
16 OPEN refCursorValue FOR SELECT h.product_description,o.company_short_na
me
17 FROM company o,product h
18 WHERE o.product_id =h.product_id
19 AND NOT EXISTS
20 (SELECT *
21 FROM company o1
22 WHERE o1.company_id =o.company_id
23 AND o1.product_id =2 );
24 END IF;
25 dbms_output.put_line(p_title);
26 dbms_output.put_line(rpad('-',length(p_title),'-'));
27 dbms_output.put_line(rpad('Hierarchy',20,' ')||' '||rpad('Description',30
,' '));
28 dbms_output.put_line(rpad('-',20,'-')||' '||rpad('-',30,'-'));
29 LOOP
30 FETCH refCursorValue INTO v_product_description,v_company_short_name;
31 EXIT WHEN refCursorValue%NOTFOUND;
32 dbms_output.put_line(rpad(v_product_description,20,' ')||' '||
33 rpad(v_company_short_name,30,' '));
34 END LOOP;
35 CLOSE refCursorValue;
36 END p_print_report;
37 /
SQL> CREATE OR REPLACE PROCEDURE showemps (where_in IN VARCHAR2 := NULL)
2 IS
3 TYPE cv_typ IS REF CURSOR;
4 cv cv_typ;
5 v_nm emp.ename%TYPE;
6 BEGIN
7 OPEN cv FOR
8 'SELECT ename FROM emp WHERE ' || NVL (where_in, '1=1');
9 LOOP
10 FETCH cv INTO v_nm;
11 EXIT WHEN cv%NOTFOUND;
12 DBMS_OUTPUT.PUT_LINE (v_nm);
13 END LOOP;
14 CLOSE cv;
15 END;
16 /

declare
type strong_rcty is ref cursor return employee%ROWTYPE;
c_strong_rcty strong_rcty;
DECLARE
2
3 cv_emp SYS_REFCURSOR;
4 v_title BOOKS.TITLE%TYPE;
5 v_emp emp%ROWTYPE;
6 v_counter PLS_INTEGER := 0;
7
8 CURSOR book_cur IS SELECT b.title,CURSOR (SELECT * FROM emp a WHERE a.id
= b.emp1 OR a.id = b.emp2 OR a.id = b.emp3)
9 FROM books b
10 WHERE isbn = '1';
11
12 BEGIN
13
14 DBMS_OUTPUT.ENABLE(1000000);
15
16 OPEN book_cur;
17
18 LOOP
19 FETCH book_cur INTO v_title, cv_emp;
20 EXIT WHEN book_cur%NOTFOUND;
21
22 v_counter := 0;
23
24 DBMS_OUTPUT.PUT_LINE('Title from the main cursor: '||v_title);
25
26 LOOP
27 FETCH cv_emp INTO v_emp;
28 EXIT WHEN cv_emp%NOTFOUND;
29
30 v_counter := v_counter + 1;
31
32 DBMS_OUTPUT.PUT_LINE('emp'||v_counter||': '||v_emp.fname||' '||v_e
mp.lname);
33 END LOOP;
34 END LOOP;
35
36 CLOSE book_cur;
37
38 END;
39 /
CREATE OR REPLACE PROCEDURE authors_sel (cv_results IN OUT SYS_REFCURSOR)
2 IS
3 BEGIN
4 OPEN cv_results FOR
5 SELECT id, first_name, last_name
6 FROM employee;
7 END;
8 /
Procedure created.
SQL>
SQL> VARIABLE x REFCURSOR
SQL> EXEC authors_sel(:x)

SQL> create table t


2 as
3 select * from all_users;
Table created.
SQL>
SQL> variable x refcursor
SQL>
SQL> begin
2 open :x for select * from t;
3 end;
4 /
PL/SQL procedure successfully completed.
SQL>
SQL> delete from t;
17 rows deleted.
SQL>
SQL> commit;
Commit complete.
SQL>
SQL> print x

SQL> declare
2 c sys_refcursor;
3 type array is table of big_table%rowtype index by binary_integer;
4 l_rec array;
5 begin
6 for i in 1 .. 5
7 loop
8 open c for select * from big_table ORDER BY OWNER;
9 fetch c bulk collect into l_rec limit i*1000;
10 close c;
11 end loop;
12 end;
13 /

SQL> DECLARE
2
3 TYPE book_typ IS REF CURSOR RETURN book%ROWTYPE;
4 cv_book book_typ;
5 v_book book%ROWTYPE;
6
7 BEGIN
8
9 DBMS_OUTPUT.ENABLE(1000000);
10
11 OPEN cv_book FOR SELECT * FROM book WHERE isbn = '1';
12
13 FETCH cv_book INTO v_book;
14
15 DBMS_OUTPUT.PUT_LINE(v_book.title||' is '||v_book.price);
16
17 CLOSE cv_book;
18 END;
19 /

Oracle REF CURSOR


With the REF_CURSOR you can return a recordset/cursor from a stored procedure.
There are 2 basic types: Strong ref cursor and weak ref cursor
For the strong ref cursor the returning columns with datatype and length need to
be known at compile time.
For the weak ref cursor the structure does not need to be known at compile time.
The STRONG_REF_CURSOR and until Oracle 9i also the weak-type need to be declared
in a package structure lik this:
create or replace package REFCURSOR_PKG as
TYPE WEAK8i_REF_CURSOR IS REF CURSOR;
TYPE STRONG_REF_CURSOR IS REF CURSOR RETURN EMP%ROWTYPE;
end REFCURSOR_PKG;
The pl/sql procedure that returns a ref-cursor looks like this:
/** until Oracle 9 */
create or replace procedure test( p_deptno IN number
, p_cursor OUT
REFCURSOR_PKG.WEAK8i_REF_CURSOR)
is
begin
open p_cursor FOR
select *
from emp
where deptno = p_deptno;
end test;

Since Oracle 9i you can use SYS_REFCURSOR as the type for the returning REF_CURS
OR.
/** From Oracle 9 */
create or replace procedure test( p_deptno IN number
, p_cursor OUT SYS_REFCURSOR)
is
begin
open p_cursor FOR
select *
from emp
where deptno = p_deptno;
end test;

/* Strong type */
create or replace procedure test( p_deptno IN number
, p_cursor OUT REFCURSOR_PKG.STRONG
REF_CURSOR)
is
begin
open p_cursor FOR
select *
from emp
where deptno = p_deptno;
end test;
===============================================================
Oracle provides the FOR UPDATE clause in SQL syntax to allow the developer to lo
ck a set of Oracle rows for the duration of a transaction.
The FOR UPDATE clause is generally used in cases where an online system needs to
display a set of row data on a screen and
they need to ensure that the data does not change before the end-user has an opp
ortunity to update the data.
However, the FOR UPDATE clause in SQL will cause the SQL to “hang” if one of the req
uested rows is locked by another user.
If you try to access the rows with the NOWAIT clause, you will get an error mess
age, ORA-00054 Resource busy and acquire with NOWAIT specified.
Essentially, the options were either “wait forever” or “don’t wait. Oracle has added ad
ditional flexibility to the syntax by allowing
the SQL to wait for a pre-defined amount of time for locked rows before aborting
.
In this example we select a student row and wait up to 15 seconds for another se
ssion to release their lock:
select
student_last_name
from
student
where student_id = 12345
FOR UPDATE WAIT 15;
===============================================================
There are three types of collections:
Varrays,Nested tables,Associative arrays (formerly known as index-by tables)
A varray is similar to an array in Java.
You can use a varray to store an ordered set of elements having an index associa
ted with it.
The elements in a varray are of the same type.
A varray has one dimension.
A varray has a maximum size that you set when creating it.
Elements in a varray can only be modified as a whole, not individually.
You can change the size of a varray later.
The elements stored in a varray are stored with the table when the size of the v
array is 4KB or less, otherwise the varray is stored outside of the table.
When a varray is stored with the table, accessing its elements is faster than ac
cessing elements in a nested table.
A nested table is a table that is embedded within another table.
You can insert, update, and delete individual elements in a nested table.
Because you can modify individual elements in a nested table, this makes them mo
re flexible than a varray.
A nested table doesn t have a maximum size, and you can store an arbitrary numbe
r of elements in a nested table.
The elements for nested tables are stored in separate tables.
Associative arrays is formerly known as index-by tables.
An associative array is a set of key and value pairs.
You can get the value from the array using the key (which may be a string) or an
integer.
An associative array is similar to a hash table.
You create a collection type using the SQL DDL CREATE TYPE statement.
Then you can use these types to define columns in a table.
An associative array is a PL/SQL construct, not a SQL construct.
An associative array cannot be stored persistently in a table.
You might be asking yourself why you would want to use collections in the first
place.
After all, using two tables with a foreign key already allows you to model relat
ionships between data.
The answer is that the data stored in the collection may be accessed more rapidl
y by the database than if you were to use two tables instead.
Typically, you ll want to use a collection if you have data that is only used by
one table.
A series of collection methods can be used to determine the size, and the rows p
opulated,
in any collection datatype: index-by tables, VARRAYs, and nested tables. The fol
lowing is a list of the collection methods and their purposes:
EXISTS(row) returns TRUE if the row specified exists.
COUNT returns the number of rows.
FIRST returns the row number of the first populated row.
LAST returns the row number of the last populated row.
PRIOR(row) returns the row number of the last row populated before the row speci
fied.
NEXT(row) returns the row number of the next row populated after the row specifi
ed.
DELETE removes all rows.
DELETE(row) removes the specified row.
DELETE(start_row,end_row) removes all rows between and including the start_row a
nd end_row.
TRIM removes the last row.
TRIM(n) removes the last n rows.
EXTEND adds one row.
EXTEND(n) adds n rows.
EXTEND(n,m) adds n copies of row m.

declare
type VarrayType is varray(size) of ElementType;
...
create or replace type VarrayType is varray(size) of ElementType;
declare
2 type month_va is varray(13) of VARCHAR2(20);
3 v_month_va month_va;
4 v_count_nr number;
5 begin
6 v_month_va:=month_va( January , February , March , April , May , June ,
July , August , September , October , November , December );
7 DBMS_OUTPUT.put_line( Length: ||v_month_va.count);
8
9 v_month_va.extend;
10 v_month_va(v_month_va.last):='Null';
11 DBMS_OUTPUT.put_line('Length:'||v_month_va.count);
12
13 for i in v_month_va.first..v_month_va.last
14 loop
15 select count(*) into v_count_nr from employee
16 where nvl(replace(to_char(start_date,'Month'),' '), 'Null')=v_month
_va(i);
17 DBMS_OUTPUT.put_line(v_month_va(i)||': '||v_count_nr);
19 end loop;
20 end;
21 /
SQL> CREATE OR REPLACE TYPE mem_type IS VARRAY(10) of VARCHAR2(15)
2 /
SQL>
SQL>
SQL> CREATE TABLE club (Name VARCHAR2(10),
2 Address VARCHAR2(20),
3 City VARCHAR2(20),
4 Phone VARCHAR2(8),
5 Members mem_type)
6 /
Defining our type to be a VARRAY with 10 elements, where each element is a vary
ing character string of up to 15 characters.
SQL> CREATE OR REPLACE TYPE mem_type IS VARRAY(10) of VARCHAR2(15)
2 /
SQL> DECLARE
2 CURSOR all_emps IS
3 SELECT *
4 FROM employee
5 ORDER BY first_name;
6
7 TYPE emp_array IS VARRAY(100) OF employee%ROWTYPE;
8
9 emps emp_array;
10 inx1 PLS_INTEGER;
11 inx2 PLS_INTEGER;
12 BEGIN
13 inx1 := 0;
14
15 emps := emp_array ();
16
17 FOR emp IN all_emps LOOP
18 inx1 := inx1 + 1;
19 emps.extend();
20 emps(inx1).id := emp.id;
21 emps(inx1).first_name := emp.first_name;
22 emps(inx1).salary := emp.salary;
23 END LOOP;
24
25 FOR inx2 IN 1..emps.count LOOP
26 DBMS_OUTPUT.PUT_LINE (emps(inx2).id ||' ' || emps(inx2).first_name)
;
27 END LOOP;
28 END;
29 /
SQL> CREATE OR REPLACE FUNCTION vs (vlist club.members%type, sub integer)
2 RETURN VARCHAR2
3 IS
4 BEGIN
5 IF sub <= vlist.last THEN
6 RETURN vlist(sub);
7 END IF;
8 RETURN NULL;
9 END vs;
10 /
The CAST function may also be used with the MULTISET function to perform DML ope
rations on VARRAYs.
MULTISET is the "reverse" of CAST in that MULTISET converts a nonobject set of d
ata to an object set. Suppose we create a new table of names:

SQL> CREATE OR REPLACE TYPE mem_type IS VARRAY(10) of VARCHAR2(15)


2 /
Type created.
SQL> CREATE TABLE club (Name VARCHAR2(10),
2 Address VARCHAR2(20),
3 City VARCHAR2(20),
4 Phone VARCHAR2(8),
5 Members mem_type)
6 /
SQL> INSERT INTO club VALUES ('FL','222 Second St.','Orlando',
2 '333-3333', mem_type('Gen','John','Steph','JJ'));
SQL> SELECT COLUMN_VALUE FROM THE (SELECT CAST(c.members as mem_type) FROM club
c
WHERE c.name = 'FL');
CREATE TABLE newnames (n varchar2(20));
SQL> INSERT INTO club VALUES('MD',null, null,null,
2 CAST(MULTISET(SELECT * FROM newnames) as mem_type))
3 /
SELECT n, val
2 FROM
3 (SELECT rownum n, COLUMN_VALUE val FROM
4 THE(SELECT c.members FROM club c
5 WHERE c.name = 'FL') x
6 WHERE COLUMN_VALUE IS NOT NULL);

SELECT VALUE(x) FROM


2 THE(SELECT c.members FROM club c
3 WHERE c.name = 'FL') x
4 WHERE VALUE(x) is not null;
SELECT c.name, p.column_value, COUNT(p.column_value)
2 FROM club c, TABLE(c.members) p
3 -- WHERE c.name = 'AL'
4 GROUP by c.name, p.column_value
Alter size of type:
MYDBA > CREATE TYPE phone_list_typ_demo AS VARRAY(5) OF VARCHAR2(25)
2 /
MYDBA > alter type phone_list_typ_demo modify limit 10;
Type altered.
But note 2 things:
1. These operations seemed rather slow.
2. You can only increase the datatype size.

DECLARE
2 CURSOR all_emps IS
3 SELECT *
4 FROM employee
5 ORDER BY first_name;
6
7 TYPE emp_table IS TABLE OF employee%ROWTYPE;
8
9 emps emp_table;
10 emps_max PLS_INTEGER;
11 inx1 PLS_INTEGER;
12 BEGIN
13 emps_max := 0;
14
15 emps := emp_table ();
16
17 FOR emp IN all_emps LOOP
18 emps_max := emps_max + 1;
19 emps.extend;
20 emps(emps_max).id := emp.id;
21 emps(emps_max).first_name := emp.first_name;
22 emps(emps_max).salary := emp.salary;
23 END LOOP;
24
25 emps.extend(5,1);
26
27 FOR inx1 IN 1..emps_max+5 LOOP
28 DBMS_OUTPUT.PUT_LINE(emps(inx1).id ||' ' || emps(inx1).first_name);
29 END LOOP;
30 emps.trim(5);
31
32 emps.delete(1);
33
34 DBMS_OUTPUT.PUT_LINE(emps.count);
35
36 FOR inx1 IN 1..emps_max+5 LOOP
37 IF emps.exists(inx1) THEN
38 DBMS_OUTPUT.PUT_LINE (emps(inx1).id ||' ' || emps(inx1).first_n
ame);
39 END IF;
40 END LOOP;
41 END;
42 /

SQL> declare
2 type dept_rty is record(id number, extra_tx VARCHAR2(2000));
3 type dept_aa is table of dept_rty index by binary_integer;
4 v_dept_aa dept_aa;
5 begin
6 v_dept_aa(10).id :=10;
7 v_dept_aa(20).id :=20;
8
9 for i in v_dept_aa.first..v_dept_aa.last loop
10 if v_dept_aa.exists(i) then
11 DBMS_OUTPUT.put_line(v_dept_aa(i).id);
12 end if;
13 end loop;
14 end;
15 /

================================================================================
=
Once you have defined your nested table type, you can use it to define a column
in a table.
The NESTED TABLE clause identifies the name of the nested table column (addresse
s).
The STORE AS clause specifies the name of the actual nested table (nested_addres
ses).
You cannot access the nested table independently of the table in which it is emb
edded.
create table courses
2 ( code VARCHAR2(6)
3 , description VARCHAR2(30)
4 , category CHAR(3)
5 , duration NUMBER(2)) ;
create type errorNumberType as object
2 ( code varchar2(4)
3 , ch number(2)
4 , pg number(3)
5 , txt varchar2(40)
6 ) ;
create type errorNumberTableCollection as table of errorNumberType;
SQL> alter table c
2 add (errata errorNumberTableCollection)
3 nested table errata store as errata_tab;
SQL> update c
2 set errata = errorNumberTableCollection();
SQL>
SQL>
SQL> insert into table ( select errata from c where code = 'SQL' )
2 values ( 'SQL', 3, 45, 'line.' );
SQL> select code, cardinality(errata)
2 from c
3 where errata is not empty;
SQL> update table ( select errata from c where code = 'SQL') e
2 set e.ch = 7;
SQL> CREATE TABLE employee (
2 id INTEGER PRIMARY KEY,
3 first_name VARCHAR2(10),
4 last_name VARCHAR2(10),
5 addresses nested_table_AddressType
6 )
7 NESTED TABLE
8 addresses
9 STORE AS
10 nested_addresses;
Table created.
SQL>
SQL>
SQL> INSERT INTO employee VALUES (
2 1, 'Steve', 'Brown',
3 nested_table_AddressType(
4 AddressType('2 Ave', 'City', 'MA', '12345'),
5 AddressType('4 Ave', 'City', 'CA', '54321')
6 )
7 );
1 row created.

===============================================================
SQL> CREATE OR REPLACE PROCEDURE myProc
2 AS
3 CURSOR ccur IS SELECT first_name, city FROM employee;
4 TYPE nametab IS TABLE OF employee.first_name%type
5 INDEX BY employee.city%type;
6 ch nametab;
7 i integer := 0;
8 imax integer;
9 BEGIN
10 FOR j IN ccur LOOP
11 /* i := i + 1; */
12 ch(j.first_name) := j.first_name;
13 END LOOP;
14 /* imax := i;
15 i := 0;
16 dbms_output.put_line('number of values read: '||imax); */
17 dbms_output.put_line('Name ... '||ch('Vancouver'));
18 END myProc;
19 /

SQL> declare
2 type text_nt is table of VARCHAR2(256);
3 v_ename_nt text_nt;
4 cursor c_emp is select first_name from employee where id='02';
5 procedure p_print_row is
6 begin
7 if v_eName_nt.count=2 then
8 DBMS_OUTPUT.put_line(v_eName_nt(1)||' '||v_eName_nt(2));
9 elsif v_eName_nt.count=1 then
10 DBMS_OUTPUT.put_line(v_eName_nt(1));
11 end if;
12 end;
13 begin
14 open c_emp;
15 loop
16 fetch c_emp bulk collect into v_eName_nt limit 2;
17 p_print_row;
18 exit when c_emp%NOTFOUND;
19 end loop;
20 close c_emp;
21 end;
22 /
Alison

===============================================================
SQL> declare
2 type cust_array_type is table of number
3 index by binary_integer;
4 employee_array cust_array_type;
5 v_index number;
6 begin
7 select empl_no bulk collect
8 into employee_array from employee_history;
9
10 FORALL i IN employee_array.FIRST..employee_array.LAST
11 delete from ord where empl_no = employee_array(i);
12
13 v_index := employee_array.FIRST;
14 for i in employee_array.FIRST..employee_array.LAST loop
15 dbms_output.put_line('delete for employee '
16 ||employee_array(v_index)
17 ||' deleted '
18 ||SQL%BULK_ROWCOUNT(v_index)
19 ||' rows.');
20 v_index := employee_Array.NEXT(v_index);
21 end loop;
22 end;
23 /
===============================================================
BEGIN
12 CASE
13 WHEN salary >= 10000 AND salary <=20000 THEN
14 give_bonus(employee_id, 1500);
15 WHEN salary > 20000 AND salary <= 40000 THEN
16 give_bonus(employee_id, 1000);
17 WHEN salary > 40000 THEN
18 give_bonus(employee_id, 500);
19 ELSE
20 give_bonus(employee_id, 0);
21 END CASE;
22 END;
23 /

==============================================================
select job, ename
2 , decode(greatest(msal,2500),2500,'cheap','expensive') as class
3 from employees
4 where bdate <date '1964-01-01'
5 order by decode(job,'DIRECTOR',1,
6 'MANAGER',2,
7 3);
SQL> select attendee, begindate
2 , case evaluation
3 when 1 then 'bad'
4 when 2 then 'mediocre'
5 when 3 then 'ok'
6 when 4 then 'good'
7 when 5 then 'excellent'
8 else 'not filled in'
9 end
10 from registrations
11 where course = 'SQL';

SQL> select lastname, salary,


2 decode( sign( salary - avg_sal ),
3 1, '> Average of ' || to_char(avg_sal, '99.99') ,
4 0, '= Average of ' || to_char(avg_sal, '99.99'),
5 -1, '< Average of ' || to_char(avg_sal, '99.99') ) sal_desc
6 from emp, avg_sal
7 /
SQL> SELECT id, NVL2(first_name, 'Known', 'Unknown') FROM employee;
SQL> SELECT * FROM employee WHERE id NOT IN (2, 3, NULL);
no rows selected
===============================================================
SQL> DECLARE
2 v_Radius NUMBER := 2;
3 BEGIN
4 WHILE TRUE LOOP
5 DBMS_OUTPUT.PUT_LINE('The Area is ' ||
6 3.14 * v_Radius * v_Radius);
7 IF v_Radius = 10 THEN
8 EXIT;
9 END IF;
10 v_Radius := v_Radius + 2 ;
11 END LOOP;
12 END;
13 /
SQL> BEGIN
2 FOR v_loopcounter IN 1..5 LOOP
3 DBMS_OUTPUT.PUT_LINE('Loop counter is ' || v_loopcounter);
4 END LOOP;
5 END;

SQL> DECLARE
2 v_Calc NUMBER := 0;
3 BEGIN
4 WHILE v_Calc >= 10 LOOP
5 v_Calc := v_Calc + 1;
6 DBMS_OUTPUT.PUT_LINE('The value of v_Calc is ' || v_Calc);
7 END LOOP;
8 END;
9 /
SQL> declare
2 v_ind NUMBER;
3 v_current NUMBER;
4 begin
5 v_current:=0; -- should not be null!
6 loop
7 v_ind:=0; -- reset each time
8 loop
9 v_ind:=v_ind+1;
10 DBMS_OUTPUT.put_line(v_current);
11 exit when v_ind=4;
12 end loop;
13 v_current:=v_current+5;
14 exit when v_current=25;
15 end loop;
16 end;
17 /

===============================================================
SQL> DECLARE
2 CURSOR cursorValue(p_product_id NUMBER) IS
3 SELECT h.product_description,o.company_short_name
4 FROM company o,product h
5 WHERE o.product_id =h.product_id
6 AND h.product_id =p_product_id
7 ORDER by 2;
8 v_company_rec cursorValue%ROWTYPE;
9 BEGIN
10 OPEN cursorValue(1);
11
12 LOOP
13 FETCH cursorValue INTO v_company_rec;
14 EXIT WHEN cursorValue%NOTFOUND;
15 dbms_output.put_line(rpad(v_company_rec.product_description,20,' ')||'
'||
16 rpad(v_company_rec.company_short_name,30,' '));
17 END LOOP;
18 CLOSE cursorValue;
19 OPEN cursorValue(2);
20
21 LOOP
22 FETCH cursorValue INTO v_company_rec;
23 EXIT WHEN cursorValue%NOTFOUND;
24 dbms_output.put_line(rpad(v_company_rec.product_description,20,' ')||'
'||
25 rpad(v_company_rec.company_short_name,30,' '));
26 END LOOP;
27 CLOSE cursorValue;
28 END;
29 /

SQL> DECLARE
2 CURSOR cursorValue IS
3 SELECT h.product_description,o.company_short_name
4 FROM company o,product h
5 WHERE o.product_id =h.product_id
6 ORDER by 2;
7 v_company_rec cursorValue%ROWTYPE;
8 BEGIN
9 IF (NOT cursorValue%ISOPEN) THEN
10 OPEN cursorValue;
11 END IF;
12
13 FETCH cursorValue INTO v_company_rec;
14
15 WHILE (cursorValue%FOUND)LOOP
16 dbms_output.put_line(rpad(v_company_rec.product_description,20,' ')||'
'||
17 rpad(v_company_rec.company_short_name,30,' '));
18 FETCH cursorValue INTO v_company_rec;
19 END LOOP;
20 IF (cursorValue%ISOPEN)THEN
21 CLOSE cursorValue;
22 END IF;
23 END;
24 /
SQL> DECLARE
2 CURSOR cursorValue IS
3 SELECT h.product_description,o.company_short_name FROM company o,produc
t h
4 WHERE o.product_id =h.product_id
5 ORDER by 2;
6 num_total_rows NUMBER;
7 BEGIN
8
9 FOR idx IN cursorValue LOOP
10 dbms_output.put_line(rpad(idx.product_description,20,' ')||' '||
11 rpad(idx.company_short_name,30,' '));
12
13 num_total_rows :=cursorValue%ROWCOUNT;
14 END LOOP;
15 IF num_total_rows >0 THEN
16 dbms_output.new_line;
17 dbms_output.put_line('Total Organizations = '||to_char(num_total_rows))
;
18 END IF;
19 END;
20 /
===============================================================
SQL> DECLARE
2 CURSOR c_AllEmployees IS
3 SELECT first_name, last_name
4 FROM employee;
5
6 v_FormattedName VARCHAR2(50);
7
8 /* Function which will return the first and last name
9 concatenated together, separated by a space. */
10 FUNCTION FormatName(p_FirstName IN VARCHAR2,
11 p_LastName IN VARCHAR2)
12 RETURN VARCHAR2 IS
13 BEGIN
14 RETURN p_FirstName || ' ' || p_LastName;
15 END FormatName;
16
17 -- Begin main block.
18 BEGIN
19 FOR v_EmployeeRecord IN c_AllEmployees LOOP
20 v_FormattedName :=
21 FormatName(v_EmployeeRecord.first_name,
22 v_EmployeeRecord.last_name);
23 DBMS_OUTPUT.put_line(v_FormattedName);
24 END LOOP;
25
26 COMMIT;
27 END;
28 /
SQL> CREATE OR REPLACE function exitfunc(myString VARCHAR2)
2 RETURN NUMBER IS
3 v_current_position INTEGER := 1;
4 v_counter NUMBER := 0;
5 BEGIN
6 WHILE v_current_position <= LENGTH(myString) LOOP
7 IF SUBSTR(myString,v_current_position,1) != ' ' THEN
8 v_counter := v_counter + 1;
9 ELSE
10 NULL;
11 END IF;
12 v_current_position := v_current_position + 1;
13 EXIT WHEN SUBSTR(myString,v_current_position,1) = ' ';
14 END LOOP;
15 RETURN v_counter ;
16 END exitfunc;
17 /

If there are no parameters to pass, you can simply call the function without the
parentheses.
To pass actual values, you can use commas as placeholders for parameters.
SQL>
SQL> CREATE OR REPLACE FUNCTION squareme(thenum number)
2 RETURN NUMBER IS
3 BEGIN
4 RETURN thenum * thenum;
5 END squareme;
6 /
Function created.

CREATE [OR REPLACE] FUNCTION function_name


[(parameter_name [IN | OUT | IN OUT] type [, ...])]
RETURN type
{IS | AS}
BEGIN
function_body
END function_name;
If there are no parameters to pass, you can simply call the function without the
parentheses.
To pass actual values, you can use commas as placeholders for parameters.
SQL> CREATE OR REPLACE FUNCTION average_salary RETURN NUMBER AS
2 v_average_salary NUMBER;
3 BEGIN
4 SELECT AVG(salary)
5 INTO v_average_salary
6 FROM employee;
7 RETURN v_average_salary;
8 END average_salary;
9 /
SQL> select average_salary from dual;
Local sub programmes:
SQL> declare
2
3 function myFunction (myValue in number,myFactor in number) return number
is
4
5 myFactor_to_use number;
6 minimum_wage number := 5;
7 begin
8 if (myFactor is null) or (myFactor < minimum_wage) then
9 myFactor_to_use := minimum_wage;
10 else
11 myFactor_to_use := myFactor;
12 end if;
13
14 return myValue * myFactor_to_use;
15 end;
16 begin
17 dbms_output.put_line(myFunction(40,10));
18 dbms_output.put_line(myFunction(40,2));
19 dbms_output.put_line(myFunction(40,null));
20 end;
21 /
SQL> CREATE OR REPLACE function exitfunc(myString VARCHAR2)
2 RETURN NUMBER IS
3 v_current_position INTEGER := 1;
4 v_counter NUMBER := 0;
5 BEGIN
6 WHILE v_current_position <= LENGTH(myString) LOOP
7 IF SUBSTR(myString,v_current_position,1) != ' ' THEN
8 v_counter := v_counter + 1;
9 ELSE
10 NULL;
11 END IF;
12 v_current_position := v_current_position + 1;
13 EXIT WHEN SUBSTR(myString,v_current_position,1) = ' ';
14 END LOOP;
15 RETURN v_counter ;
16 END exitfunc;
17 /
Function created.
Function to get characters and spaces:
CREATE OR REPLACE function exitfunc(myString in VARCHAR2, v_chars out varchar
2, v_spaces out varchar2 )
RETURN NUMBER IS
v_current_position INTEGER := 1;
-- v_counter NUMBER := 0;
-- v_spaces integer :=0;
BEGIN
v_chars := 0;
v_spaces := 0;
WHILE v_current_position <= LENGTH(myString) LOOP
IF SUBSTR(myString,v_current_position,1) != ' ' THEN
v_chars := v_chars + 1;
ELSE
v_spaces:= v_spaces+1;
END IF;
v_current_position := v_current_position + 1;
-- EXIT WHEN SUBSTR(myString,v_current_position,1) = ' ';
END LOOP;
RETURN 0 ;
END exitfunc;
/
Function to get only first four characters:
CREATE OR REPLACE function exitfunc(myString in VARCHAR2, v_chars out varchar
2, v_spaces out varchar2 )
RETURN NUMBER IS
v_current_position INTEGER := 1;
-- v_counter NUMBER := 0;
-- v_spaces integer :=0;
BEGIN
v_chars := 0;
v_spaces := 0;
WHILE v_current_position <= LENGTH(myString) LOOP
IF SUBSTR(myString,v_current_position,1) != ' ' THEN
v_chars := v_chars + 1;
ELSE
v_spaces:= v_spaces+1;
END IF;
v_current_position := v_current_position + 1;
EXIT WHEN SUBSTR(myString,v_current_position,1) = ' ';
END LOOP;
RETURN 0 ;
END exitfunc;
/
SQL> create or replace function factorial (i NUMBER)
2 return NUMBER
3 is
4 begin
5 if i = 1
6 then
7 return 1;
8 else
9 return i*factorial(i-1);
10 end if;
11 end;
12 /
Procedures:
You can create a procedure that contains a group of SQL and PL/SQL statements.
Procedures allow you to centralize your business logic in the database.
Procedures may be used by any program that accesses the database.
You create a procedure using the CREATE PROCEDURE statement.
The simplified syntax for the CREATE PROCEDURE statement is as follows:
CREATE [OR REPLACE] PROCEDURE procedure_name
[(parameter_name [IN | OUT | IN OUT] type [, ...])]
{IS | AS}
BEGIN
procedure_body
END procedure_name;
where
OR REPLACE specifies the procedure is to replace an existing procedure if presen
t.
You can use this option when you want to modify a procedure.
A procedure may be passed multiple parameters.
IN | OUT | IN OUT specifies the mode of the parameter.
type specifies the type of the parameter.
procedure_body contains the SQL and PL/SQL statements to perform the procedure's
task.
You may pick one of the following modes for each parameter:
IN is the default mode for a parameter.
IN parameters already have a value when the procedure is run.
The value of IN parameters may not be changed in the body.
OUT is specified for parameters whose values are only set in the body.
IN OUT parameters may already have a value when the procedure is called, but the
ir value may also be changed in the body.
Oracle provides several data dictionary views that provide information about pro
cedures that are currently stored in your schema:
all_errors: A list of current errors on all objects accessible to the user
all_source: Text source of all stored objects accessible to the user
user_objects:A list of all the objects the current user has access to
dba_errors:Current errors on all stored objects in the database
dba_object_size:All PL/SQL objects in the database
dba_source:Text source of all stored objects in the database
user_errors:Current errors on all a user's stored objects
user_source:Text source of all stored objects belonging to the user
user_object_size:User's PL/SQL objects
SELECT object_name, object_type
from user_objects
WHERE status = 'INVALID';
SQL> DECLARE
2 PROCEDURE b (caller VARCHAR2); -- This is a forward referencing stub.
3 PROCEDURE a (caller VARCHAR2) IS
4 procedure_name VARCHAR2(1) := 'A';
5 BEGIN
6 dbms_output.put_line('Procedure "A" called by ['||caller||']');
7 b(procedure_name);
8 END;
9 PROCEDURE b (caller VARCHAR2) IS
10 procedure_name VARCHAR2(1) := 'B';
11 BEGIN
12 dbms_output.put_line('Procedure "B" called by ['||caller||']');
13 END;
14 BEGIN
15 a('Main');
16 END;
17 /
Procedure "A" called by [Main]
Procedure "B" called by [A]
PL/SQL procedure successfully completed.

SQL> CREATE OR REPLACE PROCEDURE update_employee_salary(


2 p_factor IN NUMBER
3 ) AS
4 v_employee_count INTEGER;
5 BEGIN
6 UPDATE employee
7 SET salary = salary * p_factor;
8 COMMIT;
9 EXCEPTION
10 WHEN OTHERS THEN
11 ROLLBACK;
12 END update_employee_salary;
13 /
create or replace procedure p_print (i_replace in VARCHAR2, i_string in out VA
RCHAR2 ) is
begin
if i_string is null then
return;
else
i_string := i_replace;
end if;
-- DBMS_OUTPUT.put_line(replace(i_string,'<in>', i_replace));
end;
/

declare
2
3 function myFunction (myValue in number,myFactor in number) return number
is
4
5 myFactor_to_use number;
6 minimum_wage number := 5;
7 begin
8 if (myFactor is null) or (myFactor < minimum_wage) then
9 myFactor_to_use := minimum_wage;
10 else
11 myFactor_to_use := myFactor;
12 end if;
13
14 return myValue * myFactor_to_use;
15 end;
16 begin
17 dbms_output.put_line(myFunction(40,10));
18 dbms_output.put_line(myFunction(40,2));
19 dbms_output.put_line(myFunction(40,null));
20 end;
21 /
SQL>

================================================================================
==
Private Versus Public Package Objects:

For objects that are declared inside the package body, you are restricted to use
within that package.
Therefore, PL/SQL code outside the package cannot reference any of the variables
that were privately declared within the package.
Any items declared inside the package specification are visible outside the pack
age.
These objects declared in the package specification are called public.
If the variable, constant, or cursor was declared in a package specification or
body, their values persist for the duration of the user's session.
The values are lost when the current user's session terminates or the package is
recompiled.
================================================================================
============
Packages:
Packages encapsulate related functionality into one self-contained unit.
Packages are typically made up of two components: a specification and a body.
The package specification contains information about the package.
The package specification lists the available procedures and functions.
These are potentially available to all database users.
The package specification generally doesn't contain the code.
The package body contains the actual code.
SQL> create or replace package pkg_test1
2 as
3 function getArea (i_rad NUMBER) return NUMBER;
4 procedure p_print (i_str1 VARCHAR2 :='hello',
5 i_str2 VARCHAR2 :='world',
6 i_end VARCHAR2 :='!' );
7 end;
8 /
Package created.
SQL>
SQL> create or replace package body pkg_test1
2 as
3 function getArea (i_rad NUMBER)return NUMBER
4 is
5 v_pi NUMBER:=3.14;
6 begin
7 return v_pi * (i_rad ** 2);
8 end;
9
10 procedure p_print(i_str1 VARCHAR2 :='hello',
11 i_str2 VARCHAR2 :='world',
12 i_end VARCHAR2 :='!' )
13 is
14 begin
15 DBMS_OUTPUT.put_line(i_str1||','||i_str2||i_end);
16 end;
17 end;
18 /
Package body created.
Package State:
A package is always either valid or invalid.
A package is valid if none of its source code or objects it references have been
dropped, replaced, or altered since the package specification was last recompil
ed.
a package is invalid if its source code or any object that it references has bee
n dropped, altered, or replaced since the package specification was last recompi
led.
When a package becomes invalid, Oracle will also make invalid any object that re
ferences the package.
Recompiling Packages:
To recompile a package, use the ALTER PACKAGE command with the compile keyword.
Recompiling a package recompiles all objects defined within the package.
The following examples recompile just the body of a package.
ALTER PACKAGE inventory_pkg compile body
The following statement recompiles the entire package including the body and spe
cification:
ALTER PACKAGE inventory_pkg compile package
Creating a Package Specification:
You create a package specification using the CREATE PACKAGE statement.
The simplified syntax for the CREATE PACKAGE statement is as follows:
CREATE [OR REPLACE] PACKAGE package_name
{IS | AS}
package_specification
END package_name;
package_specification specifies the list of procedures and functions (along with
any variables, type definitions, and cursors) that are available to your packag
e's users.
Creating a Package Body
You create a package body using the CREATE PACKAGE BODY statement.
The simplified syntax for the CREATE PACKAGE BODY statement is as follows:
SQL>
SQL> CREATE [OR REPLACE] PACKAGE BODY package_name
2 {IS | AS}
3 package_body
4 END package_name;
where
package_name specifies the name of the package, which must match the package nam
e previously set in the package specification.
package_body specifies the code for the procedures and functions, along with any
variables and cursors.

SQL> CREATE OR REPLACE PACKAGE employee_package AS


2 TYPE t_ref_cursor IS REF CURSOR;
3 FUNCTION get_employee_ref_cursor RETURN t_ref_cursor;
4 PROCEDURE update_salary (p_id IN VARCHAR2,p_factor IN NUMBER);
5 END employee_package;
6 /
Package created.
SQL>
SQL>
SQL> CREATE OR REPLACE PACKAGE BODY employee_package AS
2 FUNCTION get_employee_ref_cursor
3 RETURN t_ref_cursor IS
4 employee_ref_cursor t_ref_cursor;
5 BEGIN
6 -- get the REF CURSOR
7 OPEN employee_ref_cursor FOR
8 SELECT id, first_name, salary
9 FROM employee;
10 -- return the REF CURSOR
11 RETURN employee_ref_cursor;
12 END get_employee_ref_cursor;
13
14 PROCEDURE update_salary (p_id IN VARCHAR2, p_factor IN NUMBER) AS
15 v_employee_count INTEGER;
16 BEGIN
17 UPDATE employee
18 SET salary = salary * p_factor;
19 COMMIT;
20 EXCEPTION
21 WHEN OTHERS THEN
22 -- perform a rollback when an exception occurs
23 ROLLBACK;
24 END update_salary;
25 END employee_package;
26 /
Package body created.
SQL>
SQL> SELECT employee_package.get_employee_ref_cursor
2 FROM dual;

CREATE OR REPLACE PACKAGE inv_pck_spec as


FUNCTION inv_count(qty integer) RETURN integer;
PROCEDURE inv_adjust(qty integer);
END inv_pck_spec;
/
CREATE OR REPLACE PACKAGE BODY inv_pck_spec is
FUNCTION inv_count(qty integer)RETURN integer is
new_qty integer;
BEGIN
new_qty:= qty*6;
INSERT into emp (empno,sal) values ('02',new_qty);
RETURN(new_qty);
END inv_count;
PROCEDURE inv_adjust(qty integer) is
BEGIN
DELETE from emp WHERE sal <qty;
END;
BEGIN -- package initialization begins here
INSERT into emp (empno, ename)values('02', 'new');
END inv_pck_spec;
/
SQL> --call inv_pck_spec.inv_count(2);
SQL>
SQL> call inv_pck_spec.inv_adjust(2000);
Overloading Packaged Subprograms:
SQL> CREATE OR REPLACE PACKAGE EmployeePackage AS
2 PROCEDURE AddEmployee(p_EmployeeID IN Employee.id%TYPE,
3 p_first_name IN Employee.first_name%TYPE,
4 p_last_name IN Employee.last_name%TYPE);
5
6 PROCEDURE AddEmployee(p_FirstName IN Employee.first_name%TYPE,
7 p_LastName IN Employee.last_name%TYPE,
8 p_city IN Employee.city%TYPE);
9
10 END EmployeePackage;
11 /
Package created.
SQL>
SQL> CREATE OR REPLACE PACKAGE BODY EmployeePackage AS
2
3 PROCEDURE AddEmployee(p_EmployeeID IN Employee.id%TYPE,
4 p_first_name IN Employee.first_name%TYPE,
5 p_last_name IN Employee.last_name%TYPE) IS
6 BEGIN
7 INSERT INTO Employee (id, first_name, last_name)
8 VALUES (p_EmployeeID, p_first_name, p_last_name);
9 COMMIT;
10 END AddEmployee;
11
12 -- Add a new student by name, rather than ID.
13 PROCEDURE AddEmployee(p_FirstName IN Employee.first_name%TYPE,
14 p_LastName IN Employee.last_name%TYPE,
15 p_city IN Employee.city%TYPE) IS
16 v_EmployeeID Employee.ID%TYPE;
17 BEGIN
18 SELECT ID
19 INTO v_EmployeeID
20 FROM Employee
21 WHERE first_name = p_FirstName
22 AND last_name = p_LastName;
23
24 -- Now we can add the student by ID.
25 INSERT INTO Employee (id, first_name, city)
26 VALUES (v_EmployeeID, p_firstname, p_city);
27 COMMIT;
28 END AddEmployee;
29
30 END EmployeePackage;
31 /
Dropping a package:
Following statement will destroy only the package body of emp_admin:
DROP PACKAGE BODY emp_admin;
The following statement will drop the entire emp_admin package:
DROP PACKAGE emp_admin;
After the dropping package body package spec remains valid.
create or replace package pkg_Util is
cursor c_emp is select * from employee;
r_emp c_emp%ROWTYPE;
end;
/

--Here is a different package that references the cursor


create or replace package body pkg_aDifferentUtil is
procedure p_printEmps is
begin
open pkg_Util.c_emp;
loop
fetch pkg_Util.c_emp into pkg_Util.r_emp;
exit when pkg_Util.c_emp%NOTFOUND;
DBMS_OUTPUT.put_line(pkg_Util.r_emp.first_Name);
end loop;
close pkg_Util.c_emp;
end;
end;
/
SQL> create or replace package pkg_a is
2 v_a number:=0;
3 function a1 return NUMBER;
4 end;
5 /
Package created.
SQL> create or replace package body pkg_a is
2 function a1 return NUMBER is
3 begin
4 return 0;
5 end;
6 end;
7 /
Package body created.
SQL> create or replace package pkg_b is
2 function b1 return NUMBER;
3 end;
4 /
Package created.
SQL> create or replace package body pkg_b is
2 function b1 return NUMBER is
3 begin
4 return pkg_a.a1+1;
5 end;
6 end;
7 /
Package body created.

===========================================================================
Controlling access to packages
You can do this in one of two ways: a simple command or a wrapper package.
To grant a user rights to access a particular package:
grant execute on package_name to user
You can't grant rights to execute a portion of a package.
Rights must be granted to an entire package.
To revoke grants from a user, use the following command:
revoke execute on package_name from user

===========================================================================
SQL> create or replace package pkg_global is
2 function f_countryUSA_cd return VARCHAR2;
3 end;
4 /
Package created.
SQL> create or replace package body pkg_global is
2 gv_countryUSA_cd VARCHAR2(3) := 'USA';
3
4 function f_countryUSA_cd return VARCHAR2 is
5 begin
6 return gv_countryUSA_cd;
7 end;
8
9 end;
10 /
Package body created.
===========================================================================
package RECURSION
SQL> create package RECURSION is
2 procedure A(p number);
3 procedure B(p number);
4 end;
5 /

SQL> create or replace package body RECURSION is


2
3 procedure A(p number) is
4 begin
5 B(p+1);
6 end;
7
8 procedure B(p number) is
9 begin
10 A(p+1);
11 end;
12
13 end;
14 /
Package body created.
===========================================================================
Package with only one function:
SQL> CREATE OR REPLACE PACKAGE valerr
2 IS
3 FUNCTION get RETURN VARCHAR2;
4 END valerr;
5 /
Package created.
SQL>
SQL>
SQL> CREATE OR REPLACE PACKAGE BODY valerr
2 IS
3 v VARCHAR2(1) := 'ABC';
4
5 FUNCTION get RETURN VARCHAR2
6 IS
7 BEGIN
8 RETURN v;
9 END;
10 BEGIN
11 DBMS_OUTPUT.PUT_LINE ('Before I show you v...');
12
13 EXCEPTION
14 WHEN OTHERS
15 THEN
16 DBMS_OUTPUT.PUT_LINE ('Trapped the error!');
17
18 END valerr;
19 /
===========================================================================
SQL> CREATE OR REPLACE PACKAGE fixer AS
2
3 PROCEDURE fix_stuff;
4 PROCEDURE fix_this ( p_thing_to_fix VARCHAR2 );
5
6 END fixer;
7 /
Package created.
SQL>
SQL> CREATE OR REPLACE PACKAGE BODY fixer AS
2
3 PROCEDURE fix_this ( p_thing_to_fix VARCHAR2 ) IS
4 PRAGMA AUTONOMOUS_TRANSACTION;
5 BEGIN
6 INSERT INTO stuff_to_fix(stuff,fixed)VALUES(p_thing_to_fix,'N');
7 COMMIT;
8 END fix_this;
9
10 PROCEDURE fix_stuff IS
11 CURSOR curs_get_stuff_to_fix IS
12 SELECT stuff,ROWID FROM stuff_to_fix WHERE fixed = 'N';
13
14 BEGIN
15
16 FOR v_stuff_rec IN curs_get_stuff_to_fix LOOP
17
18 EXECUTE IMMEDIATE v_stuff_rec.stuff;
19
20 UPDATE stuff_to_fix SET fixed = 'Y' WHERE ROWID = v_stuff_rec.rowid;
21
22 END LOOP;
23
24 COMMIT;
25
26 END fix_stuff;
27
28 END fixer;
29 /
===========================================================================
SQL> CREATE OR REPLACE PACKAGE name_pkg IS
2 TYPE type_name_rec IS RECORD (name VARCHAR2(100));
3 TYPE type_name_refcur IS REF CURSOR RETURN type_name_rec;
4 FUNCTION open_name (p_table_txt IN VARCHAR2, p_id_num IN NUMBER) RETURN
type_name_refcur;
5 FUNCTION get_name (p_table_txt IN VARCHAR2, p_id_num IN NUMBER) RETURN V
ARCHAR2;
6 END name_pkg;
7 /
Package created.
SQL>
SQL> CREATE OR REPLACE PACKAGE BODY name_pkg IS
2 FUNCTION open_name (p_table_txt IN VARCHAR2, p_id_num IN NUMBER)
3 RETURN type_name_refcur IS
4 lv_table_txt VARCHAR2(100) := UPPER(p_table_txt);
5 lv_name_rec type_name_refcur;
6 BEGIN
7 IF lv_table_txt = 'EMPLOYEE' THEN
8 OPEN lv_name_rec FOR
9 SELECT last_name || ', '|| first_name
10 FROM employee
11 WHERE employee_id = p_id_num;
12 ELSIF lv_table_txt = 'CUSTOMER' THEN
13 OPEN lv_name_rec FOR
14 SELECT customer_name
15 FROM customer
16 WHERE customer_id = p_id_num;
17 ELSIF lv_table_txt = 'PRODUCT' THEN
18 OPEN lv_name_rec FOR
19 SELECT product_name
20 FROM product
21 WHERE product_id = p_id_num;
22 ELSE
23 RAISE_APPLICATION_ERROR (-20222,
24 'Invalid table specified for name request.');
25 END IF;
26 RETURN lv_name_rec;
27 END open_name;
28 FUNCTION get_name (p_table_txt IN VARCHAR2, p_id_num IN NUMBER)
29 RETURN VARCHAR2 IS
30 lv_name_rec type_name_rec;
31 lv_name_refcur type_name_refcur;
32 BEGIN
33 lv_name_refcur := open_name(p_table_txt, p_id_num);
34 FETCH lv_name_refcur INTO lv_name_rec;
35 IF (lv_name_refcur%NOTFOUND) THEN
36 CLOSE lv_name_refcur;
37 RAISE NO_DATA_FOUND;
38 ELSE
39 CLOSE lv_name_refcur;
40 END IF;
41 RETURN lv_name_rec.name;
42 END get_name;
43 END name_pkg;
44 /
Package body created.
SQL> CREATE OR REPLACE PACKAGE BODY name_pkg IS
2 FUNCTION open_name (p_table_txt IN VARCHAR2, p_id_num IN NUMBER)
3 RETURN type_name_refcur IS
4 lv_table_txt VARCHAR2(100) := UPPER(p_table_txt);
5 lv_name_rec type_name_refcur;
6 BEGIN
7 IF lv_table_txt = 'EMPLOYEE' THEN
8 OPEN lv_name_rec FOR
9 SELECT last_name || ', '|| first_name
10 FROM employee
11 WHERE employee_id = p_id_num;
12 ELSIF lv_table_txt = 'CUSTOMER' THEN
13 OPEN lv_name_rec FOR
14 SELECT customer_name
15 FROM customer
16 WHERE customer_id = p_id_num;
17 ELSIF lv_table_txt = 'PRODUCT' THEN
18 OPEN lv_name_rec FOR
19 SELECT product_name
20 FROM product
21 WHERE product_id = p_id_num;
22 ELSE
23 RAISE_APPLICATION_ERROR (-20222,
24 'Invalid table specified for name request.');
25 END IF;
26 RETURN lv_name_rec;
27 END open_name;
28 FUNCTION get_name (p_table_txt IN VARCHAR2, p_id_num IN NUMBER)
29 RETURN VARCHAR2 IS
30 lv_name_rec type_name_rec;
31 lv_name_refcur type_name_refcur;
32 BEGIN
33 lv_name_refcur := open_name(p_table_txt, p_id_num);
34 FETCH lv_name_refcur INTO lv_name_rec;
35 IF (lv_name_refcur%NOTFOUND) THEN
36 CLOSE lv_name_refcur;
37 RAISE NO_DATA_FOUND;
38 ELSE
39 CLOSE lv_name_refcur;
40 END IF;
41 RETURN lv_name_rec.name;
42 END get_name;
43 END name_pkg;
44 /
Package body created.
SQL> create or replace function factorial (i NUMBER)
2 return NUMBER
3 is
4 begin
5 if i = 1
6 then
7 return 1;
8 else
9 return i*factorial(i-1);
10 end if;
11 end;
12 /
===============================================================
SQL> CREATE OR REPLACE PACKAGE cc_debug
2 IS
3 debug_active CONSTANT BOOLEAN := TRUE;
4
5 trace_level CONSTANT PLS_INTEGER := 10;
6
7 END cc_debug;
8 /
Package created.
SQL> CREATE OR REPLACE PACKAGE myPackage
2 IS
3 PRAGMA SERIALLY_REUSABLE;
4 CURSOR cursor_site IS
5 SELECT * from company_site ORDER BY site_no;
6 PROCEDURE displaySites;
7 END myPackage;
8 /
Package created.
SQL> CREATE OR REPLACE PACKAGE BODY myPackage
2 IS
3 PRAGMA SERIALLY_REUSABLE;
4 PROCEDURE displaySites
5 IS
6 site_rec company_site%ROWTYPE;
7 BEGIN
8 OPEN cursor_site;
9 FETCH cursor_site INTO site_rec;
10 dbms_output.put_line(TO_CHAR(site_rec.site_no)||' '||site_rec.site_desc
r);
11 FETCH cursor_site INTO site_rec;
12 dbms_output.put_line(TO_CHAR(site_rec.site_no)||' '||site_rec.site_desc
r);
13 END displaySites;
14 END myPackage;
15 /
SQL> CREATE OR REPLACE PACKAGE valerr
2 IS
3 FUNCTION get RETURN VARCHAR2;
4 END valerr;
5 /
Package created.
SQL>
SQL> CREATE OR REPLACE PACKAGE BODY valerr
2 IS
3 v VARCHAR2(1);
4
5 FUNCTION get RETURN VARCHAR2 -- Added Line
6 IS
7 BEGIN
8 RETURN v;
9 END;
10 BEGIN
11 v := 'ABC';
12
13 EXCEPTION
14 WHEN OTHERS
15 THEN
16 DBMS_OUTPUT.PUT_LINE ('Error initializing valerr:');
17 DBMS_OUTPUT.PUT_LINE (SQLERRM);
18
19 END valerr;
20 /

SQL> exec :as1:=valerr.get();


PL/SQL procedure successfully completed.
SQL> print :as1;
AS1
--------------------------------
A
SQL> var x number
SQL> exec add_up(10,10,:x)
PL/SQL procedure successfully completed.
SQL> print x
X
----------
20
There are some restrictions on overloading:
You can't overload standalone procedures or functions.
The second definition simply overwrites the first one.
You can't overload functions that differ only by the datatype of the return valu
e.
If you need to implement this requirement, use overloaded procedures with OUT pa
rameters.

The hint NOCOPY is applicable only to OUT and IN OUT types of variables
IN ----> The IN mode is the default mode. It means a formal parameter is read-on
ly.
When you set a formal parameter as read-only, you can’t alter it during the
execution of the subroutine. You can assign a default value to a parameter,
making the parameter optional.
OUT ----> The OUT mode means a formal parameter is write-only. When you set a
formal parameter as write-only, there is no initial physical size allocated
to the variable. You allocate the physical size and value inside your
subroutine. You can’t assign a default value, which would make an OUT
mode formal parameter optional.
IN OUT ----> The IN OUT mode means a formal parameter is read-write. When you
set a formal parameter as read-write, the actual parameter provides the
physical size of the actual parameter. While you can change the contents
of the variable inside the subroutine, you can’t change or exceed the actual
parameter’s allocated size. You can’t assign a default value making an IN
OUT mode parameter optional.
NOCOPY ----> You can override the default behavior of passing copies of variable
s when calling functions
and procedures for local transactions. This means you use fewer resources and ac
tually pass a
reference, not a copy of data.
CREATE OR REPLACE PROCEDURE very_confusing (
arg1 IN VARCHAR2
, arg3 IN OUT NOCOPY VARCHAR2
)
IS
BEGIN
arg1 := Hello ;
DBMS_OUTPUT.put_line ( arg3 assigned, arg1 = || arg1);
arg3 := 'Third value';
DBMS_OUTPUT.put_line ('arg3 assigned, arg1 = ' || arg1);
END;
DECLARE
str VARCHAR2 (100) := 'First value';
BEGIN
DBMS_OUTPUT.put_line ('str before = ' || str);
very_confusing (str, str);
DBMS_OUTPUT.put_line ('str after = ' || str);
END;
str before = First value
arg3 assigned, arg1 = Third value
str after = Third value

CREATE OR REPLACE PROCEDURE very_confusing (


arg1 IN VARCHAR2
, arg2 IN OUT VARCHAR2
, arg3 IN OUT NOCOPY VARCHAR2
)
IS
BEGIN
arg2 := 'Second value';
DBMS_OUTPUT.put_line ('arg2 assigned, arg1 = ' || arg1);
arg3 := 'Third value';
DBMS_OUTPUT.put_line ('arg3 assigned, arg1 = ' || arg1);
END;
/
DECLARE
str VARCHAR2 (100) := 'First value';
BEGIN
DBMS_OUTPUT.put_line ('str before = ' || str);
very_confusing (str, str, str);
DBMS_OUTPUT.put_line ('str after = ' || str);
END;
/
str before = First value
arg2 assigned, arg1 = First value
arg3 assigned, arg1 = Third value
str after = Second value
===============================================================
SQL> CREATE OR REPLACE PROCEDURE update_emp (emp_rec employee%ROWTYPE) IS
2 BEGIN
3 UPDATE employee
4 SET start_date = emp_rec.start_date + 100
5 WHERE id = emp_rec.id;
6 END update_emp;
7 /
Procedure created.
SQL>
SQL>
SQL> DECLARE
2 a employee%ROWTYPE;
3 BEGIN
4 a.id := '01';
5 update_emp(a);
6 END;
7 /

SQL> DECLARE
2 PROCEDURE b (caller VARCHAR2); -- This is a forward referencing stub.
3 PROCEDURE a (caller VARCHAR2) IS
4 procedure_name VARCHAR2(1) := 'A';
5 BEGIN
6 dbms_output.put_line('Procedure "A" called by ['||caller||']');
7 b(procedure_name);
8 END;
9 PROCEDURE b (caller VARCHAR2) IS
10 procedure_name VARCHAR2(1) := 'B';
11 BEGIN
12 dbms_output.put_line('Procedure "B" called by ['||caller||']');
13 END;
14 BEGIN
15 a('Main');
16 END;
17 /
Procedure "A" called by [Main]
Procedure "B" called by [A]
SQL> create or replace procedure p_print (i_string in VARCHAR2,i_replace in VAR
CHAR2 := 'new') is
2 begin
3 if i_string is null then
4 return;
5 end if;
6 DBMS_OUTPUT.put_line(replace(i_string,'<in>', i_replace));
7 end;
8 /
SQL> CREATE OR REPLACE PACKAGE EmployeePackage AS
2 PROCEDURE AddEmployee(p_EmployeeID IN Employee.id%TYPE,
3 p_first_name IN Employee.first_name%TYPE,
4 p_last_name IN Employee.last_name%TYPE);
5
6 PROCEDURE AddEmployee(p_FirstName IN Employee.first_name%TYPE,
7 p_LastName IN Employee.last_name%TYPE,
8 p_city IN Employee.city%TYPE);
9
10 END EmployeePackage;
11 /
Package created.
SQL>
SQL> CREATE OR REPLACE PACKAGE BODY EmployeePackage AS
2
3 PROCEDURE AddEmployee(p_EmployeeID IN Employee.id%TYPE,
4 p_first_name IN Employee.first_name%TYPE,
5 p_last_name IN Employee.last_name%TYPE) IS
6 BEGIN
7 INSERT INTO Employee (id, first_name, last_name)
8 VALUES (p_EmployeeID, p_first_name, p_last_name);
9 COMMIT;
10 END AddEmployee;
11
12 -- Add a new student by name, rather than ID.
13 PROCEDURE AddEmployee(p_FirstName IN Employee.first_name%TYPE,
14 p_LastName IN Employee.last_name%TYPE,
15 p_city IN Employee.city%TYPE) IS
16 v_EmployeeID Employee.ID%TYPE;
17 BEGIN
18 SELECT ID
19 INTO v_EmployeeID
20 FROM Employee
21 WHERE first_name = p_FirstName
22 AND last_name = p_LastName;
23
24 -- Now we can add the student by ID.
25 INSERT INTO Employee (id, first_name, city)
26 VALUES (v_EmployeeID, p_firstname, p_city);
27 COMMIT;
28 END AddEmployee;
29
30 END EmployeePackage;
31 /

You can do this in one of two ways: a simple command or a wrapper package.
To grant a user rights to access a particular package:
grant execute on package_name to user
You can't grant rights to execute a portion of a package.
Rights must be granted to an entire package.
To revoke grants from a user, use the following command:
revoke execute on package_name from user

SQL> select pkg_global.f_countryUSA_cd from dual;

SQL> CREATE OR REPLACE PACKAGE Random AS


2
3 PROCEDURE ChangeSeed(p_NewSeed IN NUMBER);
4
5 FUNCTION Rand RETURN NUMBER;
6
7 PROCEDURE GetRand(p_RandomNumber OUT NUMBER);
8
9 FUNCTION RandMax(p_MaxVal IN NUMBER) RETURN NUMBER;
10
11 PROCEDURE GetRandMax(p_RandomNumber OUT NUMBER,p_MaxVal IN NUMBER);
12 END Random;
13 /
Package created.
SQL>
SQL> CREATE OR REPLACE PACKAGE BODY Random AS
2
3 v_Multiplier CONSTANT NUMBER := 2;
4 v_Increment CONSTANT NUMBER := 1;
5
6 v_Seed number := 1;
7
8 PROCEDURE ChangeSeed(p_NewSeed IN NUMBER) IS
9 BEGIN
10 v_Seed := p_NewSeed;
11 END ChangeSeed;
12
13 FUNCTION Rand RETURN NUMBER IS
14 BEGIN
15 v_Seed := MOD(v_Multiplier * v_Seed + v_Increment,(2 ** 32));
16 RETURN BITAND(v_Seed/(2 ** 16), 32767);
17 END Rand;
18
19 PROCEDURE GetRand(p_RandomNumber OUT NUMBER) IS
20 BEGIN
21 p_RandomNumber := Rand;
22 END GetRand;
23
24 FUNCTION RandMax(p_MaxVal IN NUMBER) RETURN NUMBER IS
25 BEGIN
26 RETURN MOD(Rand, p_MaxVal) + 1;
27 END RandMax;
28
29 PROCEDURE GetRandMax(p_RandomNumber OUT NUMBER,p_MaxVal IN NUMBER) IS
30 BEGIN
31 -- Simply call RandMax and return the value.
32 p_RandomNumber := RandMax(p_MaxVal);
33 END GetRandMax;
34
35 BEGIN
36 ChangeSeed(TO_NUMBER(TO_CHAR(SYSDATE, 'SSSSS')));
37 END Random;
38 /
Package body created.

SQL> CREATE OR REPLACE PACKAGE myPackage


2 IS
3 PRAGMA SERIALLY_REUSABLE;
4 CURSOR cursor_site IS
5 SELECT * from company_site ORDER BY site_no;
6 PROCEDURE displaySites;
7 END myPackage;
8 /
Package created.
SQL> CREATE OR REPLACE PACKAGE BODY myPackage
2 IS
3 PRAGMA SERIALLY_REUSABLE;
4 PROCEDURE displaySites
5 IS
6 site_rec company_site%ROWTYPE;
7 BEGIN
8 OPEN cursor_site;
9 FETCH cursor_site INTO site_rec;
10 dbms_output.put_line(TO_CHAR(site_rec.site_no)||' '||site_rec.site_desc
r);
11 FETCH cursor_site INTO site_rec;
12 dbms_output.put_line(TO_CHAR(site_rec.site_no)||' '||site_rec.site_desc
r);
13 END displaySites;
14 END myPackage;
15 /
===============================================================
SQL> DECLARE
2 quantity1 NUMBER := -2;
3 quantity2 NUMBER := 3;
4 total NUMBER := 0;
5 quantity_must_positive EXCEPTION;
6 FUNCTION find_cost (quant NUMBER) RETURN NUMBER IS
7 BEGIN
8 IF (quant > 0)
9 THEN
10 RETURN(quant * 20);
11 ELSE
12 RAISE quantity_must_positive;
13 END IF;
14 END find_cost;
15 BEGIN
16 total := find_cost (quantity2);
17 total := total + find_cost(quantity1);
18 EXCEPTION
19 WHEN quantity_must_positive
20 THEN
21 dbms_output.put_line('Total until now: ' || total);
22 dbms_output.put_line('Tried to use negative quantity ');
23 END;
24 /

SQL> DECLARE
2 v_descr VARCHAR2(20);
3 BEGIN
4 SELECT product_description
5 INTO v_descr
6 FROM product
7 WHERE product_id =10;
8 dbms_output.put_line(v_descr);
9 EXCEPTION
10 WHEN NO_DATA_FOUND THEN
11 dbms_output.put_line('ERR:Invalid Hierarchy Code 10');
12 WHEN OTHERS THEN
13 dbms_output.put_line('ERR:An error occurred with info :'||
14 TO_CHAR(SQLCODE)||' '||SQLERRM);
15 END;
16 /

SQL> CREATE OR REPLACE PROCEDURE Register is


2 BEGIN
3 RAISE_APPLICATION_ERROR(-20000, 'Can''t add more Employee');
4
5 EXCEPTION
6 WHEN NO_DATA_FOUND THEN
7 RAISE_APPLICATION_ERROR(-20001, ' doesn''t exist!');
8 END Register;
9 /

SQL> DECLARE
2 e_MissingNull EXCEPTION;
3 PRAGMA EXCEPTION_INIT(e_MissingNull, -1400);
4 BEGIN
5 INSERT INTO Employee (id) VALUES (NULL);
6 EXCEPTION
7 WHEN e_MissingNull then
8 DBMS_OUTPUT.put_line('ORA-1400 occurred');
9 END;
10 /
ORA-1400 occurred
==============================================================
There are four types of database triggers:
Table-level triggers can initiate activity before or after an INSERT, UPDATE, or
DELETE event.
View-level triggers defines what can be done to the view.
Database-level triggers can be activated at startup and shutdown of a database.
Session-level triggers can be used to store specific information.
A trigger is a procedure that is run automatically by the database when a specif
ied SQL DML INSERT, UPDATE, or DELETE statement is run against a table.
Triggers are useful for doing things like advanced auditing of changes made to c
olumn values in a table.
When a Trigger Runs
A trigger can fire before or after the SQL statement runs.
A trigger can may be run once for every row affected. Such a trigger is known as
a row-level trigger.
A trigger can may be run for all the rows. Such trigger is known as a statement-
level trigger.
A row-level trigger has access to the old and new column values when the trigger
fires as a result of an UPDATE statement on that column.
The firing of a trigger may also be limited using a trigger condition.
Different events may fire a trigger, but these events are always divided into th
ree groups:
DML triggers,
INSTEAD OF triggers, and
system event triggers.
DML triggers are the triggers on INSERT/UPDATE/DELETE operations in any table.

CREATE [OR REPLACE] TRIGGER trigger_name


{BEFORE|AFTER} verb_list ON table_name
[[REFERENCING correlation_names] FOR EACH ROW [WHEN (condition)]]
DECLARE
declarations
BEGIN
pl/sql_code
END;
verb_list -- The SQL verbs that fire the trigger.
table_name -- The table on which the trigger is defined.
correlation_names -- Allows you to specify correlation names other than the defa
ult of OLD and NEW.
condition -- An optional condition placed on the execution of the trigger.
declarations -- Consists of any variable, record, or cursor declarations needed
by this PL/SQL block.

Statement-level triggers
------------------------
Use statement-level triggers when you need to check business rules that are not
row dependent.
SQL> create or replace trigger emp_bid
2 before insert or delete
3 on employee
4 referencing new as new old as old
5 begin
6 if to_char(sysdate,'Dy') in ('Sat','Sun') then
7 raise_application_error(-20999,'No create/delete employees on weeke
nd!');
8 end if;
9 end;
10 /
Trigger created.
Mutating trigger:
----------------
CREATE OR REPLACE TRIGGER LimitSalary
2 BEFORE INSERT OR UPDATE OF salary ON employee
3 FOR EACH ROW
4 DECLARE
5 v_MaxSalary CONSTANT NUMBER := 2000;
6 v_CurrentSalary NUMBER;
7 BEGIN
8 SELECT salary
9 INTO v_CurrentSalary
10 FROM employee
11 WHERE id = :new.id;
12
13 IF v_CurrentSalary > v_MaxSalary THEN
14 RAISE_APPLICATION_ERROR(-20000, 'Too high in salary ' || :new.id);
15 END IF;
16 END LimitSalary;
17 /
Mutating Table Exceptions
Mutating table exceptions occur when we try to reference the triggering table in
a query from within row-level trigger code.
In this article I'll present and example of how a mutating table exception might
occur and a simple method to get round it.
A mutating trigger occurs in Oracle when the table that originally fired the tri
ggering event is being accessed in the body of the
trigger code, directly or implicitly in a procedure, or in a nested trigger call
ed from the trigger body.
This action is forbidden because the table is in middle of a transaction, and re
ferencing the same table again in the middle of the updating action
causes the trigger to mutate.

SQL> CREATE OR REPLACE PACKAGE EmployeeData AS


2 TYPE t_Salary IS TABLE OF employee.salary%TYPE INDEX BY BINARY_INTEGER;
3 TYPE t_IDs IS TABLE OF employee.ID%TYPE INDEX BY BINARY_INTEGER;
4
5 v_EmployeeSalary t_Salary;
6 v_EmployeeIDs t_IDs;
7 v_NumEntries BINARY_INTEGER := 0;
8 END EmployeeData;
9 /
Package created.
SQL>
SQL> CREATE OR REPLACE TRIGGER RLimitSalary
2 BEFORE INSERT OR UPDATE OF salary ON employee
3 FOR EACH ROW
4 BEGIN
5 /* Record the new data in EmployeeData. We don't make any
6 changes to employee, to avoid the ORA-4091 error. */
7 EmployeeData.v_NumEntries := EmployeeData.v_NumEntries + 1;
8 EmployeeData.v_EmployeeSalary(EmployeeData.v_NumEntries) := :new.salary;
9 EmployeeData.v_EmployeeIDs(EmployeeData.v_NumEntries) := :new.id;
10 END RLimitSalary;
11 /
Trigger created.
SQL>
SQL> CREATE OR REPLACE TRIGGER SLimitSalary
2 AFTER INSERT OR UPDATE OF salary ON employee
3 DECLARE
4 v_MaxEmployees CONSTANT NUMBER := 5;
5 v_CurrentEmployees NUMBER;
6 v_EmployeeID employee.ID%TYPE;
7 v_Salary employee.salary%TYPE;
8 BEGIN
9 /* Loop through each student inserted or updated, and verify
10 that we are still within the limit. */
11 FOR v_LoopIndex IN 1..EmployeeData.v_NumEntries LOOP
12 v_EmployeeID := EmployeeData.v_EmployeeIDs(v_LoopIndex);
13 v_Salary := EmployeeData.v_EmployeeSalary(v_LoopIndex);
14
15
16 SELECT COUNT(*)
17 INTO v_CurrentEmployees
18 FROM employee
19 WHERE salary = v_Salary;
20
21 -- If there isn't room, raise an error.
22 IF v_CurrentEmployees > v_MaxEmployees THEN
23 RAISE_APPLICATION_ERROR(-20000,
24 'Too much salary ' || v_Salary ||
25 ' because of employee ' || v_EmployeeID);
26 END IF;
27 END LOOP;
28
29 -- Reset the counter so the next execution will use new data.
30 EmployeeData.v_NumEntries := 0;
31 END SLimitSalary;
32 /
Trigger created.
Row level Trigger:
-----------------
SQL> create table product_audit(
2 product_id number(4) not null,
3 num_rows number(8) not null
4 );
SQL> CREATE OR REPLACE TRIGGER myTrigger
2 AFTER INSERT OR DELETE ON company
3 FOR EACH ROW
4 BEGIN
5 IF INSERTING THEN
6 UPDATE product_audit
7 SET num_rows =num_rows+1
8 WHERE product_id =:NEW.product_id;
9 IF (SQL%NOTFOUND) THEN
10 INSERT INTO product_audit VALUES (:NEW.product_id,1);
11 END IF;
12 ELSIF DELETING THEN
13 UPDATE product_audit
14 SET num_rows =num_rows-1
15 WHERE product_id =:OLD.product_id;
16 END IF;
17 END;
18 /
Trigger created.
CREATE OR REPLACE TRIGGER before_employee_salary_update
2 BEFORE UPDATE OF salary
3 ON employee
4 FOR EACH ROW WHEN (new.salary < old.salary * 0.75)
5 BEGIN
6 dbms_output.put_line('id = ' || :old.id);
7 dbms_output.put_line('Old salary = ' || :old.salary);
8 dbms_output.put_line('New salary = ' || :new.salary);
9 dbms_output.put_line('The salary reduction is more than 25%');
10
11 INSERT INTO Myaudit (
12 id, old_value, new_value
13 ) VALUES (
14 :old.id, :old.salary, :new.salary
15 );
16 END before_employee_salary_update;
17 /
Trigger created.
Referencing Current User:
------------------------
CREATE OR REPLACE TRIGGER LogRSChanges
2 BEFORE INSERT OR DELETE OR UPDATE ON employee
3 FOR EACH ROW
4 DECLARE
5 v_ChangeType CHAR(1);
6 BEGIN
7 /* Use 'I' for an INSERT, 'D' for DELETE, and 'U' for UPDATE. */
8 IF INSERTING THEN
9 v_ChangeType := 'I';
10 ELSIF UPDATING THEN
11 v_ChangeType := 'U';
12 ELSE
13 v_ChangeType := 'D';
14 END IF;
15
16 DBMS_OUTPUT.put_line(v_ChangeType ||' '|| USER ||' ' ||SYSDATE);
17 END LogRSChanges;
18 /
Trigger created.
Cursor in trigger:
-----------------
CREATE OR REPLACE TRIGGER bi_order
2 BEFORE INSERT
3 ON ord
4 REFERENCING OLD AS OLD NEW AS NEW
5 FOR EACH ROW
6 WHEN (NEW.payment_type = 'CREDIT')
7 DECLARE
8 CURSOR cur_check_customer IS
9 SELECT 'x'
10 FROM customer
11 WHERE customer_id = :NEW.customer_id
12 AND credit_rating = 'POOR';
13 lv_temp_txt VARCHAR2(1);
14 lv_poor_credit_excep EXCEPTION;
15 BEGIN
16 OPEN cur_check_customer;
17 FETCH cur_check_customer INTO lv_temp_txt;
18 IF (cur_check_customer%FOUND) THEN
19 CLOSE cur_check_customer;
20 RAISE lv_poor_credit_excep;
21 ELSE
22 CLOSE cur_check_customer;
23 END IF;
24 EXCEPTION
25 WHEN lv_poor_credit_excep THEN
26 RAISE_APPLICATION_ERROR(-20111, 'Cannot process CREDIT ' ||
27 'order for a customer with a POOR credit rating.');
28 WHEN OTHERS THEN
29 RAISE_APPLICATION_ERROR(-20122, 'Unhandled error occurred in' ||
30 ' BI_ORDER trigger for order#:' || TO_CHAR(:NEW.ORDER_ID));
31 END bi_order;
32 /
checking status of trigger:
--------------------------
SQL> SELECT trigger_name, status FROM user_triggers;

System triggers:
---------------
There are a number of events where you can set system triggers such as
ON LOGON, ON LOGOFF, ON STARTUP, ON DROP, ON TRUNCATE, and so on.
You can even track when any DDL command (CREATE, DROP, ALTER, and so on) was exe
cuted in the database.
You may place system triggers at the database level or schema level.
At the database level, triggers fire for each event for all users.
At the schema level, triggers fire for each event for a specific user.
You create a trigger using the CREATE TRIGGER statement.
The simplified syntax for the CREATE TRIGGER statement is as follows:
CREATE [OR REPLACE] TRIGGER trigger_name
{BEFORE | AFTER | INSTEAD OF} trigger_event
ON table_name
[FOR EACH ROW [WHEN trigger_condition]]
BEGIN
trigger_body
END trigger_name;
where
OR REPLACE specifies the trigger is to replace an existing trigger if present.
BEFORE specifies the trigger fires before the triggering event is performed.
AFTER specifies the trigger fires after the triggering event is performed.
INSTEAD OF specifies the trigger fires instead of performing the triggering even
t.
trigger_event specifies the event that causes the trigger to fire.
table_name specifies the table that the trigger references.
FOR EACH ROW specifies the trigger is a row-level trigger.
A row-level trigger is run for each row when the trigger fires.
If you omit FOR EACH ROW, the trigger is a statement-level trigger.
A statement-level trigger is run once when the trigger fires regardless of the n
umber of rows affected.
trigger_condition specifies a Boolean condition that limits when a trigger actua
lly runs its code.
trigger_body contains the SQL and PL/SQL statements that perform the trigger's t
ask.

SQL> CREATE OR REPLACE TRIGGER audit_game_players


2 AFTER INSERT OR UPDATE OR DELETE ON game_player
3 FOR EACH ROW
4 BEGIN
5 IF INSERTING THEN
6 INSERT INTO game_player_audit(player_id,game_id,group_number,
7 new_marked,new_pcmac,new_score,
8 change_date,operation)
9 VALUES(:new.player_id,:new.game_id,:new.group_number,
10 :new.marked,:new.pcmac,:new.score,
11 SYSDATE,'INSERT');
12 ELSIF UPDATING THEN
13 INSERT INTO game_player_audit(player_id,game_id,group_number,
14 old_marked,new_marked,
15 old_pcmac,new_pcmac,
16 old_score,new_score,
17 change_date,operation)
18 VALUES(:new.player_id,:new.game_id,:new.group_number,
19 :old.marked,:new.marked,
20 :old.pcmac,:new.pcmac,
21 :old.score,:new.score,
22 SYSDATE,'UPDATE');
23 ELSIF DELETING THEN
24 INSERT INTO game_player_audit(player_id,game_id,group_number,
25 old_marked,old_pcmac,old_score,
26 change_date,operation)
27 VALUES(:old.player_id,:old.game_id,:old.group_number,
28 :old.marked,:old.pcmac,:old.score,
29 SYSDATE,'DELETE');
30 END IF;
31 END;
32 /

ALTER TRIGGER before_employee_salary_update DISABLE;


ALTER TRIGGER before_employee_salary_update ENABLE;
INSTEAD OF triggers exist only on views.
Their main purpose is to perform data modifications of views that are not otherw
ise updatable.
The following view isn't updatable because of the ORDER BY clause:
create or replace trigger employee_view_trigger
2 instead of update on employee_view
3 referencing new as new old as old
4 begin
5 update employee
6 set last_name = :new.last_name,
7 first_name = :new.first_name
8 where id = :old.id;
9
10 end;
11 /

CREATE OR REPLACE TRIGGER audit_schema_changes


2 AFTER ALTER ON jeff.SCHEMA
3 BEGIN
4 INSERT INTO alter_audit_trail
5 (object_owner,
6 object_name,
7 object_type,
8 altered_by_user,
9 alteration_time
10 )
11 VALUES (sys.dictionary_obj_owner,
12 sys.dictionary_obj_name,
13 sys.dictionary_obj_type,
14 sys.login_user,
15 sysdate);
16 END;
17 /
SQL> create or replace trigger v_emp_iu
2 INSTEAD OF UPDATE
3 on v_employee
4 declare
5 v_error VARCHAR2(256);
6 begin
7 if updating('ID')
8 then
9 v_error:='You cannot update the PK!';
10 raise_application_error (-20999,v_error);
11 else
12 update employee
13 set first_Name = :new.first_Name
14 where id = :old.id;
15 end if;
16 end;
17 /
CREATE OR REPLACE TRIGGER before_employee_salary_update
2 BEFORE UPDATE OF salary
3 ON employee
4 FOR EACH ROW WHEN (new.salary < old.salary * 0.75)
5 BEGIN
6 dbms_output.put_line('id = ' || :old.id);
7 dbms_output.put_line('Old salary = ' || :old.salary);
8 dbms_output.put_line('New salary = ' || :new.salary);
9 dbms_output.put_line('The salary reduction is more than 25%');
10
11 INSERT INTO Myaudit (
12 id, old_value, new_value
13 ) VALUES (
14 :old.id, :old.salary, :new.salary
15 );
16 END before_employee_salary_update;
17 /
SQL> CREATE OR REPLACE TRIGGER after_ddl_creation
2 AFTER CREATE ON SCHEMA
3 BEGIN
4 INSERT INTO myAudit VALUES
5 (SYS.DICTIONARY_OBJ_NAME,SYS.DICTIONARY_OBJ_TYPE,SYSDATE,USER,NULL,NULL);
6 END;
7 /
CREATE OR REPLACE TRIGGER no_create
2 AFTER DDL ON SCHEMA
3 BEGIN
4 IF ORA_SYSEVENT = 'CREATE' THEN
5 RAISE_APPLICATION_ERROR(-20000,'Cannot create the ' || ORA_DICT_OBJ_TYP
E ||
6 ' named ' || ORA_DICT_OBJ_NAM
E ||
7 ' as requested by ' || ORA_DICT_OBJ_OWN
ER);
8 END IF;
9 END;
10 /

CREATE OR REPLACE TRIGGER log_startup


2 AFTER STARTUP ON DATABASE
3 BEGIN
4 INSERT INTO uptime_log
5 (database_name,
6 event_name,
7 event_time,
8 triggered_by_user)
9 VALUES (sys.database_name,
10 sys.sysevent,
11 sysdate,
12 sys.login_user);
13 COMMIT;
14 END;
CREATE OR REPLACE TRIGGER idAutonumberTrigger
2 BEFORE INSERT ON myTableAudit
3 FOR EACH ROW
4 BEGIN
5 SELECT idSeq.NEXTVAL
6 INTO :NEW.id FROM DUAL;
7 END;
8 /

SQL> create trigger emp_dept_cnt_trigger


2 after insert or update or delete on emp
3 for each row
4 begin
5 if ( updating and :old.deptno = :new.deptno )
6 then
7 return;
8 end if;
9 if ( inserting or updating )
10 then
11 update dept set emp_count = emp_count+1
12 where deptno = :new.deptno;
13 end if;
14 if ( updating or deleting )
15 then
16 update dept set emp_count = emp_count-1
17 where deptno = :old.deptno;
18 end if;
19 end;
20 /
SQL> CREATE OR REPLACE TRIGGER aft_ins_ceo_comp
2 AFTER INSERT
3 ON employee_compensation
4 FOR EACH ROW
5 DECLARE
6 PRAGMA AUTONOMOUS_TRANSACTION;
7 BEGIN
8 IF :NEW.compensation > 1000000000
9 THEN
10 RAISE VALUE_ERROR;
11 ELSE
12 INSERT INTO employee_history VALUES (:NEW.NAME, 'AFTER INSERT', SYSDA
TE);
13 COMMIT;
14 END IF;
15 EXCEPTION
16 WHEN OTHERS
17 THEN
18 ROLLBACK;
19 RAISE;
20 END;
21 /
=============================================================
ACCEPT continue_flag CHAR PROMPT 'Do you wish to DROP the tables first (Y/N)?'
=============================================================
Handling Large Objects in the Database
To address performance concerns, Oracle provides two options:
You can store the large objects internally, within the database itself (CLOB, BL
OB).
You can keep the objects in the file system and just store the filenames in the
database (BFILE).
CLOB (character large object): to store large amounts of character (text) inform
ation.
BLOB (binary large object): to store binary (mostly video/audio) information in
the database.
Large Objects
Large objects (LOBs) may be used to store binary data, character data, and refer
ences to external files.
LOBs are widely used to store documents such as Word and PDF documents.
LOBs can store a maximum of 128 terabytes of data depending on the block size of
your database.
There are four LOB types:
CLOB is the character LOB type.
CLOB is used to store character data.
NCLOB is the national language character LOB type.
NCLOB is used to store multiple byte character data (typically used for non-Engl
ish characters).
BLOB is the binary LOB type.
BLOB is used to store binary data.
BFILE is the binary FILE type.
BFILE is used to store pointers to files located in the file system.
Columns created using CLOB and BLOB types have three advantages over those creat
ed using the older LONG and LONG RAW types:
LOB columns can store up to 128 terabytes of data.
This is far more data than you can store in a LONG and LONG RAW column.
A LONG and LONG RAW column may only store up to 2 gigabytes of data.
Note: The RAW type may store up to 4 kilobytes of data.
A table can have multiple LOB columns, but a table can only have one LONG or LON
G RAW column.
LOB data can be accessed in random order.
LONG and LONG RAW data can only be accessed in sequential order.
A LOB consists of two parts:
The LOB locator A pointer that specifies the location of the LOB content.
The LOB content The actual character or byte data stored in the LOB.
Depending on the size of the LOB content, the actual data will either be stored
in the table or out of the table.
If the LOB content is less than 4 kilobytes in size, the content is stored in th
e table containing the LOB column.
If it's bigger, the content is stored outside the table.
With BFILE columns, only the locator is stored in the database-the locator point
s to the external file containing the LOB content.

To initialize a CLOB or NCLOB column, you use the EMPTY_CLOB() function.


A BLOB column must be initialized using the EMPTY_BLOB() function.

LOB columns can store up to 128 terabytes of data.


LOB columns store a locator that points to the LOB contents.
Initializing a CLOB and BLOB
Before you can actually write content to a LOB, you must first initialize the LO
B column.
You do this by calling an Oracle database function that generates and returns a
value for the locator.
To initialize a CLOB or NCLOB column, you use the EMPTY_CLOB() function.
A BLOB column must be initialized using the EMPTY_BLOB() function.
You use the Oracle database's BFILENAME() function to populate the BFILE column
with a pointer to your external file.
The BFILENAME() function accepts two parameters:
the database directory object's name (which was created earlier) and the name of
the file.
Oracle accesses files on the server by using a directory.
A directory is just a pointer to an operating system folder.
Assuming that a folder C:\IO exists on your server, and you want to call that fo
lder IO within Oracle:
create directory IO as 'C:\IO';
grant read, write on directory IO to public;
Now, when you refer to IO in any commands, you're referring to the C:\IO folder
in the file system.
To create a pointer to the file on the server and place that pointer in the tabl
e on an existing record.

SQL>
SQL> CREATE DIRECTORY bfile_dir AS 'c:\proj';

SQL> CREATE TABLE myBFile


2 (id NUMBER PRIMARY KEY,
3 bfile_data BFILE);
Table created.
SQL>
SQL> INSERT INTO myBFile VALUES (1,BFILENAME('BFILE_DIR','test.bmp'));
1 row created.
SQL> CREATE TABLE myBFile
2 (id NUMBER PRIMARY KEY,
3 bfile_data BFILE);
Table created.
SQL>
SQL> CREATE DIRECTORY bfile_dir AS 'c:\proj';

SQL>
SQL> DECLARE
2 bfileValue BFILE;
3 BEGIN
4 INSERT INTO myBFile VALUES (1,BFILENAME('BFILE_DIR','test.bmp'));
5 SELECT bfile_data
6 INTO bfileValue
7 FROM myBFile
8 WHERE id = 1;
9
10 END;
11 /

create or replace
2 function trace_file_contents( p_filename in varchar2 )
3 return vcArray
4 pipelined
5 as
6 l_bfile bfile := bfilename('UDUMP_DIR',p_filename);
7 l_last number := 1;
8 l_current number;
9 begin
10 select rownum into l_current
11 from user_avail_trace_files
12 where filename = p_filename;
13
14 dbms_lob.fileopen( l_bfile );
15 loop
16 l_current := dbms_lob.instr( l_bfile, '0A', l_last, 1 );
17 exit when (nvl(l_current,0) = 0);
18 pipe row(utl_raw.cast_to_varchar2(dbms_lob.substr( l_bfile, l_curre
nt-l_last+1,l_last ) ));
19 l_last := l_current+1;
20 end loop;
21 dbms_lob.fileclose(l_bfile);
22 return;
23 end;
SQL>
SQL> DECLARE
2 bfileValue BFILE;
3 BEGIN
4 INSERT INTO myBFile VALUES (1,BFILENAME('BFILE_DIR','test.bmp'));
5 SELECT bfile_data
6 INTO bfileValue
7 FROM myBFile
8 WHERE id = 1;
9
10 END;
11 /
SQL> CREATE TABLE facebook (
2 name VARCHAR2(80),
3 photo BLOB,
4 directions CLOB,
5 description NCLOB,
6 web_page BFILE);
Table created.
SQL>
SQL> CREATE DIRECTORY bfile_data AS 'c:\xxx';
Directory created.
SQL>
SQL>
SQL> DECLARE
2 web_page BFILE;
3 BEGIN
4 DELETE FROM facebook WHERE name='Tannery Falls';
5
6 web_page := BFILENAME('BFILE_DATA','Tannery Falls.htm');
7
8 INSERT INTO facebook (name, web_page) VALUES ('Tannery Falls',web_page);
9 END;
10 /
SQL> CREATE TABLE waterfalls (
2 falls_name VARCHAR2(80),
3 falls_photo BLOB,
4 falls_directions CLOB,
5 falls_description NCLOB,
6 falls_web_page BFILE);
Table created.
SQL>
SQL> CREATE DIRECTORY bfile_data AS 'c:\xxx';
Directory created.
SQL>
SQL> DECLARE
2 web_page BFILE;
3 html RAW(60);
4 amount BINARY_INTEGER := 60;
5 offset INTEGER := 1;
6 BEGIN
7
8 SELECT falls_web_page
9 INTO web_page
10 FROM waterfalls
11 WHERE falls_name='Tannery Falls';
12
13 DBMS_LOB.OPEN(web_page);
14 DBMS_LOB.READ(web_page, amount, offset, html);
15 DBMS_LOB.CLOSE(web_page);
16
17 DBMS_OUTPUT.PUT_LINE(RAWTOHEX(html));
18
19 DBMS_OUTPUT.PUT_LINE(UTL_RAW.CAST_TO_VARCHAR2(html));
20 END;
Use a BFILE to load a LOB column
SQL>
SQL> CREATE TABLE facebook (
2 name VARCHAR2(80),
3 photo BLOB,
4 directions CLOB,
5 description NCLOB,
6 web_page BFILE);
Table created.
SQL>
SQL> CREATE DIRECTORY bfile_data AS 'c:\xxx';
Directory created.
SQL>
SQL>
SQL> DECLARE
2 myBFile BFILE := BFILENAME('BFILE_DATA','TanneryFalls.directions');
3 directions CLOB;
4 destination_offset INTEGER := 1;
5 source_offset INTEGER := 1;
6 language_context INTEGER := DBMS_LOB.default_lang_ctx;
7 warning_message INTEGER;
8 BEGIN
9
10 DELETE FROM facebook WHERE name='Falls';
11
12 INSERT INTO facebook (name,directions)VALUES ('Falls',EMPTY_CLOB());
13
14 SELECT directions INTO directions FROM facebook WHERE name='Falls';
15
16 DBMS_LOB.OPEN(directions, DBMS_LOB.LOB_READWRITE);
17 DBMS_LOB.OPEN(myBFile);
18
19 DBMS_LOB.LOADCLOBFROMFILE(directions, myBFile,
20 DBMS_LOB.LOBMAXSIZE,
21 destination_offset, source_offset,
22 NLS_CHARSET_ID('US7ASCII'),
23 language_context, warning_message);
24
25 IF warning_message = DBMS_LOB.WARN_INCONVERTIBLE_CHAR THEN
26 dbms_output.put_line('Warning! Some characters couldn''t be convert
ed.');
27 END IF;
28
29 DBMS_LOB.CLOSE(directions);
30 DBMS_LOB.CLOSE(myBFile);
31 END;
32 /
SQL> CREATE OR REPLACE PROCEDURE initClob(clob_par IN OUT CLOB,id_par IN INTEGER
) IS
2 BEGIN
3 SELECT clob_column INTO clob_par FROM myTable WHERE id = id_par;
4 END initClob;
5 /
Procedure created.
SQL>
SQL>
SQL> CREATE OR REPLACE PROCEDURE readClob(id_par IN INTEGER) IS
2 clob_var CLOB;
3 char_buffer_var VARCHAR2(50);
4 offset INTEGER := 1;
5 amount_var INTEGER := 50;
6 BEGIN
7 initClob(clob_var, id_par);
8 DBMS_LOB.READ(clob_var, amount_var, offset, char_buffer_var);
9 DBMS_OUTPUT.PUT_LINE('char_buffer_var = ' || char_buffer_var);
10 DBMS_OUTPUT.PUT_LINE('amount_var = ' || amount_var);
11 END readClob;
12 /
SQL> INSERT INTO myTable(id, clob_column) VALUES (1, EMPTY_CLOB());
1 row created.
SQL>
SQL>
SQL> UPDATE myTable
2 SET clob_column = 'AAAAA'
3 WHERE id = 1;
SQL> declare
2 v_file_bf BFILE;
3 v_manual_cl CLOB;
4 lang_ctx NUMBER := DBMS_LOB.default_lang_ctx;
5 charset_id NUMBER := 0;
6 src_offset NUMBER := 1;
7 dst_offset NUMBER := 1;
8 warning NUMBER;
9 begin
10 update catalog set manual_cl = EMPTY_CLOB() where id = 1;
11
12 select mastertxt_bf, manual_cl into v_file_bf, v_manual_cl from catalog
where id = 1;
13
14 DBMS_LOB.fileopen(v_file_bf, DBMS_LOB.file_readonly);
15 DBMS_LOB.loadclobfromfile (v_manual_cl, v_file_bf,DBMS_LOB.getlength (v
_file_bf),
16 src_offset, dst_offset,charset_id, lang_ctx,war
ning);
17 DBMS_LOB.fileclose (v_file_bf);
18 end;
19 /
SQL> declare
2 v_manual_cl CLOB;
3 v_nr NUMBER;
4 v_tx VARCHAR2 (2000);
5 v_add_tx VARCHAR2 (2000):='Loaded: '||TO_CHAR(SYSDATE,'mm/dd/yyyy
hh24:mi');
6 begin
7 select manual_cl into v_manual_cl from catalog where id = 1
8 for update;
9
10 DBMS_LOB.writeappend (v_manual_cl,LENGTH (v_add_tx), v_add_tx);
11
12 v_nr := INSTR (v_manual_cl, 'Loaded:', -1);
13 v_tx := SUBSTR (v_manual_cl, v_nr);
14 DBMS_OUTPUT.put_line (v_tx);
15 end;

SQL> DECLARE
2 web_page BFILE;
3 html RAW(60);
4 amount BINARY_INTEGER := 60;
5 offset INTEGER := 1;
6 BEGIN
7
8 SELECT falls_web_page
9 INTO web_page
10 FROM waterfalls
11 WHERE falls_name='Tannery Falls';
12
13 DBMS_LOB.OPEN(web_page);
14 DBMS_LOB.READ(web_page, amount, offset, html);
15 DBMS_LOB.CLOSE(web_page);
16
17 DBMS_OUTPUT.PUT_LINE(RAWTOHEX(html));
18
19 DBMS_OUTPUT.PUT_LINE(UTL_RAW.CAST_TO_VARCHAR2(html));
20 END;
21 /
SQL> CREATE OR REPLACE PROCEDURE initClob(clob_par IN OUT CLOB,id_par IN INTEGE
R) IS
2 BEGIN
3 SELECT clobData INTO clob_par FROM myTable WHERE id = id_par;
4 END initClob;
5 /
SQL> declare
2 clob_pointer CLOB;
3 v_buf VARCHAR2(1000);
4 Amount BINARY_INTEGER :=1000;
5 Position INTEGER :=1;
6 BEGIN
7 v_buf :=rpad('A',1000,'A');
8
9 insert into myClob values (1 ,EMPTY_CLOB());
10
11 commit;
12
13 SELECT clob_data INTO clob_pointer FROM myClob WHERE id = 1 FOR UPDATE;
14 DBMS_LOB.OPEN (clob_pointer,DBMS_LOB.LOB_READWRITE);
15
16 FOR i IN 1..500 LOOP
17
18 DBMS_LOB.WRITE (clob_pointer,Amount,Position,v_buf);
19
20 Position :=Position +Amount;
21
22 END LOOP;
23
24 DBMS_LOB.CLOSE (clob_pointer);
25
26 END;
27 /

SQL> CREATE OR REPLACE PROCEDURE readClob(id_par IN INTEGER) IS


2 clobVariable CLOB;
3 charVariable VARCHAR2(50);
4 offsetPos INTEGER := 1;
5 amount_var INTEGER := 50;
6 BEGIN
7 initClob(clobVariable, id_par);
8 DBMS_LOB.READ(clobVariable, amount_var, offsetPos, charVariable);
9 DBMS_OUTPUT.PUT_LINE('charVariable = ' || charVariable);
10 DBMS_OUTPUT.PUT_LINE('amount_var = ' || amount_var);
11 END readClob;
12 /
Procedure created.
SQL>
SQL>
SQL> CREATE OR REPLACE PROCEDURE write_example(id_par IN INTEGER) IS
2 clobVariable CLOB;
3 charVariable VARCHAR2(10) := 'pretty';
4 offsetPos INTEGER := 7;
5 amount_var INTEGER := 6;
6 BEGIN
7 SELECT clobData INTO clobVariable FROM myTable WHERE id = id_par FOR UPDA
TE;
8
9 readClob(1);
10 DBMS_LOB.WRITE(clobVariable, amount_var, offsetPos, charVariable);
11 readClob(1);
12
13 END write_example;

DECLARE
2 bfile_pointer BFILE;
3 blob_pointer BLOB;
4 bfile_offset NUMBER :=1;
5 blob_offset NUMBER :=1;
6 tot_len INTEGER;
7 BEGIN
8
9 INSERT INTO myBlob VALUES (1,EMPTY_BLOB());
10
11 SELECT blob_data INTO blob_pointer FROM myBlob WHERE id = 1 FOR UPDATE;
12
13 bfile_pointer :=bfilename('BFILE_DIR','test.bmp');
14
15 dbms_lob.fileopen(bfile_pointer,dbms_lob.file_readonly);
16
17 dbms_lob.OPEN(blob_pointer,dbms_lob.lob_readwrite);
18
19 dbms_lob.LOADBLOBFROMFILE(blob_pointer,bfile_pointer,dbms_lob.lobmaxsize,
bfile_offset,blob_offset);
20
21 tot_len :=DBMS_LOB.GETLENGTH(blob_pointer);
22
23 dbms_lob.close(blob_pointer);
24
25 dbms_lob.fileclose(bfile_pointer);
26
27 DBMS_OUTPUT.PUT_LINE(TO_CHAR(tot_len));
28 END;
29 /
SQL> CREATE OR REPLACE PROCEDURE initClob(clob_par IN OUT CLOB,id_par IN INTEGER
) IS
2 BEGIN
3 SELECT clobData INTO clob_par FROM myTable WHERE id = id_par;
4 END initClob;
5 /
Procedure created.
SQL>
SQL>
SQL> CREATE OR REPLACE PROCEDURE readClob(id_par IN INTEGER) IS
2 clobVariable CLOB;
3 charVariable VARCHAR2(50);
4 offsetPos INTEGER := 1;
5 amount_var INTEGER := 50;
6 BEGIN
7 initClob(clobVariable, id_par);
8 DBMS_LOB.READ(clobVariable, amount_var, offsetPos, charVariable);
9 DBMS_OUTPUT.PUT_LINE('charVariable = ' || charVariable);
10 DBMS_OUTPUT.PUT_LINE('amount_var = ' || amount_var);
11 END readClob;
12 /
Procedure created.
SQL>
SQL> CREATE OR REPLACE PROCEDURE copy_example IS
2 clobSrc CLOB;
3 clobDest CLOB;
4 src_offsetPos INTEGER := 1;
5 dest_offsetPos INTEGER := 7;
6 amount_var INTEGER := 5;
7 BEGIN
8 SELECT clobData INTO clobSrc FROM myTable WHERE id = 2;
9 SELECT clobData INTO clobDest FROM myTable WHERE id = 1 FOR UPDATE;
10
11 readClob(1);
12 DBMS_LOB.COPY(clobDest, clobSrc, amount_var,dest_offsetPos, src_offsetPos
);
13 readClob(1);
14
15 ROLLBACK;
16 END copy_example;
17 /
SQL> declare
2
3 c1 clob := to_clob('abc');
4 c2 clob;
5
6 begin
7 case c1
8 when to_clob('abc') then dbms_output.put_line('abc');
9 when to_clob('def') then dbms_output.put_line('def');
10 end case;
11
12 c2 := case c1
13 when to_clob('abc') then 'abc'
14 when to_clob('def') then 'def'
15 end;
16
17 dbms_output.put_line(c2);
18
19 end;
20 /
abc
abc

QL> declare
2 clob_pointer CLOB;
3 v_buf VARCHAR2(1000);
4 Amount BINARY_INTEGER :=1000;
5 Position INTEGER :=1;
6 BEGIN
7 v_buf :=rpad('A',1000,'A');
8
9 insert into myClob values (1 ,EMPTY_CLOB());
10
11 commit;
12
13 SELECT clob_data INTO clob_pointer FROM myClob WHERE id = 1 FOR UPDATE;
14 DBMS_LOB.OPEN (clob_pointer,DBMS_LOB.LOB_READWRITE);
15
16 FOR i IN 1..500 LOOP
17
18 DBMS_LOB.WRITE (clob_pointer,Amount,Position,v_buf);
19
20 Position :=Position +Amount;
21
22 END LOOP;
23
24 DBMS_LOB.CLOSE (clob_pointer);
25
26 END;
27 /

SQL> DECLARE
2 bfile_pointer BFILE;
3 blob_pointer BLOB;
4 bfile_offset NUMBER :=1;
5 blob_offset NUMBER :=1;
6 tot_len INTEGER;
7 BEGIN
8
9 INSERT INTO myBlob VALUES (1,EMPTY_BLOB());
10
11 SELECT blob_data INTO blob_pointer FROM myBlob WHERE id = 1 FOR UPDATE;
12
13 bfile_pointer :=bfilename('BFILE_DIR','test.bmp');
14
15 dbms_lob.fileopen(bfile_pointer,dbms_lob.file_readonly);
16
17 dbms_lob.OPEN(blob_pointer,dbms_lob.lob_readwrite);
18
19 dbms_lob.LOADBLOBFROMFILE(blob_pointer,bfile_pointer,dbms_lob.lobmaxsize,
bfile_offset,blob_offset);
20
21 tot_len :=DBMS_LOB.GETLENGTH(blob_pointer);
22
23 dbms_lob.close(blob_pointer);
24
25 dbms_lob.fileclose(bfile_pointer);
26
27 DBMS_OUTPUT.PUT_LINE(TO_CHAR(tot_len));
28 END;
29 /
Creating a Directory Object
Before you can store a pointer to a file in a BFILE column, you must first creat
e a directory object.
The directory object represents the directory in the file system.
You create a directory object using the CREATE DIRECTORY statement.
To perform such a statement, you must have the CREATE ANY DIRECTORY database pri
vilege.
CREATE OR REPLACE DIRECTORY SAMPLE_FILES_DIR AS 'C:\';
When you create a directory object you must ensure that
The actual directory exists in the file system.
The user account has read permission on the directories and files.

SQL> declare
2 x long;
3 y clob;
4 begin
5 select long_data
6 into x
7 from myLongTable
8 where id =100;
9 y :=to_clob(x);
10 insert into myClobTable values (200,y);
11 end;

===============================================================
An execution plan is a list of steps that Oracle will
follow in order to execute a SQL statement. Each
step is one of a finite number of basic operations
known to the database server. Even the most
complex SQL statement can be broken down into a
series of basic operations.
EXPLAIN PLAN is a statement that allows you to
have Oracle generate the execution plan for any
SQL statement without actually executing it. You
will be able to examine the execution plan by
querying the plan table.
A plan table holds execution plans generated by
the EXPLAIN PLAN statement.
The typical name for a plan table
is plan_table, but you may use any
name you wish.
Create the plan table by running
utlxplan.sql, located in
$ORACLE_HOME/rdbms/admin.
Important Columns in the Plan Table
statement_id Unique identifier for each execution plan
timestamp When the execution plan was generated
operation The operation performed in one step of the execution
plan, such as “table access”
options Additional information about the operation, such as “by
index ROWID”
object_name Name of table, index, view, etc. accessed
optimizer Optimizer goal used when creating execution plan
id Step number in execution plan
parent_id Step number of parent step
EXPLAIN PLAN
SET STATEMENT_ID = 'statement id'
INTO PLAN_TABLE FOR
statement
utlxplp.sql
utlxpls.sql
select * from table(dbms_xplan.display());
SELECT id, parent_id, LPAD (' ', LEVEL - 1) ||
operation || ' ' || options operation,
object_name
FROM plan_table
WHERE statement_id = '&stmt_id'
START WITH id = 0
AND statement_id = '&stmt_id'
CONNECT BY PRIOR id = parent_id
AND statement_id = '&stmt_id';
set autotrace on: Shows the execution plan as well as statistics of the st
atement.
set autotrace on explain: Displays the execution plan only.
set autotrace on statistics: Displays the statistics only.
set autotrace traceonly: Displays the execution plan and the statistics (
as set autotrace on does), but doesn't print a query's result.
set autotrace off: Disables all autotrace

Trace Files and TKPROF


---------------------
TKPROF is a utility provided by Oracle that formats SQL trace files
into very helpful and readable reports. TKPROF is installed
automatically when the database server software is installed. You
invoke TKPROF from the operating system command line; there is
no graphical interface for TKPROF. Starting in Oracle 9i TKPROF
can read extended SQL trace files and report on wait events
statistics.
Enabling SQL Trace
At the instance level:
sql_trace = true
timed_statistics = true (optional)
In your own session:
ALTER SESSION SET sql_trace = TRUE;
ALTER SESSION SET timed_statistics = TRUE; (optional)
In another session:
SYS.dbms_system.set_sql_trace_in_session
(<SID>, <serial#>, TRUE)
Look in the user dump destination. On OFA
compliant systems this will be
$ORACLE_BASE/admin/$ORACLE_SID/udump
Check timestamps and file contents to see
which trace file is yours
If non-DBAs need access to trace files, add
_trace_files_public = true to the parameter file to avoid permissions problems
on Unix platforms
Use a dedicated server connection when
tracing, if possible.
Invoke TKPROF from the operating
system prompt like this:
tkprof <trace file> <output file> \
[explain=<username/password>] \
[sys=n] [sort=<keyword>]

Trace file is created in <USER_DUMP_DEST>


directory on the server (specified by the DBA).
tkprof
echd_ora_15319.trc $HOME/prof.out
table=plan_table
explain=dbuser/passwd
An index is a database object used to speed retrieval
of rows in a table.
• The index contains only the indexed value--usually the
key(s)--and a pointer to the row in the table.
• Multiple indexes may be created for a table
• Not all indexes contain unique values
• Indexes may have multiple columns (e.g., Oracle
allows up to 32)
• If a column appears in a WHERE clause it is a
candidate for being indexed.
• If a column is indexed the database can used the
index to find the rows instead of scanning the table.
• If the column is not referenced properly, however,
the database may not be able to used the index and
will have to scan the table anyway.
• Knowing what columns are and are not indexed can
help you write more efficient SQL
Using a function, calculation, or other operation on an
indexed column disables the use of the Index
Using NOT excludes indexed columns:
The Optimizer: Hints
Return the first rows in the result set as fast as possible:
Forces the use of a full table scan on the specified table.
SELECT /*+ FULL(emp) */ ename
FROM emp
WHERE commencement_date > sysdate - 7
If several indexes are listed, the optimizer calculates the cost of only those i
ndexes that are specified, and uses the most efficient (several indexes from the
list may be used in tandem if they are single-column indexes). If a single inde
x is specified, the optimizer performs a scan using that index.
SELECT /*+ INDEX(EMP EMP_NDX1) */
SELECT /*+ FIRST_ROWS */ empno
FROM emp E
dept D,
WHERE E.deptno = D.deptno;
Force Optimizer to use index IDX_HIREDATE:
SELECT /*+ INDEX (E idx_hiredate) */ empno
FROM emp E
WHERE E.hiredate > TO_DATE('01-JAN-2000');
FROM Clause: Driving Table
Specify the driving table last in the FROM Clause:
SELECT *
FROM dept D, -- 10 rows
emp E -- 1,000 rows
WHERE E.deptno = D.deptno;
SELECT *
FROM emp E, -- 1,000 rows
dept D -- 10 rows
WHERE E.deptno = D.deptno;
SELECT *
FROM dept D,
salgrade S,
emp E
WHERE E.deptno = D.deptno
AND E.grade = S.grade;
EMP shares columns with
DEPT and SALGRADE,
so use as the driving table
Use WHERE clauses first which discard the maximum
number of rows:
SELECT *
FROM emp E
WHERE E.empno IN (101, 102, 103)
AND E.deptno > 10;

When using an "AND" subquery, place it first:


SELECT *
FROM emp E
WHERE 25 > (SELECT COUNT(*)
FROM emp M
WHERE M.mgr = E.empno)
AND E.sal > 50000
When using an "OR" subquery, place it last:
SELECT *
FROM emp E
WHERE E.sal > 50000
OR 25 > (SELECT COUNT(*)
FROM emp M
WHERE M.mgr = E.empno)
When Joining and Filtering, specify the Filter condition
first, Joins last.
SELECT *
FROM emp E,
dept D
WHERE (E.empno = 123
OR D.deptno > 10)
AND E.deptno = D.deptno;
Use EXISTS instead of IN in subqueries:
SELECT E.*
FROM emp E
WHERE E.deptno IN (
SELECT D.deptno
FROM dept D
WHERE D.dname = 'SALES');
SELECT *
FROM emp E
WHERE EXISTS (
SELECT 'X'
FROM dept D
WHERE D.deptno = E.deptno
AND D.dname = 'SALES');

===============================================================
EXECUTE IMMEDIATE v_sql_tx INTO v_out_tx;
EXECUTE IMMEDIATE sql_stmt USING emp_id RETURNING INTO salary;
CREATE OR REPLACE PROCEDURE list_employees(loc VARCHAR2, job VARCHAR2)
IS
TYPE cur_typ IS REF CURSOR;
C cur_typ;
query_str VARCHAR2(1000);
emp_name VARCHAR2(20);
emp_num NUMBER;
BEGIN
query_str := 'SELECT ename, empno FROM emp_' || loc || ' WHERE job = :job_title'
;
-- find employees who perform the specified job
OPEN C FOR query_str USING job;
LOOP
FETCH C INTO emp_name, emp_num;
EXIT WHEN C%NOTFOUND;
-- process row here
END LOOP;
CLOSE C;
END;

CREATE OR REPLACE PROCEDURE updnumval (


2 col_in IN VARCHAR2,
3 start_in IN DATE,
4 end_in IN DATE,
5 val_in IN NUMBER)
6 IS
7 cur PLS_INTEGER := DBMS_SQL.OPEN_CURSOR;
8 returnValue PLS_INTEGER;
9 BEGIN
10 DBMS_SQL.PARSE (cur, 'UPDATE emp SET ' || col_in ||' = :val WHERE hireda
te BETWEEN :lodate AND :hidate',DBMS_SQL.NATIVE);
11 DBMS_SQL.BIND_VARIABLE (cur, 'val', val_in);
12 DBMS_SQL.BIND_VARIABLE (cur, 'lodate', start_in);
13 DBMS_SQL.BIND_VARIABLE (cur, 'hidate', end_in);
14 returnValue := DBMS_SQL.EXECUTE (cur);
15 DBMS_OUTPUT.PUT_LINE ('Rows updated: ' || TO_CHAR (returnValue));
16 DBMS_SQL.CLOSE_CURSOR (cur);
17 END;
18 /
CREATE OR REPLACE FUNCTION math_calc(p_statement_txt VARCHAR2,
2 p_precision_num PLS_INTEGER := 2)
3 RETURN NUMBER IS
4 lv_cursor_id_num PLS_INTEGER;
5 lv_statement_txt VARCHAR2(500);
6 lv_rowcount_num PLS_INTEGER;
7 lv_return_value_num NUMBER;
8 BEGIN
9 lv_cursor_id_num := DBMS_SQL.OPEN_CURSOR;
10 lv_statement_txt := 'BEGIN ' ||' :lv_value_num := ' || p_statement_tx
t || ';' ||'END;';
11 DBMS_SQL.PARSE(lv_cursor_id_num, lv_statement_txt,DBMS_SQL.NATIVE);
12 DBMS_SQL.BIND_VARIABLE(lv_cursor_id_num, ':lv_value_num', lv_return_valu
e_num);
13
14 lv_rowcount_num := DBMS_SQL.EXECUTE(lv_cursor_id_num);
15
16 DBMS_SQL.VARIABLE_VALUE(lv_cursor_id_num, ':lv_value_num',
17 lv_return_value_num);
18 DBMS_SQL.CLOSE_CURSOR(lv_cursor_id_num);
19 RETURN ROUND(lv_return_value_num, p_precision_num);
20 EXCEPTION
21 WHEN OTHERS THEN
22 IF DBMS_SQL.IS_OPEN(lv_cursor_id_num) THEN
23 DBMS_SQL.CLOSE_CURSOR(lv_cursor_id_num);
24 END IF;
25 RAISE_APPLICATION_ERROR(-20101, 'Error processing SQL ' ||
26 'statement in MATH_CALC procedure', FALSE);
27 END math_calc;
28 /

SQL> create or replace procedure print_table( p_query in varchar2 ) AUTHID CURRE


NT_USER is
2 l_theCursor integer default dbms_sql.open_cursor;
3 l_columnValue varchar2(4000);
4 l_status integer;
5 l_descTbl dbms_sql.desc_tab;
6 l_colCnt number;
7 begin
8 dbms_sql.parse(l_theCursor,p_query,dbms_sql.native);
9
10 dbms_sql.describe_columns( l_theCursor, l_colCnt, l_descTbl);
11
12 for i in 1 .. l_colCnt loop
13 dbms_sql.define_column(l_theCursor, i, l_columnValue, 4000);
14 end loop;
15
16 l_status := dbms_sql.execute(l_theCursor);
17
18 while ( dbms_sql.fetch_rows(l_theCursor) > 0 ) loop
19 for i in 1 .. l_colCnt loop
20
21 dbms_sql.column_value( l_theCursor, i, l_columnValue );
22
23 dbms_output.put_line( rpad( l_descTbl(i).col_name, 30 )|| ': ' |
|l_columnValue );
24 end loop;
25 end loop;
26 exception
27 when others then dbms_sql.close_cursor( l_theCursor ); RAISE;
28 end;
29 /
Procedure created.
SQL>
SQL> exec print_table('select * from dept')
DEPTNO : 10
DNAME : ACCOUNTING
LOC : NEW YORK
DEPTNO : 20
DNAME : RESEARCH
LOC : DALLAS
DEPTNO : 30
DNAME : SALES
LOC : CHICAGO
DEPTNO : 40
DNAME : OPERATIONS
LOC : BOSTON
DEPTNO : 10
DNAME : ACCOUNTING
LOC : NEW YORK
DEPTNO : 20
DNAME : RESEARCH
LOC : DALLAS
DEPTNO : 30
DNAME : SALES
===============================================================
wrap iname=myscript.pls oname=xxxx.plb
DECLARE
fHandler UTL_FILE.FILE_TYPE;
BEGIN
fHandler := UTL_FILE.FOPEN('MYDIR', 'myfile', 'w');
UTL_FILE.PUTF(fHandler, 'Look ma, Im writing to a file!!!\n');
UTL_FILE.FCLOSE(fHandler);
EXCEPTION
WHEN utl_file.invalid_path THEN
raise_application_error(-20000, 'Invalid path. Create directory or set UTL_
FILE_DIR.');
END;
/

DECLARE
fHandler UTL_FILE.FILE_TYPE;
buf varchar2(4000);
BEGIN
fHandler := UTL_FILE.FOPEN('MYDIR', 'myfile', 'r');
UTL_FILE.GET_LINE(fHandler, buf);
dbms_output.put_line('DATA FROM FILE: '||buf);
UTL_FILE.FCLOSE(fHandler);
EXCEPTION
WHEN utl_file.invalid_path THEN
raise_application_error(-20000, 'Invalid path. Create directory or set UTL_
FILE_DIR.');
END;
/

CREATE OR REPLACE DIRECTORY SAMPLE_FILES_DIR AS 'C:\';

CREATE OR REPLACE PROCEDURE DEPARTMENTS(NO IN DEPT.DEPTNO%TYPE) AS


v_cursor integer;
v_dname char(20);
v_rows integer;
BEGIN
v_cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(v_cursor, 'select dname from dept where deptno > :x', DBMS_SQL.
V7);
DBMS_SQL.BIND_VARIABLE(v_cursor, ':x', no);
DBMS_SQL.DEFINE_COLUMN_CHAR(v_cursor, 1, v_dname, 20);
v_rows := DBMS_SQL.EXECUTE(v_cursor);
loop
if DBMS_SQL.FETCH_ROWS(v_cursor) = 0 then
exit;
end if;
DBMS_SQL.COLUMN_VALUE_CHAR(v_cursor, 1, v_dname);
DBMS_OUTPUT.PUT_LINE('Deptartment name: '||v_dname);
end loop;
DBMS_SQL.CLOSE_CURSOR(v_cursor);
EXCEPTION
when others then
DBMS_SQL.CLOSE_CURSOR(v_cursor);
raise_application_error(-20000, 'Unknown Exception Raised: '||sqlcode||'
'||sqlerrm);
END;
/
===============================================================
A tablespace is a storage location where the actual data underlying database obj
ects can be kept. It is the physical portion of the database used to allocate
storage for all DBMS managed segments. A database segment is a database object w
hich occupies physical space such as table data and indexes.
Once created, a tablespace can be referred to by name when creating database seg
ments.
create tablespace ts_something
logging
datafile '/dbf1/ts_sth.dbf'
size 32m
autoextend on
next 32m maxsize 2048m
extent management local;
create tablespace data
datafile '/home/oracle/databases/ora10/data.dbf'
size 10M
autoextend on maxsize 200M
extent management local uniform size 64K;
Temporary tablespace
create temporary tablespace temp_mtr
tempfile '/dbf1/mtr_temp01.dbf'
size 32m
autoextend on
next 32m maxsize 2048m
extent management local;
Note, a temporary tablespace has tempfiles, not datafiles.
Undo tablespace
create undo tablespace ts_undo
datafile '/dbf/undo.dbf'
size 100M;
Misc
More than one datafile can be created with a single create tablespace command:
create tablespace ts_sth
datafile 'c:\xx\sth_01.dbf' size 4M autoextend off,
'c:\xx\sth_02.dbf' size 4M autoextend off,
'c:\xx\sth_03.dbf' size 4M autoextend off
logging
extent management local;
==============================================================================
====
Escape wildcard characters
The LIKE keyword allows for string searches. The '_' wild card character is used
to match exactly one character, while '%' is used to match zero or more occurre
nces of any characters. These characters can be escaped in SQL. Examples:
SELECT name FROM emp
WHERE id LIKE '%/_%' ESCAPE '/';
SELECT name FROM emp
WHERE id LIKE '%\%%' ESCAPE '\';
Escape ampersand (&) characters in SQL*Plus
When using SQL*Plus, the DEFINE setting can be changed to allow &'s (ampersands)
to be used in text:
SET DEFINE ~
SELECT 'Laurel & Hardy' FROM dual;
Other methods:
Define an escape character:
SET ESCAPE '\'
SELECT '\&abc' FROM dual;
Don't scan for substitution variables:
SET SCAN OFF
SELECT '&ABC' x FROM dual;
ORDER BY dbms_random.value()
This method orders the data by a random column number. Example:
SQL> SELECT * FROM (SELECT ename
2 FROM emp
3 ORDER BY dbms_random.value())
4 WHERE rownum <= 3;
ENAME
----------
WARD
MILLER
TURNER
How does one eliminate duplicates rows from a table?
Choose one of the following queries to identify or remove duplicate rows from a
table leaving only unique records in the table:
Method 1:
Delete all rowids that is BIGGER than the SMALLEST rowid value (for a given key)
:
SQL> DELETE FROM table_name A
2 WHERE ROWID > ( SELECT min(rowid)
3 FROM table_name B
4 WHERE A.key_values = B.key_values );
Method 2:
This method is usually faster. However, remember to recreate all indexes, constr
aints, triggers, etc. on the table when done.
SQL> create table table_name2 as select distinct * from table_name1;
SQL> drop table table_name1;
SQL> rename table_name2 to table_name1;

Description Date Expression


Now SYSDATE
Tomorow/ next day SYSDATE + 1
Seven days from now SYSDATE + 7
One hour from now SYSDATE + 1/24
Three hours from now SYSDATE + 3/24
A half hour from now SYSDATE + 1/48
10 minutes from now SYSDATE + 10/1440
30 seconds from now SYSDATE + 30/86400
Tomorrow at 12 midnight TRUNC(SYSDATE + 1)
Tomorrow at 8 AM TRUNC(SYSDATE + 1) + 8/24
Next Monday at 12:00 noon NEXT_DAY(TRUNC(SYSDATE), 'MONDAY') + 12/24
First day of the month at 12 midnight TRUNC(LAST_DAY(SYSDATE ) + 1)
The next Monday, Wednesday or Friday at 9 a.m TRUNC(LEAST(NEXT_DAY(sysdate, 'M
ONDAY'), NEXT_DAY(sysdate, 'WEDNESDAY'), NEXT_DAY(sysdate, 'FRIDAY'))) + 9/24
Can one retrieve only rows X to Y from a table?
SELECT * FROM (
SELECT ename, rownum rn
FROM emp WHERE rownum < 101
) WHERE RN between 91 and 100 ;

SELECT *
FROM tableX
WHERE rowid in (
SELECT rowid FROM tableX
WHERE rownum <= 7
MINUS
SELECT rowid FROM tableX
WHERE rownum < 5);
Can one retrieve only the Nth row from a table?
SELECT * FROM t1 a
WHERE n = (SELECT COUNT(rowid)
FROM t1 b
WHERE a.rowid >= b.rowid);
3rd largest
SELECT * FROM t1 a
WHERE 3 = (SELECT COUNT(rowid)
FROM t1 b
WHERE a.rowid <= b.rowid);
SELECT * FROM (
SELECT ENAME,ROWNUM RN FROM EMP WHERE ROWNUM < 101 )
WHERE RN = 100;

How does one code a hierarchical tree-structured query?


SQL> SELECT level, empno, ename, mgr
2 FROM emp
3 CONNECT BY PRIOR empno = mgr
4 START WITH mgr IS NULL
5 /
LEVEL EMPNO ENAME MGR
---------- ---------- ---------- ----------
1 7839 KING
2 7566 JONES 7839
3 7788 SCOTT 7566
Count/sum RANGES of data values in a column:
A value x will be between values y and z if GREATEST(x, y) = LEAST(x, z). Look a
t this example:
select f2,
sum(decode(greatest(f1,59), least(f1,100), 1, 0)) "Range 60-100",
sum(decode(greatest(f1,30), least(f1, 59), 1, 0)) "Range 30-59",
sum(decode(greatest(f1, 0), least(f1, 29), 1, 0)) "Range 00-29"
from my_table
group by f2;

select dept, sum( decode(sex,'M',1,0)) MALE,


sum( decode(sex,'F',1,0)) FEMALE,
count(decode(sex,'M',1,'F',1)) TOTAL
from my_emp_table
group by dept;
How does one prevent Oracle from using an Index?
Adding an expression to the indexed column
SQL>select count(*) from t where empno+0=1000;
COUNT(*)
----------
1
Execution Plan
--------------------------------------------- ----- --------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=3)
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=1 Bytes=3)

Specifying the FULL hint to force full table scan


SQL>select /*+ FULL(t) */ * from t where empno=1000;
EMPNO ENAME JOB MGR HIREDATE SAL COMM D
EPTNO GRADE
---------- ---------- --------- ---------- --------- ---------- ---------- -----
----- ----------
1000 Victor DBA 7839 20-MAY-03 11000 0
10 JUNIOR
Execution Plan
--------------------------------------------- ----- --------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=41)
1 0 TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=1 Bytes=41)
Specifying NO_INDEX hint
SQL>select /*+ NO_INDEX(T) */ count(*) from t where empno=1000;
COUNT(*)
----------
1
Execution Plan
--------------------------------------------- ----- --------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=3)
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=1 Bytes=3)
How does one select EVERY Nth row from a table?
SELECT *
FROM emp
WHERE (ROWID,0) IN (SELECT ROWID, MOD(ROWNUM,4)
FROM emp);

Get the bottom 10 employees based on their salary


SELECT ename, sal
FROM ( SELECT ename, sal, RANK() OVER (ORDER BY sal ASC) sal_rank
FROM emp )
WHERE sal_rank <= 10;
Select the employees getting the lowest 10 salaries
SELECT ename, sal
FROM ( SELECT ename, sal, DENSE_RANK() OVER (ORDER BY sal) sal_dense_rank
FROM emp )
WHERE sal_dense_rank <= 10;

SELECT *
FROM my_table a
WHERE 10 >= (SELECT COUNT(DISTINCT maxcol)
FROM my_table b
WHERE b.maxcol <= a.maxcol)
ORDER BY maxcol;

Get the top 10 employees based on their salary


SELECT ename, sal
FROM ( SELECT ename, sal, RANK() OVER (ORDER BY sal DESC) sal_rank
FROM emp )
WHERE sal_rank <= 10;
SELECT *
FROM my_table a
WHERE 10 >= (SELECT COUNT(DISTINCT maxcol)
FROM my_table b
WHERE b.maxcol >= a.maxcol)
ORDER BY maxcol DESC;
What is the difference between VARCHAR, VARCHAR2 and CHAR data types?
Both CHAR and VARCHAR2 types are used to store character string values, however,
they behave very differently. The VARCHAR type should not be used:
CHAR
CHAR should be used for storing fixed length character strings. String values wi
ll be space/blank padded before stored on disk. If this type is used to store va
riable length strings, it will waste a lot of disk space
VARCHAR
Currently VARCHAR behaves exactly the same as VARCHAR2. However, this type shoul
d not be used as it is reserved for future usage.
VARCHAR2
VARCHAR2 is used to store variable length character strings. The string value's
length will be stored on disk with the value itself.

Converting rows in columns:


select * from
(select job,
sum( decode(deptno, 10, sal)) dept_no_10_sal,
sum( decode(deptno, 20, sal)) dept_no_20_sal,
sum( decode(deptno, 30, sal)) dept_no_30_sal,
sum( decode(deptno, 40, sal)) dept_no_40_sal
from emp
group by job
)t;

Das könnte Ihnen auch gefallen