SlideShare a Scribd company logo
Page 1 of 77
Features of SQL
 SQL stands for Structured Query Language
 SQL lets you access and manipulate databases
 SQL is an ANSI (American National Standards Institute) standard
 SQL can execute queries against a database
 SQL can retrieve data from a database
 SQL can insert records in a database
 SQL can updaterecords in a database
 SQL can delete records from a database
 SQL can create new databases
 SQL can create new tables in a database
 SQL can create stored procedures in a database
 SQL can create views in a database
 SQL can set permissions on tables, procedures, and views
SQLDML and DDL
SQL can be divided into two parts:The DataManipulation Language (DML) and the Data
Definition Language (DDL).
The query and updatecommands form theDMLpart of SQL:
 SELECT - extracts data from a database
 UPDATE - updates data in a database
 DELETE - deletes data from a database
 INSERT INTO - inserts new data into a database
The DDLpart of SQL permits database tables to be created or deleted. It also defines
indexes (keys), specifies links between tables, and imposes constraints between tables. The
most important DDL statements in SQL are:
 CREATE DATABASE - creates a new database
 ALTER DATABASE - modifies a database
 CREATE TABLE - creates a new table
 ALTER TABLE - modifies a table
 DROP TABLE - deletes a table
 CREATE INDEX - creates an index (search key)
 DROP INDEX - deletes an index
SQLDDL Statements
The CREATE TABLE Statement
The CREATETABLE statement is used to create a table in a database.
SQLCREATE TABLE Syntax
CREATE TABLEtable_name
(
column_name1 data_type,
column_name2 data_type,
column_name3 data_type,
....
)
The data typespecifies what typeof datathe column can hold.
CREATE TABLE Example
Now we want to create a table called "Persons" that contains five columns: P_Id,
LastName, FirstName, Address, and City.
We use thefollowing CREATE TABLEstatement:
CREATE TABLEPersons
(
P_Id int,
LastName varchar(255),
FirstName varchar(255),
Address varchar(255),
City varchar(255)
)
The P_Id column is of typeint and will hold a number. The LastName, FirstName,
Address, and City columns are of typevarchar with a maximum length of 255 characters.
The empty "Persons" tablewill now look like this:
P_Id LastName FirstName Address City
The empty tablecan be filled with data with the INSERT INTO statement.
SQLConstraints
Constraints are used to limit the typeof data that can go into a table.
Constraints can be specified when a table is created (with theCREATE TABLE statement)
or after the table is created (with the ALTER TABLE statement).
We will focus on the following constraints:
 NOT NULL
 UNIQUE
 PRIMARYKEY
 FOREIGN KEY
 CHECK
 DEFAULT
The next chapters will describe each constraint in detail.
SQLNOT NULL Constraint
By default, a table column can hold NULL values.
SQLNOT NULL Constraint
The NOT NULLconstraint enforces a column to NOT accept NULL values.
The NOT NULLconstraint enforces a field to always contain a value. This means that you
cannot insert a new record, or updatea record without adding a value to this field.
The following SQL enforces the"P_Id" column and the "LastName" column to not accept
NULL values:
CREATE TABLEPersons
(
P_Id int NOT NULL,
Page 2 of 77
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255)
)
SQLUNIQUE Constraint
The UNIQUEconstraint uniquely identifies each record in a database table.
The UNIQUEand PRIMARYKEY constraints both providea guarantee for uniqueness
for a column or set of columns.
A PRIMARYKEY constraint automatically has a UNIQUE constraint defined on it.
Notethat you can have many UNIQUEconstraints per table, but only one PRIMARY
KEY constraint per table.
SQLUNIQUE Constraint on CREATE TABLE
The following SQL creates a UNIQUEconstraint on the "P_Id" column when the
"Persons" table is created:
MySQL:
CREATE TABLEPersons
(
P_Id int NOT NULL,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255),
UNIQUE(P_Id)
)
CREATE TABLEPersons
(
P_Id int NOT NULL UNIQUE,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255)
)
To allow naming of a UNIQUEconstraint, and for defining a UNIQUEconstraint on
multiple columns, use thefollowing SQL syntax:
CREATE TABLEPersons
(
P_Id int NOT NULL,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255),
CONSTRAINT uc_PersonID UNIQUE(P_Id,LastName)
)
SQLUNIQUE Constraint on ALTER TABLE
To create a UNIQUEconstraint on the"P_Id" column when the table is already created,
use the following SQL:
MySQL/ SQLServer/ Oracle / MS Access:
ALTER TABLEPersons
ADD UNIQUE(P_Id)
To allow naming of a UNIQUEconstraint, and for defining a UNIQUEconstraint on
multiple columns, use thefollowing SQL syntax:
MySQL/ SQLServer/ Oracle / MS Access:
ALTER TABLEPersons
ADD CONSTRAINT uc_PersonID UNIQUE(P_Id,LastName)
To DROP a UNIQUE Constraint
To drop a UNIQUEconstraint, use the following SQL:
MySQL:
ALTER TABLEPersons
DROP INDEX uc_PersonID
SQLServer/ Oracle / MS Access:
ALTER TABLEPersons
DROP CONSTRAINT uc_PersonID
SQLPRIMARY KEY Constraint
The PRIMARYKEY constraint uniquely identifies each record in a database table.
Primary keys must contain unique values.
A primary key column cannot contain NULL values.
Each table should have a primary key, and each table can have only ONE primary key.
SQLPRIMARY KEY Constraint on CREATE TABLE
The following SQL creates a PRIMARYKEY on the "P_Id" column when the "Persons"
table is created:
CREATE TABLEPersons
(
P_Id int NOT NULL PRIMARYKEY,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255)
)
To allow naming of a PRIMARYKEY constraint, and for defining a PRIMARYKEY
constraint on multiple columns, use the following SQL syntax:
CREATE TABLEPersons
(
P_Id int NOT NULL,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255),
Page 3 of 77
CONSTRAINT pk_PersonID PRIMARYKEY (P_Id,LastName)
)
Note: In the example above there is only ONE PRIMARYKEY (pk_PersonID). However,
the value of thepk_PersonID is made up of two columns (P_Id and LastName).
SQLPRIMARY KEY Constraint on ALTER TABLE
To create a PRIMARYKEY constraint on the "P_Id" column when the table is already
created, use thefollowing SQL:
MySQL/ SQLServer/ Oracle / MS Access:
ALTER TABLEPersons
ADD PRIMARYKEY (P_Id)
To allow naming of a PRIMARYKEY constraint, and for defining a PRIMARYKEY
constraint on multiple columns, use the following SQL syntax:
MySQL/ SQLServer/ Oracle / MS Access:
ALTER TABLEPersons
ADD CONSTRAINT pk_PersonID PRIMARYKEY (P_Id,LastName)
Note: If you use the ALTER TABLE statement to add a primary key, the primary key
column(s) must already have been declared to not contain NULL values (when the table
was first created).
To DROP a PRIMARY KEY Constraint
To drop a PRIMARYKEY constraint, use the following SQL:
MySQL:
ALTER TABLEPersons
DROP PRIMARYKEY
SQLServer/ Oracle / MS Access:
ALTER TABLEPersons
DROP CONSTRAINT pk_PersonID
SQLFOREIGN KEY Constraint
A FOREIGN KEY in one table points to a PRIMARYKEY in another table.
Let's illustrate the foreign key with an example. Look at the following two tables:
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
The "Orders" table:
O_Id OrderNo P_Id
1 77895 3
2 44678 3
3 22456 2
4 24562 1
Notethat the "P_Id" column in the "Orders" table points to the "P_Id" column in the
"Persons" table.
The "P_Id" column in the"Persons" table is thePRIMARYKEY in the "Persons" table.
The "P_Id" column in the"Orders" table is a FOREIGN KEY in the "Orders" table.
The FOREIGN KEY constraint is used to prevent actions that would destroy links between
tables.
The FOREIGN KEY constraint also prevents invalid data from being inserted into the
foreign key column, because it has to be one of thevalues contained in the table it points
to.
SQLFOREIGN KEY Constraint on CREATE TABLE
The following SQL creates a FOREIGN KEY on the "P_Id" column when the"Orders"
table is created:
MySQL:
CREATE TABLEOrders
(
O_Id int NOT NULL,
OrderNo int NOT NULL,
P_Id int,
PRIMARYKEY (O_Id),
FOREIGN KEY (P_Id) REFERENCES Persons(P_Id)
)
SQLServer/ Oracle / MS Access:
CREATE TABLEOrders
(
O_Id int NOT NULL PRIMARYKEY,
OrderNo int NOT NULL,
P_Id int FOREIGN KEY REFERENCES Persons(P_Id)
)
To allow naming of a FOREIGN KEY constraint, and for defining a FOREIGN KEY
constraint on multiple columns, use the following SQL syntax:
MySQL/ SQLServer/ Oracle / MS Access:
CREATE TABLEOrders
(
O_Id int NOT NULL,
OrderNo int NOT NULL,
P_Id int,
PRIMARYKEY (O_Id),
CONSTRAINT fk_PerOrders FOREIGN KEY (P_Id)
REFERENCES Persons(P_Id)
)
SQLFOREIGN KEY Constraint on ALTER TABLE
To create a FOREIGN KEY constraint on the "P_Id" column when the"Orders" table is
already created, use the following SQL:
MySQL/ SQLServer/ Oracle / MS Access:
ALTER TABLEOrders
ADD FOREIGN KEY (P_Id)
REFERENCES Persons(P_Id)
To allow naming of a FOREIGN KEY constraint, and for defining a FOREIGN KEY
constraint on multiple columns, use the following SQL syntax:
Page 4 of 77
MySQL/ SQLServer/ Oracle / MS Access:
ALTER TABLEOrders
ADD CONSTRAINT fk_PerOrders
FOREIGN KEY (P_Id)
REFERENCES Persons(P_Id)
To DROP a FOREIGN KEY Constraint
To drop a FOREIGN KEY constraint, use thefollowing SQL:
MySQL:
ALTER TABLEOrders
DROP FOREIGN KEY fk_PerOrders
SQLServer/ Oracle / MS Access:
ALTER TABLEOrders
DROP CONSTRAINT fk_PerOrders
SQLCHECK Constraint
The CHECK constraint is used to limit thevalue range that can be placed in a column.
If you define a CHECK constraint on a single column it allows only certain values for this
column.
If you define a CHECK constraint on a table it can limit the values in certain columns
based on values in other columns in the row.
SQLCHECK Constraint on CREATE TABLE
The following SQL creates a CHECK constraint on the"P_Id" column when the "Persons"
table is created. The CHECK constraint specifies that thecolumn "P_Id" must only include
integers greater than 0.
MySQL:
CREATE TABLEPersons
(
P_Id int NOT NULL,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255),
CHECK (P_Id>0)
)
CREATE TABLEPersons
(
P_Id int NOT NULL CHECK (P_Id>0),
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255)
)
To allow naming of a CHECK constraint, and for defining a CHECK constraint on
multiple columns, use thefollowing SQL syntax:
CREATE TABLEPersons
(
P_Id int NOT NULL,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255),
CONSTRAINT chk_Person CHECK (P_Id>0 AND City='Sandnes')
)
SQLCHECK Constraint on ALTER TABLE
To create a CHECK constraint on the "P_Id" column when thetable is already created, use
the following SQL:
MySQL/ SQLServer/ Oracle / MS Access:
ALTER TABLEPersons
ADD CHECK (P_Id>0)
To allow naming of a CHECK constraint, and for defining a CHECK constraint on
multiple columns, use thefollowing SQL syntax:
MySQL/ SQLServer/ Oracle / MS Access:
ALTER TABLEPersons
ADD CONSTRAINT chk_Person CHECK (P_Id>0 AND City='Sandnes')
To DROP a CHECK Constraint
To drop a CHECK constraint, use thefollowing SQL:
SQLServer/ Oracle / MS Access:
ALTER TABLEPersons
DROP CONSTRAINT chk_Person
MySQL:
ALTER TABLEPersons
DROP CHECK chk_Person
AUTO INCREMENT a Field
Very often we would like thevalue of the primary key field to be created automatically
every time a new record is inserted.
We would like to create an auto-increment field in a table.
Syntax for Oracle
In Oracle the code is a little bit more tricky.
You will have to create an auto-increment field with the sequence object (this object
generates a number sequence).
Use thefollowing CREATE SEQUENCE syntax:
CREATE SEQUENCE seq_person
MINVALUE1
START WITH 1
INCREMENT BY 1
CACHE 10
Page 5 of 77
The code above creates a sequence object called seq_person, that starts with 1 and will
increment by 1. It will also cache up to 10 values for performance. The cache option
specifies how many sequence values will be stored in memory for faster access.
To insert a new record into the"Persons" table, we will have to use thenextval function
(this function retrieves thenext value from seq_person sequence):
INSERT INTO Persons (P_Id,FirstName,LastName)
VALUES (seq_person.nextval,'Lars','Monsen')
The SQL statement above would insert a new record into the "Persons" table. The"P_Id"
column would be assigned thenext number from the seq_person sequence. The
"FirstName" column would be set to "Lars" and the "LastName" column would be set to
"Monsen".
The ALTER TABLE Statement
The ALTER TABLE statement is used to add, delete, or modify columns in an existing
table.
SQLALTER TABLE Syntax
To add a column in a table, use the following syntax:
ALTER TABLEtable_name
ADD column_name datatype
To delete a column in a table, use the following syntax(notice that some database systems
don't allow deleting a column):
ALTER TABLEtable_name
DROP COLUMN column_name
To change the data typeof a column in a table, use the following syntax:
ALTER TABLEtable_name
MODIFYcolumn_name datatype
SQLALTER TABLE Example
Look at the "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
Now we want to add a column named "DateOfBirth" in the "Persons" table.
We use thefollowing SQL statement:
ALTER TABLEPersons
ADD DateOfBirth date
Notice that the new column, "DateOfBirth", is of typedateand is going to hold a date. The
data typespecifies what typeof data thecolumn can hold. The "Persons" table will now
like this:
P_Id LastName FirstName Address City DateOfBirth
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
Change Data Type Example
Now we want to change the data typeof thecolumn named "DateOfBirth" in the "Persons"
table.
We use thefollowing SQL statement:
ALTER TABLEPersons
ALTER COLUMN DateOfBirth year
Notice that the "DateOfBirth" column is now of typeyear and is going to hold a year in a
two-digit or four-digit format.
DROP COLUMN Example
Next, we want to delete thecolumn named "DateOfBirth" in the"Persons" table.
We use thefollowing SQL statement:
ALTER TABLEPersons
DROP COLUMN DateOfBirth
The "Persons" table will now like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
The SQLSELECT Statement
The SELECT statement is used to select data from a database.
The result is stored in a result table, called the result-set.
SQLSELECT Syntax
SELECT column_name(s)
FROM table_name
and
SELECT * FROM table_name
Note: SQL is not case sensitive. SELECT is thesame as select.
An SQLSELECT Example
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
Now we want to select the content of the columns named "LastName" and "FirstName"
from the table above.
We use thefollowing SELECT statement:
SELECT LastName,FirstName FROM Persons
The result-set will look like this:
LastName FirstName
Hansen Ola
Svendson Tove
Pettersen Kari
Page 6 of 77
SELECT * Example
Now we want to select all the columns from the "Persons" table.
We use thefollowing SELECT statement:
SELECT * FROM Persons
Tip: The asterisk (*) is a quick way of selecting all columns!
The result-set will look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
The SQLSELECT DISTINCT Statement
In a table, some of the columns may contain duplicate values. This is not a problem,
however, sometimes you will want to list only the different (distinct) values in a table.
The DISTINCT keyword can be used to return only distinct (different) values.
SQLSELECT DISTINCT Syntax
SELECT DISTINCT column_name(s)
FROM table_name
SELECT DISTINCT Example
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
Now we want to select only thedistinct values from thecolumn named "City" from the
table above.
We use thefollowing SELECT statement:
SELECT DISTINCT City FROM Persons
The result-set will look like this:
City
Sandnes
Stavanger
The WHERE Clause
The WHERE clause is used to extract only thoserecords that fulfill a specified criterion.
SQLWHERE Syntax
SELECT column_name(s)
FROM table_name
WHERE column_name operator value
WHERE Clause Example
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
Now we want to select only thepersons living in the city "Sandnes" from the table above.
We use thefollowing SELECT statement:
SELECT * FROM Persons
WHERE City='Sandnes'
The result-set will look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
Quotes Around Text Fields
SQL uses single quotes around text values (most database systems will also accept double
quotes).
However, numeric values should not be enclosed in quotes.
For text values:
This is correct:
SELECT * FROM Persons WHERE FirstName='Tove'
This is wrong:
SELECT * FROM Persons WHERE FirstName=Tove
For numeric values:
This is correct:
SELECT * FROM Persons WHERE Year=1965
This is wrong:
SELECT * FROM Persons WHERE Year='1965'
Operators Allowed in the WHERE Clause
With the WHERE clause, the following operators can be used:
Operator Description
= Equal
<> Not equal
> Greater than
< Less than
>= Greater than or equal
<= Less than or equal
BETWEEN Between an inclusive range
LIKE Search for a pattern
IN To specify multiple possiblevalues for a column
Note: In some versions of SQL the <> operator may be written as !=
The AND & OR Operators
The AND operator displays arecord if both the first condition and the second condition
are true.
The OR operator displays a record if either the first condition or thesecond condition is
true.
Page 7 of 77
AND Operator Example
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
Now we want to select only thepersons with the first name equal to "Tove" AND the last
name equal to "Svendson":
We use thefollowing SELECT statement:
SELECT * FROM Persons
WHERE FirstName='Tove'
AND LastName='Svendson'
The result-set will look like this:
P_Id LastName FirstName Address City
2 Svendson Tove Borgvn 23 Sandnes
OR Operator Example
Now we want to select only thepersons with the first name equal to "Tove" OR the first
name equal to "Ola":
We use thefollowing SELECT statement:
SELECT * FROM Persons
WHERE FirstName='Tove'
OR FirstName='Ola'
The result-set will look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
Combining AND & OR
You can also combine AND and OR (use parenthesis to form complex expressions).
Now we want to select only thepersons with the last name equal to "Svendson" AND the
first name equal to "Tove" OR to "Ola":
We use thefollowing SELECT statement:
SELECT * FROM Persons WHERE
LastName='Svendson'
AND (FirstName='Tove' OR FirstName='Ola')
The result-set will look like this:
P_Id LastName FirstName Address City
2 Svendson Tove Borgvn 23 Sandnes
SQLORDER BY Keyword
The ORDER BY keyword is used to sort the result-set.
The ORDER BY Keyword
The ORDER BY keyword is used to sort the result-set by a specified column.
The ORDER BY keyword sorts therecords in ascending order by default.
If you want to sort therecords in a descending order, you can use the DESC keyword.
SQLORDER BY Syntax
SELECT column_name(s)
FROM table_name
ORDER BY column_name(s) ASC|DESC
ORDER BY Example
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
4 Nilsen Tom Vingvn 23 Stavanger
Now we want to select all the persons from the table above, however, we want to sort the
persons by their last name.
We use thefollowing SELECT statement:
SELECT * FROM Persons
ORDER BY LastName
The result-set will look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
4 Nilsen Tom Vingvn 23 Stavanger
3 Pettersen Kari Storgt 20 Stavanger
2 Svendson Tove Borgvn 23 Sandnes
ORDER BY DESC Example
Now we want to select all the persons from the table above, however, we want to sort the
persons descending by their last name.
We use thefollowing SELECT statement:
SELECT * FROM Persons
ORDER BY LastName DESC
The result-set will look like this:
P_Id LastName FirstName Address City
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
4 Nilsen Tom Vingvn 23 Stavanger
1 Hansen Ola Timoteivn 10 Sandnes
The INSERT INTO Statement
The INSERT INTO statement is used to insert a new row in a table.
SQLINSERT INTO Syntax
It is possible to write theINSERT INTO statement in two forms.
The first form doesn't specify thecolumn names where the data will be inserted, only their
values:
INSERT INTO table_name
VALUES (value1, value2, value3,...)
Page 8 of 77
The second form specifies both thecolumn names and the values to be inserted:
INSERT INTO table_name (column1, column2, column3,...)
VALUES (value1, value2, value3,...)
SQLINSERT INTO Example
We have the following "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
Now we want to insert a new row in the "Persons" table.
We use thefollowing SQL statement:
INSERT INTO Persons
VALUES (4,'Nilsen', 'Johan', 'Bakken 2', 'Stavanger')
The "Persons" table will now look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
4 Nilsen Johan Bakken 2 Stavanger
Insert Data Only in SpecifiedColumns
It is also possibleto only add data in specific columns.
The following SQL statement will add a new row, but only add data in the "P_Id",
"LastName" and the "FirstName" columns:
INSERT INTO Persons (P_Id, LastName, FirstName)
VALUES (5, 'Tjessem', 'Jakob')
The "Persons" table will now look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
4 Nilsen Johan Bakken 2 Stavanger
5 Tjessem Jakob
The UPDATE Statement
The UPDATEstatement is used to updateexisting records in a table.
SQLUPDATE Syntax
UPDATEtable_name
SET column1=value, column2=value2,...
WHERE some_column=some_value
Note: Notice the WHERE clause in theUPDATEsyntax. The WHERE clause specifies
which record or records that should be updated. If you omit theWHERE clause, all
records will be updated!
SQLUPDATE Example
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
4 Nilsen Johan Bakken 2 Stavanger
5 Tjessem Jakob
Now we want to updatethe person "Tjessem, Jakob" in the "Persons" table.
We use thefollowing SQL statement:
UPDATEPersons
SET Address='Nissestien 67', City='Sandnes'
WHERE LastName='Tjessem' AND FirstName='Jakob'
The "Persons" table will now look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
4 Nilsen Johan Bakken 2 Stavanger
5 Tjessem Jakob Nissestien 67 Sandnes
SQLUPDATE Warning
Be careful when updating records. If we had omitted the WHERE clause in theexample
above, like this:
UPDATEPersons
SET Address='Nissestien 67', City='Sandnes'
The "Persons" table would have looked like this:
P_Id LastName FirstName Address City
1 Hansen Ola Nissestien 67 Sandnes
2 Svendson Tove Nissestien 67 Sandnes
3 Pettersen Kari Nissestien 67 Sandnes
4 Nilsen Johan Nissestien 67 Sandnes
5 Tjessem Jakob Nissestien 67 Sandnes
The DELETE Statement
The DELETE statement is used to delete rows in a table.
SQLDELETE Syntax
DELETE FROM table_name
WHERE some_column=some_value
Note: Notice the WHERE clause in theDELETE syntax. The WHERE clause specifies
which record or records that should be deleted. If you omit the WHERE clause, all records
will be deleted!
SQLDELETE Example
The "Persons" table:
Page 9 of 77
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
4 Nilsen Johan Bakken 2 Stavanger
5 Tjessem Jakob Nissestien 67 Sandnes
Now we want to delete theperson "Tjessem, Jakob" in the"Persons" table.
We use thefollowing SQL statement:
DELETE FROM Persons
WHERE LastName='Tjessem' AND FirstName='Jakob'
The "Persons" table will now look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
4 Nilsen Johan Bakken 2 Stavanger
Delete All Rows
It is possible to delete all rows in a table without deleting the table. This means that the
table structure, attributes, and indexes will be intact:
DELETE FROM table_name
or
DELETE * FROM table_name
Note: Be very careful when deleting records. You cannot undo this statement!
The TOP Clause
The TOP clause is used to specify thenumber of records to return.
The TOP clause can be very useful on large tables with thousands of records. Returning a
large number of records can impact on performance.
Note: Not all database systems supporttheTOP clause.
Oracle Syntax
SELECT column_name(s)
FROM table_name
WHERE ROWNUM <= number
Example
SELECT *
FROM Persons
WHERE ROWNUM <=5
SQLTOP Example
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
4 Nilsen Tom Vingvn 23 Stavanger
Now we want to select only thetwo first records in the table above.
We use thefollowing SELECT statement:
SELECT TOP 2 * FROM Persons
The result-set will look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
SQLTOP PERCENT Example
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
4 Nilsen Tom Vingvn 23 Stavanger
Now we want to select only 50% of therecords in the table above.
We use thefollowing SELECT statement:
SELECT TOP 50 PERCENT * FROM Persons
The result-set will look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
The LIKE Operator
The LIKE operator is used to search for a specified pattern in a column.
SQLLIKE Syntax
SELECT column_name(s)
FROM table_name
WHERE column_name LIKE pattern
LIKE Operator Example
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
Now we want to select the persons living in a city that starts with "s" from the table above.
We use thefollowing SELECT statement:
SELECT * FROM Persons
WHERE City LIKE 's%'
The "%" sign can be used to define wildcards (missing letters in thepattern) both before
and after the pattern.
The result-set will look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
Page 10 of 77
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
Next, we want to select the persons living in a city that ends with an "s" from the
"Persons" table.
We use thefollowing SELECT statement:
SELECT * FROM Persons
WHERE City LIKE '%s'
The result-set will look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
Next, we want to select the persons living in a city that contains the pattern "tav" from the
"Persons" table.
We use thefollowing SELECT statement:
SELECT * FROM Persons
WHERE City LIKE '%tav%'
The result-set will look like this:
P_Id LastName FirstName Address City
3 Pettersen Kari Storgt 20 Stavanger
It is also possibleto select thepersons living in a city that does NOT contain thepattern
"tav" from the"Persons" table, by using the NOT keyword.
We use thefollowing SELECT statement:
SELECT * FROM Persons
WHERE City NOT LIKE'%tav%'
The result-set will look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
SQLWildcards
SQL wildcards can be used when searching for data in a database.
SQLWildcards
SQL wildcards can substitutefor one or more characters when searching for data in a
database.
SQL wildcards must be used with the SQL LIKE operator.
With SQL, thefollowing wildcards can be used:
Wildcard Description
% A substitutefor zero or more characters
_ A substitutefor exactly one character
[charlist] Any single character in charlist
[^charlist]
or
[!charlist]
Any single character not in charlist
SQLWildcard Examples
We have the following "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
Using the % Wildcard
Now we want to select the persons living in a city that starts with "sa" from the "Persons"
table.
We use thefollowing SELECT statement:
SELECT * FROM Persons
WHERE City LIKE 'sa%'
The result-set will look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
Next, we want to select the persons living in a city that contains the pattern "nes" from the
"Persons" table.
We use thefollowing SELECT statement:
SELECT * FROM Persons
WHERE City LIKE '%nes%'
The result-set will look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
Using the _ Wildcard
Now we want to select the persons with a first name that starts with any character,
followed by "la" from the "Persons" table.
We use thefollowing SELECT statement:
SELECT * FROM Persons
WHERE FirstNameLIKE '_la'
The result-set will look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
Next, we want to select the persons with a last name that starts with "S", followed by any
character, followed by "end", followed by any character, followed by "on" from the
"Persons" table.
We use thefollowing SELECT statement:
SELECT * FROM Persons
WHERE LastName LIKE 'S_end_on'
The result-set will look like this:
P_Id LastName FirstName Address City
2 Svendson Tove Borgvn 23 Sandnes
Page 11 of 77
Using the [charlist] Wildcard
Now we want to select the persons with a last name that starts with "b" or "s" or "p" from
the "Persons" table.
We use thefollowing SELECT statement:
SELECT * FROM Persons
WHERE LastName LIKE '[bsp]%'
The result-set will look like this:
P_Id LastName FirstName Address City
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
Next, we want to select the persons with a last name that do not start with "b" or "s" or "p"
from the "Persons" table.
We use thefollowing SELECT statement:
SELECT * FROM Persons
WHERE LastName LIKE '[!bsp]%'
The result-set will look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
The IN Operator
The IN operator allows you to specify multiple values in a WHERE clause.
SQLIN Syntax
SELECT column_name(s) FROM table_name
WHERE column_name IN (value1,value2,...)
IN Operator Example
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
Now we want to select the persons with a last name equal to "Hansen" or "Pettersen" from
the table above.
We use thefollowing SELECT statement:
SELECT * FROM Persons
WHERE LastName IN ('Hansen','Pettersen')
The result-set will look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
The BETWEEN Operator
The BETWEEN operator selects a range of data between two values. Thevalues can be
numbers, text, or dates.
SQLBETWEEN Syntax
SELECT column_name(s)
FROM table_name
WHERE column_name
BETWEEN value1 AND value2
BETWEEN Operator Example
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
Now we want to select the persons with a last name alphabetically between "Hansen" and
"Pettersen" from the table above.
We use thefollowing SELECT statement:
SELECT * FROM Persons
WHERE LastName
BETWEEN 'Hansen' AND 'Pettersen'
The result-set will look like this:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
Note: The BETWEEN operator is treated differently in different databases!
In some databases, persons with the LastName of "Hansen" or "Pettersen" will not be
listed, because the BETWEEN operator only selects fields that are between and excluding
the test values.
In other databases, persons with theLastName of "Hansen" or "Pettersen" will be listed,
because the BETWEEN operator selects fields that are between and including the test
values.
And in other databases, persons with theLastName of "Hansen" will be listed, but
"Pettersen" will not be listed (like the example above), because the BETWEEN operator
selects fields between thetest values, including the first test value and excluding the last
test value.
Therefore: Check how your database treats the BETWEEN operator.
Example 2
To display the persons outsidethe range in theprevious example, use NOT BETWEEN:
SELECT * FROM Persons
WHERE LastName
NOT BETWEEN 'Hansen' AND 'Pettersen'
The result-set will look like this:
P_Id LastName FirstName Address City
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
SQLAlias
You can give a table or a column another name by using an alias. This can be a good thing
to do if you have very long or complex table names or column names.
Page 12 of 77
An alias name could be anything, but usually it is short.
SQLAlias Syntax for Tables
SELECT column_name(s)
FROM table_name
AS alias_name
SQLAlias Syntax for Columns
SELECT column_name AS alias_name
FROM table_name
Alias Example
Assume we have a table called "Persons" and another table called "Product_Orders". We
will give the table aliases of "p" and "po" respectively.
Now we want to list all theorders that "Ola Hansen" is responsiblefor.
We use thefollowing SELECT statement:
SELECT po.OrderID, p.LastName, p.FirstName
FROM Persons AS p,
Product_Orders AS po
WHERE p.LastName='Hansen' AND p.FirstName='Ola'
The same SELECT statement without aliases:
SELECT Product_Orders.OrderID, Persons.LastName, Persons.FirstName
FROM Persons,
Product_Orders
WHERE Persons.LastName='Hansen' AND Persons.FirstName='Ola'
As you'll see from the two SELECT statements above; aliases can make queries easier
both to writeand to read.
SQLJOIN
The JOIN keyword is used in an SQL statement to query data from two or more tables,
based on a relationship between certain columns in these tables.
SQLINNER JOIN Syntax
SELECT column_name(s)
FROM table_name1
INNER JOIN table_name2
ON table_name1.column_name=table_name2.column_name
PS: INNER JOIN is thesame as JOIN.
SQLINNER JOIN Example
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
The "Orders" table:
O_Id OrderNo P_Id
1 77895 3
2 44678 3
3 22456 1
4 24562 1
5 34764 15
Now we want to list all thepersons with any orders.
We use thefollowing SELECT statement:
SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
INNER JOIN Orders
ON Persons.P_Id=Orders.P_Id
ORDER BY Persons.LastName
The result-set will look like this:
LastName FirstName OrderNo
Hansen Ola 22456
Hansen Ola 24562
Pettersen Kari 77895
Pettersen Kari 44678
The INNER JOIN keyword returns rows when there is at least one match in both tables. If
there are rows in "Persons" that do not have matches in "Orders", thoserows will NOT be
listed.
SQLLEFT JOIN Keyword
The LEFT JOIN keyword returns all rows from the left table (table_name1), even if there
are no matches in the right table (table_name2).
SQLLEFT JOIN Syntax
SELECT column_name(s)
FROM table_name1
LEFT JOIN table_name2
ON table_name1.column_name=table_name2.column_name
PS: In some databases LEFT JOIN is called LEFT OUTER JOIN.
SQLLEFT JOIN Example
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
The "Orders" table:
O_Id OrderNo P_Id
1 77895 3
2 44678 3
3 22456 1
4 24562 1
5 34764 15
Now we want to list all thepersons and their orders - if any, from the tables above.
Page 13 of 77
We use thefollowing SELECT statement:
SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
LEFT JOIN Orders
ON Persons.P_Id=Orders.P_Id
ORDER BY Persons.LastName
The result-set will look like this:
LastName FirstName OrderNo
Hansen Ola 22456
Hansen Ola 24562
Pettersen Kari 77895
Pettersen Kari 44678
Svendson Tove
The LEFT JOIN keyword returns all therows from the left table (Persons), even if there
are no matches in the right table (Orders).
SQLRIGHT JOIN Keyword
The RIGHT JOIN keyword returns all therows from the right table (table_name2), even if
there are no matches in the left table (table_name1).
SQLRIGHT JOIN Syntax
SELECT column_name(s)
FROM table_name1
RIGHT JOIN table_name2
ON table_name1.column_name=table_name2.column_name
PS: In some databases RIGHT JOIN is called RIGHT OUTER JOIN.
SQLRIGHT JOIN Example
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
The "Orders" table:
O_Id OrderNo P_Id
1 77895 3
2 44678 3
3 22456 1
4 24562 1
5 34764 15
Now we want to list all theorders with containing persons - if any, from the tables above.
We use thefollowing SELECT statement:
SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
RIGHT JOIN Orders
ON Persons.P_Id=Orders.P_Id
ORDER BY Persons.LastName
The result-set will look like this:
LastName FirstName OrderNo
Hansen Ola 22456
Hansen Ola 24562
Pettersen Kari 77895
Pettersen Kari 44678
34764
The RIGHT JOIN keyword returns all therows from the right table (Orders), even if there
are no matches in the left table (Persons).
SQLFULL JOIN Keyword
The FULL JOIN keyword return rows when there is a match in one of the tables.
SQLFULL JOIN Syntax
SELECT column_name(s)
FROM table_name1
FULL JOIN table_name2
ON table_name1.column_name=table_name2.column_name
SQLFULL JOIN Example
The "Persons" table:
P_Id LastName FirstName Address City
1 Hansen Ola Timoteivn 10 Sandnes
2 Svendson Tove Borgvn 23 Sandnes
3 Pettersen Kari Storgt 20 Stavanger
The "Orders" table:
O_Id OrderNo P_Id
1 77895 3
2 44678 3
3 22456 1
4 24562 1
5 34764 15
Now we want to list all thepersons and their orders, and all theorders with their persons.
We use thefollowing SELECT statement:
SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
FULL JOIN Orders
ON Persons.P_Id=Orders.P_Id
ORDER BY Persons.LastName
The result-set will look like this:
LastName FirstName OrderNo
Hansen Ola 22456
Hansen Ola 24562
Pettersen Kari 77895
Pettersen Kari 44678
Svendson Tove
34764
Page 14 of 77
The FULL JOIN keyword returns all the rows from the left table (Persons), and all the
rows from theright table (Orders). If there are rows in "Persons" that do not have matches
in "Orders", or if there are rows in "Orders" that do not have matches in "Persons", those
rows will be listed as well.
The SQLUNION Operator
The UNION operator is used to combine the result-set of two or more SELECT
statements.
Notice that each SELECT statement within theUNION must have the same number of
columns. Thecolumns must also have similar data types. Also, thecolumns in each
SELECT statement must be in the same order.
SQLUNION Syntax
SELECT column_name(s) FROM table_name1
UNION
SELECT column_name(s) FROM table_name2
Note: The UNION operator selects only distinct values by default. To allow duplicate
values, use UNION ALL.
SQLUNION ALL Syntax
SELECT column_name(s) FROM table_name1
UNION ALL
SELECT column_name(s) FROM table_name2
PS: The column names in theresult-set of a UNION are always equal to thecolumn names
in the first SELECT statement in theUNION.
SQLUNION Example
Look at the following tables:
"Employees_Norway":
E_ID E_Name
01 Hansen, Ola
02 Svendson, Tove
03 Svendson, Stephen
04 Pettersen, Kari
"Employees_USA":
E_ID E_Name
01 Turner, Sally
02 Kent, Clark
03 Svendson, Stephen
04 Scott, Stephen
Now we want to list all the different employees in Norway and USA.
We use thefollowing SELECT statement:
SELECT E_Name FROM Employees_Norway
UNION
SELECT E_Name FROM Employees_USA
The result-set will look like this:
E_Name
Hansen, Ola
Svendson, Tove
Svendson, Stephen
Pettersen, Kari
Turner, Sally
Kent, Clark
Scott, Stephen
Note: This command cannot be used to list all employees in Norway and USA. In the
example above we have two employees with equal names, and only one of them will be
listed. The UNION command selects only distinct values.
SQLUNION ALL Example
Now we want to list all employees in Norway and USA:
SELECT E_Name FROM Employees_Norway
UNION ALL
SELECT E_Name FROM Employees_USA
Result
E_Name
Hansen, Ola
Svendson, Tove
Svendson, Stephen
Pettersen, Kari
Turner, Sally
Kent, Clark
Svendson, Stephen
Scott, Stephen
SQLINTERSECT Operator
The SQL INTERSECT query allows you to return theresults of 2 or more "select" queries.
However, it only returns the rows selected by all queries. If a record exists in one query
and not in the other, it will be omitted from the INTERSECT results.
Each SQL statement within the SQL INTERSECT query must have thesame number of
fields in the result sets with similar data types.
The syntaxfor the SQL INTERSECT query is:
select field1, field2, . field_n
from tables
INTERSECT
select field1, field2, . field_n
from tables;
SQLINTERSECT Query - Singlefieldexample
The following is an example of an SQL INTERSECT query that has one field with the
same data type:
select supplier_id
from suppliers
INTERSECT
select supplier_id
from orders;
Page 15 of 77
In this SQL INTERSECT query example, if a supplier_id appeared in both the suppliers
and orders table, it would appear in your result set.
SQLINTERSECT Query - Using ORDER BY Clause example
The following is an SQL INTERSECT query that uses an SQL ORDER BY clause:
select supplier_id, supplier_name
from suppliers
where supplier_id > 2000
INTERSECT
select company_id, company_name
from companies
where company_id > 1000
ORDER BY 2;
Since thecolumn names are different between the two "select" statements, it is more
advantageous to reference the columns in the SQL ORDER BY clause by their position in
the result set. In this example, we've sorted theresults by supplier_name/ company_name
in ascending order, as denoted by the"ORDER BY 2".
The supplier_name/ company_name fields are in position #2 in the result set.
SQLMINUS Operator
The SQL MINUSquery returns all rows in the first SQL SELECT statement that are not
returned in the second SQL SELECT statement.
Each SQL SELECT statement within the SQL MINUSquery must have thesame number
of fields in the result sets with similar data types.
The syntaxfor the SQL MINUSquery is:
select field1, field2, ... field_n
from tables
MINUS
select field1, field2, ... field_n
from tables;
SQLMINUS Query - Singlefieldexample
The following is an example of an SQL MINUSquery that has one field with the same
data type:
select supplier_id
from suppliers
MINUS
select supplier_id
from orders;
This SQL Minus query example returns all supplier_id values that are in thesuppliers table
and not in the orders table. What this means is that if a supplier_id value existed in the
suppliers table and also existed in theorders table, thesupplier_id value would not appear
in this result set.
SQLMINUS Query - Using ORDER BY Clause example
The following is an SQL MINUSquery that uses an ORDER BY clause:
select supplier_id, supplier_name
from suppliers
where supplier_id > 2000
MINUS
select company_id, company_name
from companies
where company_id > 1000
ORDER BY 2;
In this SQL MINUSquery example, since the column names are different between thetwo
"select" statements, it is more advantageous to reference the columns in the SQL ORDER
BY clause by their position in theresult set. In this example, we've sorted the results by
supplier_name / company_name in ascending order, as denoted by the"ORDER BY 2".
The supplier_name/ company_name fields are in position #2 in the result set.
The SQLSELECT INTO Statement
The SELECT INTO statement selects data from one table and inserts it into a different
table.
The SELECT INTO statement is most often used to create backup copies of tables.
SQLSELECT INTO Syntax
We can select all columns into the new table:
SELECT *
INTO new_table_name [IN externaldatabase]
FROM old_tablename
Or we can select only the columns we want into the new table:
SELECT column_name(s)
INTO new_table_name [IN externaldatabase]
FROM old_tablename
SQLSELECT INTO Example
Make a Backup Copy - Now we want to make an exact copy of the data in our "Persons"
table.
We use thefollowing SQL statement:
SELECT *
INTO Persons_Backup
FROM Persons
We can also use theIN clause to copy thetable into another database:
SELECT *
INTO Persons_Backup IN 'Backup.mdb'
FROM Persons
We can also copy only a few fields into the new table:
SELECT LastName,FirstName
INTO Persons_Backup
FROM Persons
SQLSELECT INTO - With a WHERE Clause
We can also add a WHERE clause.
The following SQL statement creates a "Persons_Backup" table with only thepersons who
lives in the city "Sandnes":
SELECT LastName,Firstname
INTO Persons_Backup
Page 16 of 77
FROM Persons
WHERE City='Sandnes'
SQLSELECT INTO - JoinedTables
Selecting data from more than one table is also possible.
The following example creates a "Persons_Order_Backup" table contains data from the
two tables "Persons" and "Orders":
SELECT Persons.LastName,Orders.OrderNo
INTO Persons_Order_Backup
FROM Persons
INNER JOIN Orders
ON Persons.P_Id=Orders.P_Id
The GROUP BY Statement
The GROUP BY statement is used in conjunction with the aggregate functions to group
the result-set by one or more columns.
SQLGROUP BY Syntax
SELECT column_name, aggregate_function(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name
SQLGROUP BY Example
We have the following "Orders" table:
O_Id OrderDate OrderPrice Customer
1 2008/11/12 1000 Hansen
2 2008/10/23 1600 Nilsen
3 2008/09/02 700 Hansen
4 2008/09/03 300 Hansen
5 2008/08/30 2000 Jensen
6 2008/10/04 100 Nilsen
Now we want to find the totalsum (total order) of each customer.
We will have to use the GROUP BY statement to group thecustomers.
We use thefollowing SQL statement:
SELECT Customer,SUM(OrderPrice) FROM Orders
GROUP BY Customer
The result-set will look like this:
Customer SUM(OrderPrice)
Hansen 2000
Nilsen 1700
Jensen 2000
Nice! Isn't it? :)
Let's see what happens if we omit the GROUP BY statement:
SELECT Customer,SUM(OrderPrice) FROM Orders
The result-set will look like this:
Customer SUM(OrderPrice)
Hansen 5700
Nilsen 5700
Hansen 5700
Hansen 5700
Jensen 5700
Nilsen 5700
The result-set above is not what we wanted.
Explanation of why the above SELECT statement cannot be used: TheSELECT
statement above has two columns specified (Customer and SUM(OrderPrice). The
"SUM(OrderPrice)" returns a single value (that is the totalsum of the "OrderPrice"
column), while "Customer" returns 6 values (one value for each row in the"Orders" table).
This will therefore not give us the correct result. However, you have seen that theGROUP
BY statement solves this problem.
GROUP BY More Than One Column
We can also use theGROUP BY statement on more than one column, like this:
SELECT Customer,OrderDate,SUM(OrderPrice) FROM Orders
GROUP BY Customer,OrderDate
The HAVING Clause
The HAVING clause was added to SQL because the WHERE keyword could not be used
with aggregate functions.
SQLHAVING Syntax
SELECT column_name, aggregate_function(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name
HAVING aggregate_function(column_name) operator value
SQLHAVING Example
We have the following "Orders" table:
O_Id OrderDate OrderPrice Customer
1 2008/11/12 1000 Hansen
2 2008/10/23 1600 Nilsen
3 2008/09/02 700 Hansen
4 2008/09/03 300 Hansen
5 2008/08/30 2000 Jensen
6 2008/10/04 100 Nilsen
Now we want to find if any of the customers have a total order of less than 2000.
We use thefollowing SQL statement:
SELECT Customer,SUM(OrderPrice) FROM Orders
GROUP BY Customer
HAVING SUM(OrderPrice)<2000
The result-set will look like this:
Customer SUM(OrderPrice)
Nilsen 1700
Page 17 of 77
Now we want to find if thecustomers "Hansen" or "Jensen" have a totalorder of more
than 1500.
We add an ordinary WHERE clause to the SQL statement:
SELECT Customer,SUM(OrderPrice) FROM Orders
WHERE Customer='Hansen' OR Customer='Jensen'
GROUP BY Customer
HAVING SUM(OrderPrice)>1500
The result-set will look like this:
Customer SUM(OrderPrice)
Hansen 2000
Jensen 2000
SQLViews
A view is a virtual table.
SQLCREATE VIEW Statement
In SQL, a view is a virtual table based on theresult-set of an SQL statement.
A view contains rows and columns, just like a real table. The fields in a view are fields
from one or more real tables in thedatabase.
You can add SQL functions, WHERE, and JOIN statements to a view and present thedata
as if the data were coming from one single table.
SQLCREATE VIEW Syntax
CREATE VIEW view_name AS
SELECT column_name(s)
FROM table_name
WHERE condition
Note: A view always shows up-to-datedata! Thedatabase engine recreates the data, using
the view's SQL statement, every time a user queries a view.
SQLCREATE VIEW Examples
If you have theNorthwind database you can see that it has several views installed by
default.
The view "Current Product List" lists all active products (products that arenot
discontinued) from the"Products" table. Theview is created with thefollowing SQL:
CREATE VIEW [Current Product List] AS
SELECT ProductID,ProductName
FROM Products
WHERE Discontinued=No
We can query theview above as follows:
SELECT * FROM [Current Product List]
Another view in the Northwind sample database selects every product in the"Products"
table with a unit price higher than the average unit price:
CREATE VIEW [Products Above Average Price] AS
SELECT ProductName,UnitPrice
FROM Products
WHERE UnitPrice>(SELECT AVG(UnitPrice) FROM Products)
We can query theview above as follows:
SELECT * FROM [Products AboveAverage Price]
Another view in the Northwind database calculates the totalsale for each category in 1997.
Notethat this view selects its data from another view called "Product Sales for 1997":
CREATE VIEW [Category Sales For 1997] AS
SELECT DISTINCT CategoryName,Sum(ProductSales) AS CategorySales
FROM [Product Sales for 1997]
GROUP BY CategoryName
We can query theview above as follows:
SELECT * FROM [Category Sales For 1997]
We can also add a condition to the query. Now we want to see the totalsale only for the
category "Beverages":
SELECT * FROM [Category Sales For 1997]
WHERE CategoryName='Beverages'
SQLUpdating a View
You can updatea view by using the following syntax:
SQLCREATE OR REPLACE VIEW Syntax
CREATE OR REPLACE VIEW view_name AS
SELECT column_name(s)
FROM table_name
WHERE condition
Now we want to add the "Category" column to the "Current Product List" view. We will
updatethe view with the following SQL:
CREATE VIEW [Current Product List] AS
SELECT ProductID,ProductName,Category
FROM Products
WHERE Discontinued=No
SQLDropping a View
You can delete a view with the DROP VIEW command.
SQLDROP VIEW Syntax
DROP VIEW view_name
SQLCREATE INDEX Statement
The CREATEINDEX statement is used to create indexes in tables.
Indexes allow the database application to find data fast; without reading the whole table.
Indexes
An index can be created in a table to find data more quickly and efficiently.
The users cannot see theindexes, they are just used to speed up searches/queries.
Note: Updating a table with indexes takes more time than updating a table without
(because the indexes also need an update). So you should only create indexes on columns
(and tables) that will be frequently searched against.
SQLCREATE INDEX Syntax
Creates an index on a table. Duplicatevalues are allowed:
CREATE INDEX index_name
ON table_name (column_name)
Page 18 of 77
SQLCREATE UNIQUE INDEX Syntax
Creates a unique index on a table. Duplicate values are not allowed:
CREATE UNIQUEINDEX index_name
ON table_name (column_name)
Note: The syntaxfor creating indexes varies amongst different databases. Therefore:
Check thesyntax for creating indexes in your database.
CREATE INDEX Example
The SQL statement below creates an index named "PIndex" on the "LastName" column in
the "Persons" table:
CREATE INDEX PIndex
ON Persons (LastName)
If you want to create an index on a combination of columns, you can list thecolumn names
within theparentheses, separated by commas:
CREATE INDEX PIndex
ON Persons (LastName, FirstName)
Grant and Revoke privileges in Oracle
Data ControlLanguage Statements are used to grant privileges on tables, views,
sequences, synonyms, procedures to other users or roles.
The DCL statements are
GRANT :Use to grant privileges to other users or roles.
REVOKE :Use to take back privileges granted to other users and roles.
Privileges are of two types :
 SYSTEM PRIVILEGES
 OBJECT PRIVILEGES
SystemPrivileges are normally granted by a DBA to users. Examples of systemprivileges
are CREATESESSION, CREATE TABLE, CREATE USER etc.
Object privileges means privileges on objects such as tables, views, synonyms, procedure.
These are granted by owner of the object.
Object Privileges are
ALTER Change the table definition with the ALTER TABLE statement.
DELETE Remove rows from thetable with the DELETE statement.
Note: You must grant the SELECT privilege on the table along with
the DELETE privilege.
INDEX Create an index on the table with the CREATE INDEX statement.
INSERT Add new rows to the table with the INSERT statement.
REFERENCES Create a constraint that refers to thetable. You cannot grant this
privilege to a role.
SELECT Query the table with the SELECT statement.
UPDATE Change data in the table with the UPDATEstatement.
Note: You must grant the SELECT privilege on the table along with
the UPDATEprivilege.
Grant
Grant is use to grant privileges on tables, view, procedure to other users or roles
Examples:
Supposeyou own emp table. Now you want to grant select,update,insert privilege on this
table to other user “SAMI”.
grant select, update, insert on emp to sami;
Supposeyou want to grant all privileges on emp table to sami. Then
grant all on emp to sami;
Supposeyou want to grant select privilege on emp to all other users of the database. Then
grant select on emp to public;
Supposeyou want to grant updateand insert privilege on only certain columns not on all
the columns then include the column names in grant statement. For example you want to
grant updateprivilege on ename column only and insert privilege on empno and ename
columns only. Then give the following statement
grant update (ename),insert (empno, ename) on empto sami;
To grant select statement on emp table to sami and to make sami be able further pass on
this privilege you have to give WITH GRANT OPTION clausein GRANT statement like
this.
grant select on emp to sami with grant option;
REVOKE
Use to revoke privileges already granted to other users.
For example to revoke select, update, insert privilege you have granted to Sami then give
the following statement.
revoke select, update, insert on emp from sami;
To revoke select statement on emp granted to public give the following command.
revoke select on empfrom public;
To revoke updateprivilege on ename column and insert privilege on empno and ename
columns give the following revoke statement.
revoke update, insert on emp from sami;
Note:You cannot take back column level privileges. Supposeyou just want to take
back insert privilege on ename column then you have to first take back the whole insert
privilege and then grant privilege on empno column.
ROLES
A role is a group of Privileges. A role is very handy in managing privileges, Particularly
in such situation when number of users should have thesame set of privileges.
For example you have four users :Sami, Scott, Ashi, Tanyain the database. To theseusers
you want to grant select ,updateprivilege on emp table, select,delete privilege on dept
table. To do this first create a role by giving thefollowing statement
create role clerks
Page 19 of 77
Then grant privileges to this role.
grant select,update on emp to clerks;
grant select,deleteon dept to clerks;
Now grant this clerks role to users like this
grant clerks to sami, scott, ashi, tanya ;
Now Sami, Scott, Ashiand Tanyahave all the privileges granted on clerks role.
Supposeafter one month you want grant delete on privilege on emp table all these users
then just grant this privilege to clerks role and automatically all the users will have the
privilege.
grant delete on emp to clerks;
If you want to take back updateprivilege on emp table from these users just take it back
from clerks role.
revoke update on emp from clerks;
To Drop a role
Drop role clerks;
LISTING INFORMATION ABOUT PRIVILEGES
To see which table privileges are granted by you to other users.
SELECT * FROM USER_TAB_PRIVS_MADE
To see which table privileges are granted to you by other users
SELECT * FROM USER_TAB_PRIVS_RECD;
To see which column level privileges are granted by you to other users.
SELECT * FROM USER_COL_PRIVS_MADE
To see which column level privileges are granted to you by other users
SELECT * FROM USER_COL_PRIVS_RECD;
To see which privileges are granted to roles
SELECT * FROM USER_ROLE_PRIVS;
Grant Privileges on Tables
You can grant users various privileges to tables. Theseprivileges can be any combination
of select, insert, update, delete, references, alter, and index. Below is an explanation of
what each privilege means.
Privilege Description
Select Ability to query thetable with a select statement.
Insert Ability to add new rows to the table with theinsert statement.
Update Ability to updaterows in the table with the updatestatement.
Delete Ability to delete rows from thetable with the delete statement.
References Ability to create a constraint that refers to the table.
Alter Ability to change the table definition with the alter table statement.
Index Ability to create an index on the table with the create index statement.
The syntaxfor granting privileges on a table is:
grant privileges on object to user;
For example, if you wanted to grant select, insert, update, and delete privileges on a table
called suppliers to a user name smithj, you would execute the following statement:
grant select, insert, update, delete on suppliers to smithj;
You can also use theall keyword to indicate that you wish all permissions to be granted.
For example:
grant all on suppliers to smithj;
If you wanted to grant select access on your table to all users, you could grant the
privileges to thepublic keyword. For example:
grant select on suppliers to public;
Revoke Privileges on Tables
Once you have granted privileges, you may need to revoke some or all of these privileges.
To do this, you can execute a revoke command. You can revoke any combination of select,
insert, update, delete, references, alter, and index.
The syntaxfor revoking privileges on a table is:
revoke privileges on object from user;
For example, if you wanted to revoke delete privileges on a table called suppliers froma
user named anderson, you would execute the following statement:
revoke delete on suppliers from anderson;
If you wanted to revoke all privileges on a table, you could use the all keyword. For
example:
revoke all on suppliers from anderson;
If you had granted privileges to public (all users) and you wanted to revoke these
privileges, you could execute thefollowing statement:
revoke all on suppliers from public;
Grant Privileges on Functions/Procedures
When dealing with functions and procedures, you can grant users the ability to execute
these functions and procedures. The Execute privilege is explained below:
Privilege Description
Execute Ability to compile the function/procedure.
Ability to execute the function/procedure directly.
The syntaxfor granting execute privileges on a function/procedure is:
grant execute on object to user;
For example, if you had a function called Find_Value and you wanted to grant execute
access to the user named smithj, you would execute the following statement:
grant execute on Find_Value to smithj;
If you wanted to grant all users the ability to execute this function, you would execute the
following:
grant execute on Find_Value to public;
Revoke Privileges on Functions/Procedures
Once you have granted execute privileges on a function or procedure, you may need to
revoke theseprivileges from a user. To do this, you can execute a revoke command.
The syntaxfor the revoking privileges on a function or procedure is:
revoke execute on object from user;
Page 20 of 77
If you wanted to revoke execute privileges on a function called Find_Value from a user
named anderson, you would execute the following statement:
revoke execute on Find_Value from anderson;
If you had granted privileges to public (all users) and you wanted to revoke these
privileges, you could execute thefollowing statement:
revoke execute on Find_Value from public;
SEQUENCES
CREATE SEQUENCE
The syntax for a sequence is:
CREATE SEQUENCE sequence_name
MINVALUEvalue
MAXVALUEvalue
START WITH value
INCREMENT BY value
CACHE value;
For example:
CREATE SEQUENCE supplier_seq
MINVALUE1
MAXVALUE999999999999999999999999999
START WITH 1
INCREMENT BY 1
CACHE 20;
ALTER SEQUENCE
Increment a sequence by a certain amount:
ALTER SEQUENCE <sequence_name> INCREMENT BY <integer>;
ALTER SEQUENCE seq_inc_by_ten INCREMENT BY10;
Change the maximum value of a sequence:
ALTER SEQUENCE <sequence_name> MAXVALUE<integer>;
ALTER SEQUENCE seq_maxval MAXVALUE 10;
Set the sequence to cycle or not cycle:
ALTER SEQUENCE <sequence_name> <CYCLE | NOCYCLE>;
ALTER SEQUENCE seq_cycle NOCYCLE;
Configure the sequence to cache a value:
ALTER SEQUENCE <sequence_name> CACHE <integer> | NOCACHE;
ALTER SEQUENCE seq_cache NOCACHE;
Set whetherornot the values are to be returnedin order
ALTER SEQUENCE <sequence_name> <ORDER | NOORDER>;
ALTER SEQUENCE seq_order NOORDER;
1 - Overview of PL/SQL
This chapter introduces themain features of the PL/SQL language. It shows how PL/SQL
deals with the challenges of database programming, and how you can reuse techniques that
you know from other programming languages.
Advantages of PL/SQL
PL/SQL is a completely portable, high-performance transaction processing language that
offers the following advantages:
 Support for SQL
 Support for object-oriented programming
 Better performance
 Higher productivity
 Full portability
 Tight integration with Oracle
 Tight security
Tight Integration with SQL
The PL/SQL language is tightly integrated with SQL. You do not have to translate
between SQL and PL/SQL datatypes:a NUMBER or VARCHAR2 column in thedatabase is
stored in a NUMBER or VARCHAR2 variable in PL/SQL. This integration saves you both
learning time and processing time. Special PL/SQL language features let you work with
table columns and rows without specifyingthe datatypes, saving on maintenance work
when the table definitions change.
Running a SQL query and processing the result set is as easy in PL/SQL as opening a text
file and processing each line in popular scripting languages.
Using PL/SQL to access metadata about database objects and handle database error
conditions, you can writeutility programs for database administration that are reliable and
produce readable output about thesuccess of each operation.
Many databasefeatures, such as triggers and object types, makeuse of PL/SQL. You can
write thebodies of triggers and methods for object types in PL/SQL.
Support for SQL
SQL has become the standard database language because it is flexible, powerful, and easy
to learn. A few English-like commands such as SELECT, INSERT, UPDATE, and DELETE make
it easy to manipulate thedata stored in a relational database.
PL/SQL lets you use all theSQL data manipulation, cursor control, and transaction control
commands, as well as all theSQL functions, operators, and pseudocolumns. This extensive
SQL support lets you manipulateOracle data flexibly and safely. Also, PL/SQL fully
supports SQLdatatypes, reducing theneed to convert data passed between your
applications and the database.
PL/SQL also supports dynamicSQL, a programming technique that makes your
applications more flexible and versatile. Your programs can build and process SQL data
definition, data control, and session control statements at run time, without knowing
details such as table names and WHERE clauses in advance.
Better Performance
Without PL/SQL, Oracle must process SQL statements one at a time. Programs that issue
many SQL statements require multiple calls to the database, resulting in significant
network and performance overhead.
With PL/SQL, an entire block of statements can be sent to Oracle at one time. This can
drastically reduce network traffic between thedatabase and an application. As Figure 1-1
shows, you can use PL/SQL blocks and subprograms to group SQL statements before
sending them to thedatabase for execution. PL/SQL even has language features to further
speed up SQL statements that are issued inside a loop.
Page 21 of 77
PL/SQL stored procedures are compiled once and stored in executable form, so procedure
calls are efficient. Because stored procedures execute in thedatabase server, a single call
over the network can start a large job. This division of work reduces network traffic and
improves responsetimes. Stored procedures are cached and shared among users, which
lowers memory requirements and invocation overhead.
Figure 1-1 PL/SQL Boosts Performance
Description of the illustration lnpls005.gif
Higher Productivity
PL/SQL extends tools such as Oracle Forms and Oracle Reports. With PL/SQL in these
tools, you can use familiar language constructs to build applications. For example, you can
use an entire PL/SQL block in an Oracle Forms trigger, instead of multiple trigger steps,
macros, or user exits.
PL/SQL is the same in all environments. Once you learn PL/SQL with one Oracle tool,
you can transfer your knowledge to other tools.
Full Portability
Applications written in PL/SQL can run on any operating systemand platformwhere the
Oracle database runs. With PL/SQL, you can writeportable program libraries and reuse
them in different environments.
Tight Security
PL/SQL stored procedures move application code from the client to theserver, where you
can protect it from tampering, hide the internal details, and restrict who has access. For
example, you can grant users access to a procedure that updates a table, but not grant them
access to the table itself or to the text of the UPDATE statement.
Triggers written in PL/SQL can control or record changes to data, making sure that all
changes obey your business rules.
Support for Object-Oriented Programming
Object types arean ideal object-oriented modeling tool, which you can use to reduce the
cost and time required to build complex applications. Besides allowing you to create
softwarecomponents that are modular, maintainable, and reusable, object types allow
different teams of programmers to develop softwarecomponents concurrently.
By encapsulating operations with data, object types let you move data-maintenance code
out of SQL scripts and PL/SQL blocks into methods. Also, object types hide
implementation details, so that you can change the details without affecting client
programs.
In addition, object types allow for realistic data modeling. Complex real-world entities and
relationships map directly into object types. This direct mapping helps your programs
better reflect the world they are trying to simulate.
Features of PL/SQL
PL/SQL has the following features:
 PL/SQL is tightly integrated with SQL.
 It offers extensive error checking.
 It offers numerous data types.
 It offers a variety of programming structures.
 It supports structured programming through functions and procedures.
 It supports object oriented programming.
 It supports developingweb applications and server pages.
2 - Fundamentals of the PL/SQL
Language
This chapter focuses on thedetailed aspects of the language. Like other programming
languages, PL/SQL has a character set, reserved words, punctuation, datatypes, and fixed
syntaxrules.
Character Set
You writea PL/SQL program as lines of text using a specific set of characters:
Upper- and lower-case letters A .. Z and a .. z
Numerals 0 .. 9
Symbols ( ) + - * / < > = ! ~ ^ ; : . ' @ % , " # $ & _ | { } ? [ ]
Tabs, spaces, and carriage returns
PL/SQL keywords are not case-sensitive, so lower-case letters are equivalent to
corresponding upper-caseletters except within string and character literals.
Lexical Units
A line of PL/SQL text contains groups of characters known as lexical units:
delimiters (simple and compound symbols)
identifiers, which include reserved words
literals
comments
To improve readability, you can separate lexical units by spaces. In fact, you must separate
adjacent identifiers by a space or punctuation. Thefollowing line is not allowed because
the reserved words END and IF are joined:
IF x > y THEN high := x; ENDIF; -- not allowed, must be END IF
Page 22 of 77
You cannot embed spaces inside lexical units except for string literals and comments. For
example, thefollowing line is not allowed because the compound symbolfor assignment
(:=) is split:
count : = count + 1; -- not allowed, must be :=
To show structure, you can split lines using carriage returns, and indent lines using spaces
or tabs. The formatting makes the IF statement on the right more readable:
IF x>y THEN max:=x;ELSE max:=y;END IF; | IF x > y THEN
| max := x;
| ELSE
| max := y;
| END IF;
Delimiters
A delimiter is a simple or compound symbolthat has a special meaning to PL/SQL. For
example, you use delimiters to represent arithmetic operations such as addition and
subtraction.
Identifiers
You use identifiers to name PL/SQL program items and units, which include constants,
variables, exceptions, cursors, cursor variables, subprograms, and packages. Some
examples of identifiers follow:
An identifier consists of a letter optionally followed by more letters, numerals, dollar
signs, underscores, and number signs. Other characters such as hyphens, slashes, and
spaces are not allowed, as the following examples show:
mine&yours -- not allowed because ofampersand
debit-amount -- not allowed because ofhyphen
on/off -- not allowed because ofslash
user id -- not allowed because ofspace
Adjoining and trailing dollar signs, underscores, and number signs are allowed:
money$$$tree
SN##
try_again_
You can use upper, lower, or mixed case to write identifiers. PL/SQL is not case sensitive
except within string and character literals. If the only difference between identifiers is the
case of corresponding letters, PL/SQL considers them the same:
lastname
LastName -- same as lastname
LASTNAME -- same as lastname and LastName
The sizeof an identifier cannot exceed 30 characters. Every character, including dollar
signs, underscores, and number signs, is significant. For example, PL/SQL considers the
following identifiers to be different:
lastname
last_name
Identifiers should be descriptive. Avoid obscure names such as cpm. Instead, use
meaningful names such as cost_per_thousand.
Reserved Words
Some identifiers, called reserved words, have a special syntacticmeaning to PL/SQL. For
example, thewords BEGIN and END are reserved. Trying to redefine a reserved word causes
a compilation error. Instead, you can embed reserved words as part of a longer identifier:
DECLARE
-- end BOOLEAN; -- not allowed; causes compilation error
end_of_game BOOLEAN; -- allowed
BEGIN
NULL;
END;
/
PredefinedIdentifiers
Identifiers globally declared in package STANDARD, such as the exception
INVALID_NUMBER, can be redeclared. However, redeclaring predefined identifiers is error
pronebecause your local declaration overrides theglobal declaration.
QuotedIdentifiers
For flexibility, PL/SQL lets you enclose identifiers within double quotes. Quoted
identifiers are seldom needed, but occasionally they can be useful. They can contain any
sequence of printable characters including spaces but excluding double quotes. Thus, the
following identifiers are valid:
"X+Y"
"last name"
"on/off switch"
"employee(s)"
"*** header info ***"
The maximum size of a quoted identifier is 30 characters not counting the double quotes.
Though allowed, using PL/SQL reserved words as quoted identifiers is a poor
programming practice.
Literals
A literal is an explicit numeric, character, string, or Boolean value not represented by an
identifier. The numeric literal 147 and the Boolean literal FALSE are examples.
 Numeric Literals
 Character Literals
 String Literals
 Boolean Literals
 Datetime Literals
Comments
The PL/SQL compiler ignores comments, but you should not. Adding comments to your
program promotes readability and aids understanding. Generally, you use comments to
describe the purposeand use of each code segment. PL/SQL supportstwo comment styles:
single-line and multi-line.
Single-Line Comments
Single-line comments begin with a double hyphen (--) anywhereon a line and extend to the
end of the line. A few examples follow:
DECLARE
howmany NUMBER;
BEGIN
-- begin processing
SELECT count(*) INTO howmany FROM user_objects
WHERE object_type = 'TABLE'; -- Check number oftables
howmany := howmany * 2; -- Compute some other value
END;
/
Notice that comments can appear within a statement at theend of a line.
While testing or debugging a program, you might want to disable a line of code. The
following example shows how you can "comment-out" theline:
-- DELETE FROM employees WHERE comm_pct IS NULL;
Multi-lineComments
Page 23 of 77
Multi-line comments begin with a slash-asterisk (/*), end with an asterisk-slash (*/), and
can span multiple lines. Some examples follow:
DECLARE
some_condition BOOLEAN;
pi NUMBER := 3.1415926; radius NUMBER := 15; area NUMBER;
BEGIN
/* Perform some simple tests and assignments */
IF 2 + 2 = 4 THEN
some_condition := TRUE; /* We expect this THEN to always be done */
END IF;
/*
The following line computes the area ofa
circle using pi, which is the ratio between
the circumference and diameter.
*/
area := pi * radius**2;
END;
/
You can use multi-line comment delimiters to comment-out whole sections of code:
/*
LOOP
FETCH c1 INTO emp_rec;
EXIT WHEN c1%NOTFOUND;
...
END LOOP;
*/
Restrictions on Comments
You cannot nest comments.
You cannot use single-line comments in a PL/SQL block that will be processed by an
Oracle Precompiler program because end-of-line characters are ignored. As a result,
single-line comments extend to the end of the block, not just to theend of a line. In this
case, use the/* */ notation instead.
Declarations
Your program stores values in variables and constants. As the program executes, the
values of variables can change, but the values of constants cannot.
You can declare variables and constants in thedeclarative part of any PL/SQL block,
subprogram, or package. Declarations allocate storage space for a value, specify its
datatype, and name the storage location so that you can reference it.
A couple of examples follow:
DECLARE
birthday DATE;
emp_count SMALLINT := 0;
The first declaration names a variable of type DATE. Thesecond declaration names a
variable of typeSMALLINT and uses theassignment operator to assign an initial value of
zero to the variable.
The next examples show that the expression following theassignment operator can be
arbitrarily complex and can refer to previously initialized variables:
DECLARE
pi REAL := 3.14159;
radius REAL := 1;
area REAL := pi * radius**2;
BEGIN
NULL;
END;
/
By default, variables are initialized to NULL, so it is redundant to include ":= NULL" in a
variable declaration.
To declare a constant, put thekeyword CONSTANT before thetypespecifier:
DECLARE
credit_limit CONSTANT REAL := 5000.00;
max_days_in_year CONSTANT INTEGER := 366;
urban_legend CONSTANT BOOLEAN := FALSE;
BEGIN
NULL;
END;
/
This declaration names a constant of type REAL and assigns an unchangeable value of 5000
to the constant. A constant must be initialized in its declaration. Otherwise, you get a
compilation error.
Using DEFAULT
You can use the keyword DEFAULT instead of theassignment operator to initialize
variables. For example, thedeclaration
blood_type CHAR := 'O';
can be rewritten as follows:
blood_type CHAR DEFAULT 'O';
Use DEFAULT for variables that have a typicalvalue. Use the assignment operator for
variables (such as counters and accumulators) that have no typicalvalue. For example:
hours_worked INTEGER DEFAULT 40;
employee_count INTEGER := 0;
You can also use DEFAULT to initialize subprogram parameters, cursor parameters, and
fields in a user-defined record.
Using NOT NULL
Besides assigning an initial value, declarations can imposethe NOT NULL constraint:
DECLARE
acct_id INTEGER(4) NOT NULL := 9999;
You cannot assign nulls to a variable defined as NOT NULL. If you try, PL/SQL raises the
predefined exception VALUE_ERROR.
The NOT NULL constraint must be followed by an initialization clause.
PL/SQL provide subtypes NATURALN and POSITIVEN that are predefined as NOT NULL. You
can omit theNOT NULL constraint when declaring variables of these types, and you must
include an initialization clause.
Using the %TYPE Attribute
The %TYPE attributeprovides the datatypeof a variable or database column. In the
following example, %TYPE provides thedatatypeof a variable:
DECLARE
credit NUMBER(7,2);
debit credit%TYPE;
name VARCHAR2(20) := 'JoHn SmItH';
-- If we increase the length ofNAME, the other variables
-- become longer too.
upper_name name%TYPE := UPPER(name);
lower_name name%TYPE := LOWER(name);
Page 24 of 77
init_name name%TYPE := INITCAP(name);
BEGIN
NULL;
END;
/
Variables declared using %TYPE are treated like thosedeclared using a datatypespecifier.
For example, given the previous declarations, PL/SQL treats debit like a REAL(7,2) variable.
A %TYPE declaration can also include an initialization clause.
The %TYPE attributeis particularly useful when declaring variables that refer to database
columns. You can reference a table and column, or you can reference an owner, table, and
column, as in
DECLARE
-- If the length ofthe column ever changes, this code
-- will use the new length automatically.
the_trigger user_triggers.trigger_name%TYPE;
BEGIN
NULL;
END;
/
When you use table_name.column_name.TYPE to declare a variable, you do not need to know
the actual datatype, and attributes such as precision, scale, and length. If the database
definition of thecolumn changes, the datatypeof thevariable changes accordingly at run
time.
%TYPE variables do not inherit the NOT NULL column constraint. In the next example, even
though the database column employee_id is defined as NOT NULL, you can assign a null to the
variable my_empno:
DECLARE
my_empno employees.employee_id%TYPE;
BEGIN
my_empno := NULL; -- this works
END;
/
Using the %ROWTYPE Attribute
The %ROWTYPE attributeprovides a record typethat represents arow in a table (or view).
The record can storean entire row of data selected from thetable, or fetched from a cursor
or strongly typed cursor variable:
DECLARE
-- %ROWTYPE can include all the columns in a table...
emp_rec employees%ROWTYPE;
-- ...or a subset ofthe columns, based on a cursor.
CURSOR c1 IS
SELECT department_id, department_name FROM departments;
dept_rec c1%ROWTYPE;
-- Could even make a %ROWTYPE with columns from multiple tables.
CURSOR c2 IS
SELECT employee_id, email, employees.manager_id, location_id
FROM employees, departments
WHERE employees.department_id = departments.department_id;
join_rec c2%ROWTYPE;
BEGIN
-- We know EMP_REC can hold a row fromthe EMPLOYEES table.
SELECT * INTO emp_rec FROM employees WHERE ROWNUM < 2;
-- We can refer to the fields of EMP_REC using column names
-- from the EMPLOYEES table.
IF emp_rec.department_id = 20 AND emp_rec.last_name = 'JOHNSON' THEN
emp_rec.salary := emp_rec.salary * 1.15;
END IF;
END;
/
Columns in a row and corresponding fields in a record have the same names and datatypes.
However, fields in a %ROWTYPE record do not inherit the NOT NULL column constraint.
Aggregate Assignment
Although a %ROWTYPE declaration cannot include an initialization clause, there are ways to
assign values to all fields in a record at once. You can assign one record to another if their
declarations refer to the same table or cursor. For example, the following assignment is
allowed:
DECLARE
dept_rec1 departments%ROWTYPE;
dept_rec2 departments%ROWTYPE;
CURSOR c1 IS SELECT department_id, location_id FROM departments;
dept_rec3 c1%ROWTYPE;
BEGIN
dept_rec1 := dept_rec2; -- allowed
-- dept_rec2 refers to a table, dept_rec3 refers to a cursor
-- dept_rec2 := dept_rec3; -- not allowed
END;
/
You can assign a list of column values to a record by using the SELECT or FETCH statement,
as the following example shows. The column names must appear in the order in which
they were defined by the CREATE TABLE or CREATE VIEW statement.
DECLARE
dept_rec departments%ROWTYPE;
BEGIN
SELECT * INTO dept_rec FROM departments
WHERE department_id = 30 and ROWNUM < 2;
END;
/
However, there is no constructor for a record type, so you cannot assign a list of column
values to a record by using an assignment statement.
Using Aliases
Select-list items fetched from a cursor associated with %ROWTYPE must have simple names
or, if they are expressions, must have aliases. Thefollowing example uses an alias called
complete_name to represent the concatenation of two columns:
BEGIN
-- We assign an alias (COMPLETE_NAME) to the expression value, because
-- it has no column name.
FOR itemIN
(
SELECT first_name || ' ' || last_name complete_name
FROM employees WHERE ROWNUM < 11
)
LOOP
-- Now we can refer to the field in the record using this alias.
dbms_output.put_line('Employee name: ' || item.complete_name);
END LOOP;
END;
/
Restrictions on Declarations
PL/SQL does not allow forward references. You must declare a variable or constant before
referencing it in other statements, including other declarative statements.
Page 25 of 77
PL/SQL does allow theforward declaration of subprograms. Some languages allow you to
declare a list of variables that have thesame datatype. PL/SQL does not allow this. You
must declare each variable separately:
DECLARE
-- Multiple declarations not allowed.
-- i, j, k, l SMALLINT;
-- Instead, declare each separately.
i SMALLINT;
j SMALLINT;
-- To save space, you can declare more than one on a line.
k SMALLINT; l SMALLINT;
BEGIN
NULL;
END;
/
Assigning Values to Variables
You can use assignment statements to assign values to variables. For example, the
following statement assigns a new value to the variable bonus, overwriting its old value:
bonus := salary * 0.15;
Unless you expressly initialize a variable, its value is undefined (NULL).Variables and
constants are initialized every time a block or subprogram is entered. By default, variables
are initialized to NULL:
DECLARE
counter INTEGER;
BEGIN
-- COUNTER is initially NULL, so 'COUNTER + 1' is also null.
counter := counter + 1;
IF counter IS NULL THEN
dbms_output.put_line('Sure enough, COUNTER is NULL not 1.');
END IF;
END;
/
To avoid unexpected results, never reference a variable before you assign it a value.
The expression following theassignment operator can be arbitrarily complex, but it must
yield a datatypethat is the same as or convertible to thedatatypeof the variable.
Assigning Boolean Values
Only the values TRUE, FALSE, and NULL can be assigned to a Boolean variable. You can
assign these literal values, or expressions such as comparisons using relational operators.
DECLARE
done BOOLEAN; -- DONE is initially NULL
counter NUMBER := 0;
BEGIN
done := FALSE; -- Assign a literal value
WHILE done != TRUE -- Compare to a literal value
LOOP
counter := counter + 1;
done := (counter > 500); -- If counter > 500, DONE = TRUE
END LOOP;
END;
/
Assigning a SQL Query Result to a PL/SQL Variable
You can use the SELECT statement to have Oracle assign values to a variable. For each item
in the select list, there must be a corresponding, type-compatiblevariable in the INTO list.
For example:
DECLARE
emp_id employees.employee_id%TYPE := 100;
emp_name employees.last_name%TYPE;
wages NUMBER(7,2);
BEGIN
SELECT last_name, salary + (salary * nvl(commission_pct,0))
INTO emp_name, wages FROM employees
WHERE employee_id = emp_id;
dbms_output.put_line('Employee ' || emp_name || ' might make ' || wages);
END;
/
Because SQL does not have a Boolean type, you cannot select column values into a
Boolean variable.
PL/SQL Expressions and Comparisons
Expressions are constructed using operands and operators. An operand is a variable,
constant, literal, or function call that contributes a value to an expression. An example of a
simple arithmetic expression follows:
-X / 2 + 3
Unary operators such as thenegation operator (-) operateon one operand; binary operators
such as the division operator (/) operateon two operands. PL/SQL has no ternary
operators.
The simplest expressions consist of a single variable, which yields a value directly.
PL/SQL evaluates an expression by combining thevalues of the operands in ways
specified by the operators. An expression always returns a single value. PL/SQL
determines thedatatypeof this value by examining the expression and the context in which
it appears.
Operator Precedence
The operations within an expression are done in a particular order depending on their
precedence (priority). Table2-1 shows the default order of operations from first to last
(top to bottom).
Table 2-1 Order of Operations
Operator Operation
** exponentiation
+, - identity, negation
*, / multiplication, division
+, -, || addition, subtraction,
concatenation
=, <, >, <=, >=, <>, !=, ~=, ^=, IS NULL, LIKE, BETWEEN,
IN
comparison
NOT logical negation
AND conjunction
OR inclusion
Logical Operators
Page 26 of 77
The logical operators AND, OR, and NOT follow the tri-statelogic shown in Table 2-2. AND
and OR are binary operators; NOT is a unary operator.
Table 2-2 Logic Truth Table
x y x AND y x OR y NOT x
TRUE TRUE TRUE TRUE FALSE
TRUE FALSE FALSE TRUE FALSE
TRUE NULL NULL TRUE FALSE
FALSE TRUE FALSE TRUE TRUE
FALSE FALSE FALSE FALSE TRUE
FALSE NULL FALSE NULL TRUE
NULL TRUE NULL TRUE NULL
NULL FALSE FALSE NULL NULL
NULL NULL NULL NULL NULL
Order of Evaluation
When you do not use parentheses to specify the order of evaluation, operator precedence
determines theorder. Compare thefollowing expressions:
NOT (valid AND done) | NOT valid AND done
If the Boolean variables valid and done have the value FALSE, the first expression yields
TRUE. However, the second expression yields FALSE because NOT has a higher precedence
than AND. Therefore, the second expression is equivalent to:
(NOT valid) AND done
In the following example, notice that when valid has the value FALSE, the whole expression
yields FALSE regardless of the value of done:
valid AND done
Likewise, in the next example, when valid has the value TRUE, the whole expression yields
TRUE regardless of the value of done:
valid OR done
Short-Circuit Evaluation
When evaluating a logical expression, PL/SQL uses short-circuit evaluation. That is,
PL/SQL stops evaluating theexpression as soon as the result can be determined. This lets
you write expressions that might otherwisecause an error. Consider the following OR
expression:
DECLARE
on_hand INTEGER := 0;
on_order INTEGER := 100;
BEGIN
-- Does not cause divide-by-zero error; evaluation stops after 1st expr.
IF (on_hand = 0) OR ((on_order / on_hand) < 5) THEN
dbms_output.put_line('There are no more widgets left!');
END IF;
END;
/
When thevalue of on_hand is zero, the left operand yields TRUE, so PL/SQL does not
evaluate the right operand. If PL/SQL evaluated both operands before applyingthe OR
operator, the right operand would cause a division by zero error.
Comparison Operators
Comparison operators compare one expression to another. Theresult is always true, false,
or null. Typically, you usecomparison operators in conditional control statements and in
the WHERE clause of SQL data manipulation statements. Here are some examples of
comparisons for different types:
DECLARE
PROCEDURE assert(assertion VARCHAR2, truth BOOLEAN)
IS
BEGIN
IF truth IS NULL THEN
dbms_output.put_line('Assertion ' || assertion || ' is unknown (NULL)');
ELSIF truth = TRUE THEN
dbms_output.put_line('Assertion ' || assertion || ' is TRUE');
ELSE
dbms_output.put_line('Assertion ' || assertion || ' is FALSE');
END IF;
END;
BEGIN
assert('2 + 2 = 4', 2 + 2 = 4);
assert('10 > 1', 10 > 1);
assert('10 <= 1', 10 <= 1);
assert('5 BETWEEN 1 AND 10', 5 BETWEEN 1 AND 10);
assert('NULL != 0', NULL != 0);
assert('3 IN (1,3,5)', 3 IN (1,3,5));
assert('''A'' < ''Z''', 'A' < 'Z');
assert('''baseball'' LIKE ''%all%''', 'baseball' LIKE '%all%');
assert('''suit'' || ''case'' = ''suitcase''', 'suit' || 'case' = 'suitcase');
END;
/
Relational Operators
Operator Meaning
= equal to
<>, !=, ~=, ^= not equal to
< less than
> greater than
<= less than or equal to
>= greater than or equal to
IS NULL Operator
The IS NULL operator returns the Boolean value TRUE if its operand is null or FALSE if it is
not null. Comparisons involving nulls always yield NULL. Test whether a value is null as
follows:
IF variable IS NULL THEN ...
LIKE Operator
You use theLIKE operator to compare a character, string, or CLOB value to a pattern. Case
is significant. LIKE returns the Boolean value TRUE if thepatterns match or FALSE if they do
not match.
The patterns matched by LIKE can include two special-purposecharacters called
wildcards. An underscore (_) matches exactly one character; a percent sign (%) matches
zero or more characters. For example, if thevalue of ename is 'JOHNSON', thefollowing
expression is true:
Page 27 of 77
ename LIKE 'J%S_N'
To search for the percent sign and underscore characters, you define an escape character
and put that character before the percent sign or underscore. The following example uses
the backslash as the escape character, so that thepercent sign in the string does not act as a
wildcard:
IF sale_sign LIKE '50% off!' ESCAPE '' THEN...
BETWEEN Operator
The BETWEEN operator tests whether a value lies in a specified range. It means "greater
than or equal to low value and less than or equal to high value." For example, the
following expression is false:
45 BETWEEN 38 AND 44
IN Operator
The IN operator tests set membership. It means "equal to any member of." The set can
contain nulls, but they are ignored. For example, the following expression tests whether a
value is part of a set of values:
letter IN ('a','b','c')
Be careful when inverting this condition. Expressions of theform:
value NOT IN set
yield FALSE if the set contains a null.
Concatenation Operator
Double vertical bars (||) serve as the concatenation operator, which appends one string
(CHAR, VARCHAR2, CLOB, or the equivalent Unicode-enabled type) to another. For example,
the expression
'suit' || 'case'
returns the following value:
'suitcase'
If both operands have datatype CHAR, the concatenation operator returns a CHAR value. If
either operand is a CLOB value, the operator returns a temporary CLOB. Otherwise, it
returns a VARCHAR2 value.
CASE Expressions
A CASE expression selects a result from one or more alternatives, and returns the result.
Although it contains a block that might stretch over several lines, it really is an expression
that forms part of a larger statement, such as an assignment or a procedure call.
The CASE expression uses a selector, an expression whose value determines which
alternative to return. A CASE expression has thefollowing form:
CASE selector
WHEN expression1 THEN result1
WHEN expression2 THEN result2
...
WHEN expressionN THEN resultN
[ELSE resultN+1]
END
The selector is followed by one or more WHEN clauses, which are checked sequentially.
The value of theselector determines which clause is evaluated. Thefirst WHEN clause that
matches the value of the selector determines theresult value, and subsequent WHEN clauses
are not evaluated. For example:
DECLARE
grade CHAR(1) := 'B';
appraisal VARCHAR2(20);
BEGIN
appraisal :=
CASE grade
WHEN 'A' THEN 'Excellent'
WHEN 'B' THEN 'Very Good'
WHEN 'C' THEN 'Good'
WHEN 'D' THEN 'Fair'
WHEN 'F' THEN 'Poor'
ELSE 'No such grade'
END;
dbms_output.put_line('Grade ' || grade || ' is ' || appraisal);
END;
/
The optionalELSE clause works similarly to the ELSE clause in an IF statement. If thevalue
of the selector is not one of thechoices covered by a WHEN clause, theELSE clause is
executed. If no ELSE clause is provided and none of the WHEN clauses are matched, the
expression returns NULL.
An alternative to the CASE expression is the CASE statement, where each WHEN clause can
be an entire PL/SQL block.
SearchedCASEExpression
PL/SQL also provides a searched CASE expression, which lets you test different conditions
instead of comparing a single expression to various values. It has the form:
CASE
WHEN search_condition1 THEN result1
WHEN search_condition2 THEN result2
...
WHEN search_conditionN THEN resultN
[ELSE resultN+1]
END;
A searched CASE expression has no selector. Each WHEN clause contains a search condition
that yields a Boolean value, so you can test different variables or multiple conditions in a
single WHEN clause. For example:
DECLARE
grade CHAR(1) := 'B';
appraisal VARCHAR2(120);
id NUMBER := 8429862;
attendance NUMBER := 150;
min_days CONSTANT NUMBER := 200;
FUNCTION attends_this_school(id NUMBER) RETURN BOOLEAN IS
BEGIN RETURN TRUE; END;
BEGIN
appraisal :=
CASE
WHEN attends_this_school(id) = FALSE THEN 'N/A - Student not enrolled'
-- Have to put this condition early to detect
-- good students with bad attendance
WHEN grade = 'F' OR attendance < min_days THEN 'Poor (poor performance or bad attendance)'
WHEN grade = 'A' THEN 'Excellent'
WHEN grade = 'B' THEN 'Very Good'
WHEN grade = 'C' THEN 'Good'
WHEN grade = 'D' THEN 'Fair'
Page 28 of 77
ELSE 'No such grade'
END;
dbms_output.put_line('Result for student ' || id ||
' is ' || appraisal);
END;
/
The search conditions are evaluated sequentially. The Boolean value of each search
condition determines which WHEN clause is executed. If a search condition yields TRUE, its
WHEN clause is executed. After any WHEN clause is executed, subsequent search conditions
are not evaluated. If none of the search conditions yields TRUE, theoptional ELSE clause is
executed. If no WHEN clause is executed and no ELSE clause is supplied, thevalue of the
expression is NULL.
3 - PL/SQL Datatypes
Every constant, variable, and parameter has a datatype (or type), which specifies a storage
format, constraints, and valid range of values. PL/SQL provides many predefined
datatypes. For instance, you can choose from integer, floating point, character, Boolean,
date, collection, reference, and large object (LOB) types. PL/SQLalso lets you define your
own subtypes. This chapter covers thebasic types used frequently in PL/SQL programs.
Later chapters cover the more specialized types.
Overview of Predefined PL/SQL Datatypes
A scalar typehas no internal components. It holds a single value, such as a number or
character string.
A composite typehas internal components that can be manipulated individually, such as
the elements of an array.
A reference typeholds values, called pointers, that designate other program items.
A LOB typeholds values, called lob locators, that specify thelocation of large objects, such
as text blocks or graphic images, that are stored separately from other database data.
Figure 3-1 shows the predefined PL/SQL datatypes. Thescalar types fall into four
families, which storenumber, character, Boolean, and date/time data, respectively.
Figure 3-1 Built-in Datatypes
Description of the illustration lnpls006.gif
PL/SQL Number Types
Number types let you storenumeric data (integers, real numbers, and floating-point
numbers), represent quantities, and do calculations.
BINARY_INTEGER
You use theBINARY_INTEGER datatypeto storesigned integers. Its magnitude range is -
2**31 .. 2**31.
BINARY_INTEGER values require less storage than NUMBER values. Arithmetic operations on
BINARY_INTEGER values are also faster than NUMBER arithmetic. BINARY_INTEGER and
PLS_INTEGER both have theseadvantages. Because PLS_INTEGER was faster in earlier
releases, you might use it instead of BINARY_INTEGER in code that will run on older
databases.
BINARY_INTEGER Subtypes
A base type is thedatatypefromwhich a subtypeis derived. A subtype associates a base
typewith aconstraint and so defines a subset of values. For your convenience, PL/SQL
predefines the following BINARY_INTEGER subtypes:
NATURAL
NATURALN
POSITIVE
POSITIVEN
SIGNTYPE
The subtypes NATURAL and POSITIVE let you restrict an integer variable to non-negative or
positivevalues, respectively. NATURALN and POSITIVEN prevent theassigning of nulls to an
integer variable. SIGNTYPE lets you restrict an integer variable to thevalues -1, 0, and 1,
which is useful in programming tri-statelogic.
BINARY_FLOAT and BINARY_DOUBLE
Single-precision and double-precision IEEE 754-format single-precision floating-point
numbers. These types areused primarily for high-speed scientific computation.
Literals of thesetypes end with f (for BINARY_FLOAT) or d (for BINARY_DOUBLE). For
example, 2.07f or 3.000094d.
Computations involving these types producespecialvalues that you need to check for,
rather than raising exceptions. To help deal with overflow, underflow, and other conditions
that can occur with thesenumbers, you can use several special predefined constants:
BINARY_FLOAT_NAN, BINARY_FLOAT_INFINITY, BINARY_FLOAT_MAX_NORMAL,
BINARY_FLOAT_MIN_NORMAL, BINARY_FLOAT_MAX_SUBNORMAL,
BINARY_FLOAT_MIN_SUBNORMAL, and corresponding names starting with BINARY_DOUBLE.
The constants for NaN ("not a number") and infinity are also defined by SQL; the others are
PL/SQL-only.
NUMBER
You use theNUMBER datatypeto storefixed-point or floating-point numbers. Its magnitude
range is 1E-130 .. 10E125. If the value of an expression falls outside this range, you get a
numeric overflow or underflow error. You can specify precision, which is the totalnumber
of digits, and scale, which is the number of digits to the right of thedecimal point. The
syntaxfollows:
NUMBER[(precision,scale)]
To declare fixed-point numbers, for which you must specify scale, use thefollowing form:
NUMBER(precision,scale)
Page 29 of 77
To declare floating-point numbers, for which you cannot specify precision or scale
because the decimal point can "float" to any position, use the following form:
NUMBER
To declare integers, which have no decimal point, use this form:
NUMBER(precision) -- same as NUMBER(precision,0)
You cannot use constants or variables to specify precision and scale; you must use integer
literals. Themaximum precision of a NUMBER value is 38 decimal digits. If you do not
specify precision, it defaults to 38 or the maximum supported by your system, whichever
is less.
Scale, which can range from -84 to 127, determines where rounding occurs. For instance, a
scale of 2 rounds to the nearest hundredth (3.456 becomes 3.46). A negative scale rounds
to the left of the decimal point. For example, a scale of -3 rounds to the nearest thousand
(3456 becomes 3000). A scale of 0 rounds to thenearest whole number. If you do not
specify scale, it defaults to 0.
NUMBER Subtypes
You can use the following NUMBER subtypes for compatibility with ANSI/ISO and IBM
types or when you want a more descriptivename:
DEC
DECIMAL
DOUBLE PRECISION
FLOAT
INTEGER
INT
NUMERIC
REAL
SMALLINT
Use thesubtypes DEC, DECIMAL, and NUMERIC to declare fixed-point numbers with a
maximum precision of 38 decimal digits.
Use thesubtypes DOUBLE PRECISION and FLOAT to declare floating-point numbers with a
maximum precision of 126 binary digits, which is roughly equivalent to 38 decimal digits.
Or, use thesubtypeREALto declare floating-point numbers with a maximum precision of
63 binary digits, which is roughly equivalent to 18 decimal digits.
Use thesubtypes INTEGER, INT, and SMALLINT to declare integers with a maximum
precision of 38 decimal digits.
PLS_INTEGER
You use thePLS_INTEGER datatypeto storesigned integers. Its magnitude range is -2**31 ..
2**31. PLS_INTEGER values require less storage than NUMBER values. Also, PLS_INTEGER
operations use machine arithmetic, so they are faster than NUMBER and BINARY_INTEGER
operations, which use library arithmetic. For efficiency, use PLS_INTEGER for all
calculations that fall within its magnitude range.
Although PLS_INTEGER and BINARY_INTEGER have the same magnitude range, they are not
fully compatible. When a PLS_INTEGER calculation overflows, an exception is raised.
However, when a BINARY_INTEGER calculation overflows, no exception is raised if the
result is assigned to a NUMBER variable.
Because of this small semantic difference, you might want to continue using
BINARY_INTEGER in old applications for compatibility. In new applications, always use
PLS_INTEGER for better performance.
PL/SQL Character and String Types
Character types let you storealphanumeric data, represent words and text, and manipulate
character strings.
CHAR
You use theCHAR datatypeto storefixed-length character data. How thedata is
represented internally depends on thedatabase character set. The CHAR datatypetakes an
optionalparameter that lets you specify a maximum sizeup to 32767 bytes. You can
specify thesize in terms of bytes or characters, where each character contains one or more
bytes, depending on the character set encoding. The syntaxfollows:
CHAR[(maximum_size [CHAR | BYTE] )]
You cannot use a symbolic constant or variable to specify themaximum size; you must
use an integer literal in the range 1 .. 32767.
If you do not specify a maximum size, it defaults to 1. If you specify themaximum sizein
bytes rather than characters, a CHAR(n) variable might be too small to hold n multibyte
characters. To avoid this possibility, usethe notation CHAR(n CHAR)so that the variable can
hold n characters in the database character set, even if some of thosecharacters contain
multiple bytes. When you specify thelength in characters, the upper limit is still 32767
bytes. So for double-byteand multibytecharacter sets, you can only specify 1/2 or 1/3 as
many characters as with a single-byte character set.
Although PL/SQL character variables can be relatively long, you cannot insert CHAR
values longer than 2000 bytes into a CHAR database column.
You can insert any CHAR(n) value into a LONG database column because the maximum
width of a LONG column is 2**31 bytes or two gigabytes. However, you cannot retrieve a
value longer than 32767 bytes from a LONG column into a CHAR(n) variable.
When you do not use the CHAR or BYTE qualifiers, the default is determined by the setting
of the NLS_LENGTH_SEMANTICS initialization parameter. When a PL/SQL procedure is
compiled, thesetting of this parameter is recorded, so that the same setting is used when
the procedure is recompiled after being invalidated.
CHAR Subtype
The CHAR subtypeCHARACTER has thesame range of values as its base type. That is,
CHARACTER is just another name for CHAR. You can use this subtypefor compatibility with
ANSI/ISO and IBM types or when you want an identifier more descriptive than CHAR.
LONG and LONG RAW
You use theLONG datatypeto storevariable-length character strings. The LONG datatypeis
like the VARCHAR2 datatype, except that the maximum size of a LONG value is 32760 bytes.
You use theLONG RAW datatypeto storebinary data or bytestrings. LONG RAW data is like
LONG data, except that LONG RAW data is not interpreted by PL/SQL. The maximum sizeof
a LONG RAW value is 32760 bytes.
You can insert any LONG value into a LONG database column because themaximum width
of a LONG column is 2**31 bytes. However, you cannot retrieve a value longer than 32760
bytes from a LONG column into a LONG variable.
Likewise, you can insert any LONG RAW value into a LONG RAW database column because
the maximum width of a LONG RAW column is 2**31 bytes. However, you cannot retrieve
a value longer than 32760 bytes froma LONG RAW column into a LONG RAW variable.
LONG columns can store text, arrays of characters, or even short documents. You can
reference LONG columns in UPDATE, INSERT, and (most) SELECT statements, but not in
expressions, SQL function calls, or certain SQL clauses such as WHERE, GROUP BY, and
CONNECT BY.
Page 30 of 77
Note: In SQL statements, PL/SQL binds LONG values as VARCHAR2, not as LONG.
However, if the length of the bound VARCHAR2 exceeds the maximum width of a
VARCHAR2 column (4000 bytes), Oracle converts the bind typeto LONG automatically, then
issues an error message because you cannot pass LONG values to a SQL function.
RAW
You use theRAW datatypeto storebinary data or bytestrings. For example, a RAW variable
might storea sequence of graphics characters or a digitized picture. Raw data is like
VARCHAR2 data, except that PL/SQL does not interpret raw data. Likewise, Oracle Net
does no character set conversions when you transmit raw data from one systemto another.
The RAW datatypetakes a required parameter that lets you specify a maximum size up to
32767 bytes. Thesyntaxfollows:
RAW(maximum_size)
You cannot use a symbolic constant or variable to specify themaximum size; you must
use an integer literal in the range 1 .. 32767.
You cannot insert RAW values longer than 2000 bytes into a RAW column. You can insert
any RAW value into a LONG RAW database column because the maximum width of a LONG
RAW column is 2**31 bytes. However, you cannot retrieve a value longer than 32767 bytes
from a LONG RAW column into a RAW variable.
ROWID and UROWID
Internally, every database table has a ROWID pseudocolumn, which stores binary values
called rowids. Each rowid represents the storage address of a row. A physical rowid
identifies a row in an ordinary table. A logical rowid identifies a row in an index-
organized table. The ROWID datatypecan store only physicalrowids. However, the UROWID
(universal rowid) datatypecan store physical, logical, or foreign (non-Oracle) rowids.
Suggestion: UsetheROWID datatypeonly for backward compatibility with old
applications. For new applications, use theUROWID datatype.
When you select or fetch a rowid into a ROWID variable, you can use the built-in function
ROWIDTOCHAR, which converts the binary value into an 18-bytecharacter string.
Conversely, thefunction CHARTOROWID converts a ROWID character string into a rowid. If
the conversion fails because the character string does not represent a valid rowid, PL/SQL
raises the predefined exception SYS_INVALID_ROWID. This also applies to implicit
conversions.
To convert between UROWID variables and character strings, use regular assignment
statements without any function call. The values are implicitly converted between UROWID
and character types.
Physical Rowids
Physical rowids provide fast access to particular rows. As long as the row exists, its
physicalrowid does not change. Efficient and stable, physicalrowids are useful for
selecting a set of rows, operating on the whole set, and then updating a subset. For
example, you can compare a UROWID variable with the ROWID pseudocolumn in the WHERE
clause of an UPDATE or DELETE statement to identify the latest row fetched from a cursor.
A physicalrowid can have either of two formats. The 10-byteextended rowid format
supports tablespace-relativeblock addresses and can identify rows in partitioned and non-
partitioned tables. The 6-byterestricted rowid format is provided for backward
compatibility.
Extended rowids use a base-64 encoding of thephysicaladdress for each row selected. For
example, in SQL*Plus (which implicitly converts rowids into character strings), the query
SQL> SELECT rowid, ename FROM emp WHERE empno = 7788;
might return thefollowing row:
ROWID ENAME
------------------ ----------
AAAAqcAABAAADFNAAH SCOTT
The format, OOOOOOFFFBBBBBBRRR, has four parts:
 OOOOOO:Thedata object number (AAAAqc in theexample above) identifies the
database segment. Schema objects in thesame segment, such as a cluster of
tables, have thesame data object number.
 FFF: The file number (AAB in the example) identifies the data file that contains
the row. File numbers are unique within a database.
 BBBBBB: Theblock number (AAADFN in the example) identifies the data block
that contains the row. Because block numbers are relative to their data file, not
their tablespace, two rows in thesame tablespace but in different data files can
have thesame block number.
 RRR: The row number (AAH in theexample) identifies therow in the block.
Logical Rowids
Logical rowids provide the fastest access to particular rows. Oracle uses them to construct
secondary indexes on index-organized tables. Having no permanent physicaladdress, a
logical rowid can move across data blocks when new rows are inserted. However, if the
physicallocation of a row changes, its logical rowid remains valid.
A logical rowid can include a guess, which identifies the block location of a row at the
time the guess is made. Instead of doing a full key search, Oracle uses the guess to search
the block directly. However, as new rows are inserted, guesses can become stale and slow
down access to rows. To obtain fresh guesses, you can rebuild thesecondary index.
You can use the ROWID pseudocolumn to select logical rowids (which are opaquevalues)
from an index-organized table. Also, you can insert logical rowids into a column of type
UROWID, which has a maximum sizeof 4000 bytes.
The ANALYZE statement helps you track the staleness of guesses. This is useful for
applications that storerowids with guesses in a UROWID column, then use therowids to
fetch rows.
Note: To manipulate rowids, you can use the supplied package DBMS_ROWID.
VARCHAR2
You use theVARCHAR2 datatypeto storevariable-length character data. How the data is
represented internally depends on thedatabase character set. The VARCHAR2 datatypetakes
a required parameter that specifies a maximum size up to 32767 bytes. Thesyntax follows:
VARCHAR2(maximum_size [CHAR | BYTE])
You cannot use a symbolic constant or variable to specify themaximum size; you must
use an integer literal in the range 1 .. 32767.
Small VARCHAR2 variables are optimized for performance, and larger ones are optimized
for efficient memory use. The cutoff point is 2000 bytes. For a VARCHAR2 that is 2000 bytes
or longer, PL/SQL dynamically allocates only enough memory to hold the actual value.
For a VARCHAR2 variable that is shorter than 2000 bytes, PL/SQL preallocates thefull
declared length of thevariable. For example, if you assign thesame 500-bytevalue to a
VARCHAR2(2000 BYTE) variable and to a VARCHAR2(1999 BYTE) variable, the former takes up
500 bytes and thelatter takes up 1999 bytes.
Page 31 of 77
If you specify themaximum size in bytes rather than characters, a VARCHAR2(n) variable
might be too small to hold n multibytecharacters. To avoid this possibility, usethe
notation VARCHAR2(n CHAR)so that thevariable can hold n characters in thedatabase
character set, even if some of thosecharacters contain multiple bytes. When you specify
the length in characters, theupper limit is still 32767 bytes. So for double-byte and
multibytecharacter sets, you can only specify 1/2 or 1/3 as many characters as with a
single-byte character set.
Although PL/SQL character variables can be relatively long, you cannot insert VARCHAR2
values longer than 4000 bytes into a VARCHAR2 database column.
You can insert any VARCHAR2(n) value into a LONG database column because the maximum
width of a LONG column is 2**31 bytes. However, you cannot retrieve a value longer than
32767 bytes from a LONG column into a VARCHAR2(n) variable.
When you do not use the CHAR or BYTE qualifiers, the default is determined by the setting
of the NLS_LENGTH_SEMANTICS initialization parameter. When a PL/SQL procedure is
compiled, thesetting of this parameter is recorded, so that the same setting is used when
the procedure is recompiled after being invalidated.
VARCHAR2 Subtypes
The VARCHAR2 subtypes below have the same range of values as their base type. For
example, VARCHAR is just another name for VARCHAR2.
STRING
VARCHAR
You can use these subtypes for compatibility with ANSI/ISO and IBM types.
Note: Currently, VARCHAR is synonymous with VARCHAR2. However, in future releases of
PL/SQL, to accommodate emerging SQL standards, VARCHAR might become a separate
datatypewith different comparison semantics. It is a good idea to use VARCHAR2 rather
than VARCHAR.
PL/SQL LOB Types
The LOB (large object) datatypes BFILE, BLOB, CLOB, and NCLOB let you storeblocks of
unstructured data (such as text, graphic images, video clips, and sound waveforms) up to
four gigabytes in size. And, they allow efficient, random, piece-wise access to the data.
The LOB types differ from the LONG and LONG RAW types in several ways. For example,
LOBs (except NCLOB) can be attributes of an object type, but LONGs cannot. Themaximum
size of a LOB is four gigabytes, but the maximum size of a LONG is two gigabytes. Also,
LOBs support randomaccess to data, but LONGs support only sequentialaccess.
LOB types store lob locators, which point to large objects stored in an external file, in-line
(inside therow) or out-of-line (outsidethe row). Database columns of type BLOB, CLOB,
NCLOB, or BFILE storethe locators. BLOB, CLOB, and NCLOB data is stored in the database, in
or outside therow. BFILE data is stored in operating systemfiles outside thedatabase.
PL/SQL operates on LOBs through thelocators. For example, when you select a BLOB
column value, only a locator is returned. If you got it during a transaction, the LOB locator
includes a transaction ID, so you cannot use it to updatethat LOB in another transaction.
Likewise, you cannot save a LOB locator during one session, then use it in another session.
BFILE
You use theBFILE datatypeto storelarge binary objects in operating systemfiles outside
the database. Every BFILE variable stores a file locator, which points to a large binary file
on the server. The locator includes a directory alias, which specifies a full path name
(logical path names are not supported).
BFILEs are read-only, so you cannot modify them. Thesize of a BFILE is systemdependent
but cannot exceed four gigabytes (2**32 - 1 bytes). Your DBA makes sure that a given
BFILE exists and that Oracle has read permissions on it. The underlying operating system
maintains file integrity.
BFILEs do not participatein transactions, are not recoverable, and cannot be replicated. The
maximum number of open BFILEs is set by theOracle initialization parameter
SESSION_MAX_OPEN_FILES, which is systemdependent.
BLOB
You use theBLOB datatypeto storelarge binary objects in thedatabase, in-line or out-of-
line. Every BLOB variable stores a locator, which points to a large binary object. The size
of a BLOB cannot exceed four gigabytes.
BLOBs participatefully in transactions, are recoverable, and can be replicated. Changes
made by package DBMS_LOB can be committed or rolled back. BLOB locators can span
transactions (for reads only), but they cannot span sessions.
CLOB
You use theCLOB datatypeto storelarge blocks of character data in the database, in-line or
out-of-line. Both fixed-width and variable-width character sets are supported. Every CLOB
variable stores a locator, which points to a large block of character data. The size of a CLOB
cannot exceed four gigabytes.
CLOBs participatefully in transactions, are recoverable, and can be replicated. Changes
made by package DBMS_LOB can be committed or rolled back. CLOB locators can span
transactions (for reads only), but they cannot span sessions.
NCLOB
You use theNCLOB datatypeto storelarge blocks of NCHAR data in thedatabase, in-line or
out-of-line. Both fixed-width and variable-width character sets are supported. Every
NCLOB variable stores a locator, which points to a large block of NCHAR data. Thesize of
an NCLOB cannot exceed four gigabytes.
NCLOBs participatefully in transactions, are recoverable, and can be replicated. Changes
made by package DBMS_LOB can be committed or rolled back. NCLOB locators can span
transactions (for reads only), but they cannot span sessions.
PL/SQL Boolean Types
PL/SQL has a typefor representing Boolean values (true and false). Because SQL does not
have an equivalent type, you can use BOOLEAN variables and parameters in PL/SQL
contexts but not inside SQL statements or queries.
BOOLEAN
You use theBOOLEAN datatypeto storethelogical values TRUE, FALSE, and NULL (which
stands for a missing, unknown, or inapplicable value). Only logic operations are allowed
on BOOLEAN variables.
The BOOLEAN datatypetakes no parameters. Only the values TRUE, FALSE, and NULL can be
assigned to a BOOLEAN variable.
You cannot insert the values TRUE and FALSE into a database column. You cannot select or
fetch column values into a BOOLEAN variable. Functions called from a SQL query cannot
take any BOOLEAN parameters. Neither can built-in SQL functions such as TO_CHAR; to
represent BOOLEAN values in output, you must use IF-THEN or CASE constructs to translate
BOOLEAN values into some other type, such as 0 or 1, 'Y' or 'N', 'true' or 'false', and so on.
PL/SQL Date, Time, and Interval Types
Page 32 of 77
The datatypes in this section let you storeand manipulate dates, times, and intervals
(periods of time). A variable that has a date/time datatypeholds values called datetimes; a
variable that has an interval datatypeholds values called intervals. A datetime or interval
consists of fields, which determine its value. The following list shows the valid values for
each field:
FieldName Valid Datetime Values Valid Interval Values
YEAR -4712 to 9999 (excluding year 0) Any nonzero integer
MONTH 01 to 12 0 to 11
DAY 01 to 31 (limited by the values of
MONTH and YEAR, according to therules
of the calendar for the locale)
Any nonzero integer
HOUR 00 to 23 0 to 23
MINUTE 00 to 59 0 to 59
SECOND 00 to 59.9(n), where 9(n) is the
precision of time fractional seconds
0 to 59.9(n), where 9(n)
is the precision of
interval fractional
seconds
TIMEZONE_HOUR -12 to 14 (range accommodates daylight
savings time changes)
Not applicable
TIMEZONE_MINUTE 00 to 59 Not applicable
TIMEZONE_REGION Found in the view V$TIMEZONE_NAMES Not applicable
TIMEZONE_ABBR Found in the view V$TIMEZONE_NAMES Not applicable
Except for TIMESTAMP WITH LOCAL TIMEZONE, thesetypes areall part of the SQL92
standard.
DATE
You use theDATE datatypeto storefixed-length datetimes, which include the time of day
in seconds since midnight. The date portion defaults to the first day of the current month;
the time portion defaults to midnight. The date function SYSDATE returns the current date
and time.
Tips:
 To compare dates for equality, regardless of thetime portion of each date, use
the function result TRUNC(date_variable) in comparisons, GROUP BY operations, and
so on.
 To find just the time portion of a DATEvariable, subtract thedate portion:
date_variable - TRUNC(date_variable).
Valid dates range from January 1, 4712 BC to December 31, 9999 AD. A Julian date is the
number of days since January 1, 4712 BC. Julian dates allow continuous dating from a
common reference. You can use the date format model 'J' with the date functions TO_DATE
and TO_CHAR to convert between DATE values and their Julian equivalents.
In date expressions, PL/SQL automatically converts character values in the default date
format to DATE values. The default date format is set by the Oracle initialization parameter
NLS_DATE_FORMAT. For example, the default might be 'DD-MON-YY', which includes a two-
digit number for theday of themonth, an abbreviation of the month name, and the last two
digits of theyear.
You can add and subtract dates. In arithmetic expressions, PL/SQL interprets integer
literals as days. For instance, SYSDATE + 1 signifies the same time tomorrow.
TIMESTAMP
The datatypeTIMESTAMP, which extends the datatype DATE, stores theyear, month, day,
hour, minute, and second. The syntaxis:
TIMESTAMP[(precision)]
where the optionalparameter precision specifies thenumber of digits in the fractional part of
the seconds field. You cannot use a symbolic constant or variable to specify theprecision;
you must use an integer literal in the range 0 .. 9. Thedefault is 6.
The default timestamp format is set by the Oracle initialization parameter
NLS_TIMESTAMP_FORMAT.
In the following example, you declare a variable of type TIMESTAMP, then assign a literal
value to it:
DECLARE
checkout TIMESTAMP(3);
BEGIN
checkout := '1999-06-22 07:48:53.275';
...
END;
In this example, the fractional part of the seconds field is 0.275.
Datetime and Interval Arithmetic
PL/SQL lets you construct datetime and interval expressions. The following list shows the
operators that you can use in such expressions:
Operand 1 Operator Operand 2 Result Type
datetime + interval datetime
datetime - interval datetime
interval + datetime datetime
datetime - datetime interval
interval + interval interval
interval - interval interval
interval * numeric interval
numeric * interval interval
interval / numeric interval
Overview of PL/SQL Subtypes
Each PL/SQL base typespecifies a set of values and a set of operations applicable to items
of that type. Subtypes specify thesame set of operations as their base type, but only a
subset of its values. A subtypedoes notintroduce a new type;rather, it places an optional
constraint on its base type.
Subtypes can increase reliability, providecompatibility with ANSI/ISO types, and improve
readability by indicating the intended use of constants and variables. PL/SQL predefines
several subtypes in package STANDARD. For example, PL/SQL predefines the subtypes
CHARACTER and INTEGER as follows:
SUBTYPE CHARACTER IS CHAR;
SUBTYPE INTEGER IS NUMBER(38,0); -- allows only whole numbers
The subtypeCHARACTER specifies thesame set of values as its base type CHAR, so
CHARACTER is an unconstrained subtype. But, thesubtypeINTEGER specifies only a subset
of the values of its base type NUMBER, so INTEGER is a constrained subtype.
Page 33 of 77
Defining Subtypes
You can define your own subtypes in thedeclarative part of any PL/SQL block,
subprogram, or package using thesyntax
SUBTYPE subtype_name IS base_type[(constraint)] [NOT NULL];
where subtype_name is a typespecifier used in subsequent declarations, base_type is any scalar
or user-defined PL/SQL datatype, and constraint applies only to base types that can specify
precision and scale or a maximum size.
Some examples follow:
DECLARE
SUBTYPE BirthDate IS DATE NOT NULL; -- based on DATE type
SUBTYPE Counter IS NATURAL; -- based on NATURAL subtype
TYPE NameList IS TABLE OF VARCHAR2(10);
SUBTYPE DutyRoster IS NameList; -- based on TABLE type
TYPE TimeRec IS RECORD (minutes INTEGER, hours INTEGER);
SUBTYPE FinishTime IS TimeRec; -- based on RECORD type
SUBTYPE ID_NumIS emp.empno%TYPE; -- based on column type
You can use %TYPE or %ROWTYPE to specify thebase type. When %TYPE provides the
datatypeof a database column, the subtypeinherits thesize constraint (if any) of the
column. The subtypedoes notinherit other kinds of constraints such as NOT NULL.
Using Subtypes
Once you define a subtype, you can declare items of that type. In the example below, you
declare a variable of type Counter. Noticehow the subtypenameindicates theintended use
of the variable.
DECLARE
SUBTYPE Counter IS NATURAL;
rows Counter;
You can constrain a user-defined subtypewhen declaring variables of that type:
DECLARE
SUBTYPE Accumulator IS NUMBER;
total Accumulator(7,2);
Subtypes can increase reliability by detecting out-of-range values. In the example below,
you restrict the subtype Numeral to storing integers in the range -9 .. 9. If your program tries
to store a number outsidethat range in a Numeral variable, PL/SQL raises an exception.
DECLARE
SUBTYPE Numeral IS NUMBER(1,0);
x_axis Numeral; -- magnitude range is -9 .. 9
y_axis Numeral;
BEGIN
x_axis := 10; -- raises VALUE_ERROR
...
END;
Type Compatibility
An unconstrained subtypeis interchangeable with its base type. For example, given the
following declarations, thevalue of amount can be assigned to total without conversion:
DECLARE
SUBTYPE Accumulator IS NUMBER;
amount NUMBER(7,2);
total Accumulator;
BEGIN
...
total := amount;
...
END;
Different subtypes areinterchangeable if they have the same base type:
DECLARE
SUBTYPE b1 IS BOOLEAN;
SUBTYPE b2 IS BOOLEAN;
finished b1; -- Different subtypes,
debugging b2; -- both based on BOOLEAN.
BEGIN
debugging := finished; -- They can be assigned to each other.
END;
Different subtypes arealso interchangeable if their base types arein the same datatype
family. For example, given the following declarations, thevalue of verb can be assigned to
sentence:
DECLARE
SUBTYPE Word IS CHAR(15);
SUBTYPE Text IS VARCHAR2(1500);
verb Word; -- Different subtypes
sentence Text(150); -- of types from the same family
BEGIN
sentence := verb; -- can be assigned, ifnot too long.
END;
Converting PL/SQL Datatypes
Sometimes it is necessary to convert a value from one datatypeto another. For example, to
use a DATE value in a report, you must convert it to a character string. PL/SQL supports
both explicit and implicit (automatic) datatypeconversion. To ensure your program does
exactly what you expect, use explicit conversions wherever possible.
Explicit Conversion
To convert values from one datatypeto another, you use built-in functions. For example,
to convert a CHAR value to a DATE or NUMBER value, you use the function TO_DATE or
TO_NUMBER, respectively. Conversely, to convert a DATE or NUMBER value to a CHAR
value, you use the function TO_CHAR.
Using explicit conversions, particularly when passing parameters to subprograms, can
avoid unexpected errors or wrong results. For example, the TO_CHAR function lets you
specify theformat for a DATE value, rather than relying on language settings in the
database. Including an arithmetic expression among strings being concatenated with the ||
operator can producean error unless you put parentheses or a call to TO_CHAR around the
entire arithmetic expression.
Implicit Conversion
When it makes sense, PL/SQL can convert thedatatypeof a value implicitly. This lets you
use literals, variables, and parameters of one typewhereanother typeis expected. For
example, you can pass a numeric literal to a subprogram that expects a string value, and
the subprogram receives the string representation of the number.
In the following example, the CHAR variables start_time and finish_time hold string values
representing the number of seconds past midnight. The difference between thosevalues
must be assigned to the NUMBER variable elapsed_time. PL/SQL converts the CHAR values to
NUMBER values automatically.
DECLARE
start_time CHAR(5);
finish_time CHAR(5);
elapsed_time NUMBER(5);
BEGIN
Page 34 of 77
/* Get systemtime as seconds past midnight. */
SELECT TO_CHAR(SYSDATE,'SSSSS') INTO start_time FROM sys.dual;
-- do something
/* Get systemtime again. */
SELECT TO_CHAR(SYSDATE,'SSSSS') INTO finish_time FROM sys.dual;
/* Compute elapsed time in seconds. */
elapsed_time := finish_time - start_time;
INSERT INTO results VALUES (elapsed_time, ...);
END;
Before assigning a selected column value to a variable, PL/SQL will, if necessary, convert
the value from the datatypeof thesource column to thedatatypeof the variable. This
happens, for example, when you select a DATE column value into a VARCHAR2 variable.
Likewise, before assigning the value of a variable to a database column, PL/SQL will, if
necessary, convert thevalue from thedatatypeof the variable to thedatatypeof the target
column. If PL/SQL cannot determine which implicit conversion is needed, you get a
compilation error. In such cases, you must use a datatypeconversion function. Table 3-1
shows which implicit conversions PL/SQL can do.
Notes:
 The labels PLS_INT and BIN_INT represent thetypes PLS_INTEGER and
BINARY_INTEGER in thetable. You cannot use them as abbreviations in code.
 The table lists only types that havedifferent representations. Types that havethe
same representation, such as CLOB and NCLOB, CHAR and NCHAR, and VARCHAR
and NVARCHAR2, can be substituted for each other.
 You can implicitly convert between CLOB and NCLOB, but be careful because this
can be an expensive operation. To make clear that this conversion is intended,
you can use theconversion functions TO_CLOB and TO_NCLOB.
 TIMESTAMP, TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE,
INTERVAL DAY TO SECOND, and INTERVAL YEAR TO MONTH can all be converted
using the same rules as the DATE type. However, because of their different
internal representations, thesetypes cannot always be converted to each other.
Table 3-1 ImplicitConversions
BIN_
INT
BL
OB
CH
AR
CL
OB
DA
TE
LO
NG
NUM
BER
PLS_
INT
R
A
W
URO
WID
VARC
HAR2
BIN_I
NT
X X X X X
BLOB X
CHAR X X X X X X X X X
CLOB X X
DATE X X X
LONG X X X
NUMB
ER
X X X X X
PLS_I
NT
X X X X X
RAW X X X X
UROW
ID
X X
VARC
HAR2
X X X X X X X X X
It is your responsibility to ensure that values are convertible. For instance, PL/SQL can
convert the CHAR value '02-JUN-92' to a DATE value but cannot convert the CHAR value
'YESTERDAY' to a DATE value. Similarly, PL/SQL cannot convert a VARCHAR2 value
containing alphabetic characters to a NUMBER value.
Choosing Between Implicit and Explicit Conversion
Relying on implicit datatypeconversions is a poor programming practice because they can
be slower and theconversion rules might change in later softwarereleases. Implicit
conversions are context-sensitive and not always predictable. For best reliability and
maintainability, use datatypeconversion functions.
DATE Values
When you select a DATE column value into a CHAR or VARCHAR2 variable, PL/SQL must
convert the internal binary value to a character value. PL/SQL calls thefunction TO_CHAR,
which returns a character string in thedefault date format. To get other information, such
as the time or Julian date, call TO_CHAR with a format mask.
A conversion is also necessary when you insert a CHAR or VARCHAR2 value into a DATE
column. PL/SQL calls the function TO_DATE, which expects the default date format. To
insert dates in other formats, call TO_DATE with a format mask.
RAW and LONG RAW Values
When you select a RAW or LONG RAW column value into a CHAR or VARCHAR2 variable,
PL/SQL must convert the internal binary value to a character value. In this case, PL/SQL
returns each binary byteof RAW or LONG RAW data as a pair of characters. Each character
represents thehexadecimal equivalent of a nibble (half a byte). For example, PL/SQL
returns the binary byte11111111 as the pair of characters 'FF'. The function RAWTOHEX
does the same conversion.
A conversion is also necessary when you insert a CHAR or VARCHAR2 value into a RAW or
LONG RAW column. Each pair of characters in the variable must represent the hexadecimal
equivalent of a binary byte. Otherwise, PL/SQL raises an exception.
4 - Using PL/SQL Control Structures
This chapter shows you how to structuretheflow of control through a PL/SQL program.
PL/SQL provides conditional tests, loops, and branches that let you producewell-
structured programs.
Overview of PL/SQL Control Structures
Procedural computer programs use thebasic control structures shown in Figure 4-1.
Figure 4-1 Control Structures
Page 35 of 77
Description of the illustration lnpls008.gif
The selection structuretests a condition, then executes one sequence of statements instead
of another, depending on whether thecondition is trueor false. A condition is any variable
or expression that returns a Boolean value (TRUE or FALSE). The iteration structure
executes a sequence of statements repeatedly as long as a condition holds true. The
sequence structuresimply executes a sequence of statements in the order in which they
occur.
Testing Conditions: IF and CASE Statements
The IF statement executes a sequence of statements depending on the value of a condition.
There are three forms of IF statements:IF-THEN, IF-THEN-ELSE, and IF-THEN-ELSIF.
The CASE statement is a compact way to evaluate a single condition and choose between
many alternative actions. It makes sense to use CASE when there are three or more
alternatives to choose from.
Using the IF-THEN Statement
The simplest form of IF statement associates a condition with a sequence of statements
enclosed by the keywords THEN and END IF (not ENDIF):
IF condition THEN
sequence_of_statements
END IF;
The sequence of statements is executed only if thecondition is true. If the condition is
false or null, the IF statement does nothing. In either case, control passes to the next
statement.
IF sales > quota THEN
compute_bonus(empid);
UPDATE payroll SET pay = pay + bonus WHERE empno = emp_id;
END IF;
You can place brief IF statements on a single line:
IF x > y THEN high := x; END IF;
Using the IF-THEN-ELSE Statement
The second form of IF statement adds thekeyword ELSE followed by an alternative
sequence of statements:
IF condition THEN
sequence_of_statements1
ELSE
sequence_of_statements2
END IF;
The statements in the ELSE clause are executed only if thecondition is false or null. The IF-
THEN-ELSE statement ensures that one or theother sequence of statements is executed. In
the following example, the first UPDATE statement is executed when thecondition is true,
and the second UPDATE statement is executed when the condition is false or null:
IF trans_type = 'CR' THEN
UPDATE accounts SET balance = balance + credit WHERE ...
ELSE
UPDATE accounts SET balance = balance - debit WHERE ...
END IF;
IF statements can be nested:
IF trans_type = 'CR' THEN
UPDATE accounts SET balance = balance + credit WHERE ...
ELSE
IF new_balance >= minimum_balance THEN
UPDATE accounts SET balance = balance - debit WHERE ...
ELSE
RAISE insufficient_funds;
END IF;
END IF;
Using the IF-THEN-ELSIF Statement
Sometimes you want to choose between several alternatives. You can use the keyword
ELSIF (not ELSEIF or ELSE IF) to introduce additional conditions:
IF condition1 THEN
sequence_of_statements1
ELSIF condition2 THEN
sequence_of_statements2
ELSE
sequence_of_statements3
END IF;
If the first condition is false or null, the ELSIF clause tests another condition. An IF
statement can have any number of ELSIF clauses; the final ELSE clause is optional.
Conditions are evaluated one by one from top to bottom. If any condition is true, its
associated sequence of statements is executed and control passes to the next statement. If
all conditions are false or null, the sequence in the ELSE clause is executed. Consider the
following example:
BEGIN
IF sales > 50000 THEN
bonus := 1500;
ELSIF sales > 35000 THEN
bonus := 500;
ELSE
bonus := 100;
END IF;
INSERT INTO payroll VALUES (emp_id, bonus, ...);
END;
If the value of sales is larger than 50000, thefirst and second conditions are true.
Nevertheless, bonus is assigned theproper value of 1500 because thesecond condition is
never tested. When the first condition is true, its associated statement is executed and
control passes to the INSERT statement.
Using the CASE Statement
Like the IF statement, the CASE statement selects one sequence of statements to execute.
However, to select thesequence, the CASE statement uses a selector rather than multiple
Boolean expressions. (Recall from Chapter 2 that a selector is an expression whose value
is used to select one of several alternatives.) To compare the IF and CASE statements,
consider thefollowing code that outputs descriptions of schoolgrades:
IF grade = 'A' THEN
Page 36 of 77
dbms_output.put_line('Excellent');
ELSIF grade = 'B' THEN
dbms_output.put_line('Very Good');
ELSIF grade = 'C' THEN
dbms_output.put_line('Good');
ELSIF grade = 'D' THEN
dbms_output. put_line('Fair');
ELSIF grade = 'F' THEN
dbms_output.put_line('Poor');
ELSE
dbms_output.put_line('No such grade');
END IF;
Notice the five Boolean expressions. In each instance, we test whether the same variable,
grade, is equal to one of five values: 'A', 'B', 'C', 'D', or 'F'. Let us rewrite thepreceding code
using the CASE statement, as follows:
CASE grade
WHEN 'A' THEN dbms_output.put_line('Excellent');
WHEN 'B' THEN dbms_output.put_line('Very Good');
WHEN 'C' THEN dbms_output.put_line('Good');
WHEN 'D' THEN dbms_output.put_line('Fair');
WHEN 'F' THEN dbms_output.put_line('Poor');
ELSE dbms_output.put_line('No such grade');
END CASE;
The CASE statement is more readable and more efficient. When possible, rewrite lengthy
IF-THEN-ELSIF statements as CASE statements.
The CASE statement begins with the keyword CASE. Thekeyword is followed by a selector,
which is the variable grade in thelast example. Theselector expression can be arbitrarily
complex. For example, it can contain function calls. Usually, however, it consists of a
single variable. The selector expression is evaluated only once. Thevalue it yields can
have any PL/SQL datatypeother than BLOB, BFILE, an object type, aPL/SQL record, an
index-by-table, a varray, or a nested table.
The selector is followed by one or more WHEN clauses, which are checked sequentially.
The value of theselector determines which clause is executed. If the value of the selector
equals thevalue of a WHEN-clause expression, that WHEN clause is executed. For instance,
in the last example, if grade equals 'C', theprogram outputs 'Good'. Execution never falls
through; if any WHEN clause is executed, control passes to the next statement.
The ELSE clause works similarly to the ELSE clause in an IF statement. In the last example,
if the grade is not one of the choices covered by a WHEN clause, the ELSE clause is selected,
and the phrase'No such grade' is output. TheELSE clause is optional. However, if you omit the
ELSE clause, PL/SQL adds the following implicit ELSE clause:
ELSE RAISE CASE_NOT_FOUND;
There is always a default action, even when you omit the ELSE clause. If the CASE
statement does not match any of the WHEN clauses and you omit the ELSE clause, PL/SQL
raises the predefined exception CASE_NOT_FOUND.
The keywords END CASE terminate theCASE statement. These two keywords must be
separated by a space. The CASE statement has the following form:
[<<label_name>>]
CASE selector
WHEN expression1 THEN sequence_of_statements1;
WHEN expression2 THEN sequence_of_statements2;
...
WHEN expressionN THEN sequence_of_statementsN;
[ELSE sequence_of_statementsN+1;]
END CASE [label_name];
Like PL/SQL blocks, CASE statements can be labeled. Thelabel, an undeclared identifier
enclosed by double angle brackets, must appear at thebeginning of the CASE statement.
Optionally, thelabel name can also appear at the end of the CASE statement.
Exceptions raised during the execution of a CASE statement are handled in the usual way.
That is, normal execution stops and control transfers to the exception-handling part of your
PL/SQL block or subprogram.
An alternative to the CASEstatement is the CASE expression, where each WHEN clause is an
expression.
SearchedCASEStatement
PL/SQL also provides a searched CASE statement, which has theform:
[<<label_name>>]
CASE
WHEN search_condition1 THEN sequence_of_statements1;
WHEN search_condition2 THEN sequence_of_statements2;
...
WHEN search_conditionN THEN sequence_of_statementsN;
[ELSE sequence_of_statementsN+1;]
END CASE [label_name];
The searched CASE statement has no selector. Also, its WHEN clauses contain search
conditions that yield a Boolean value, not expressions that can yield a value of any type.
An example follows:
CASE
WHEN grade = 'A' THEN dbms_output.put_line('Excellent');
WHEN grade = 'B' THEN dbms_output.put_line('Very Good');
WHEN grade = 'C' THEN dbms_output.put_line('Good');
WHEN grade = 'D' THEN dbms_output.put_line('Fair');
WHEN grade = 'F' THEN dbms_output.put_line('Poor');
ELSE dbms_output.put_line('No such grade');
END CASE;
The search conditions are evaluated sequentially. The Boolean value of each search
condition determines which WHEN clause is executed. If a search condition yields TRUE, its
WHEN clause is executed. If any WHEN clause is executed, control passes to thenext
statement, so subsequent search conditions are not evaluated.
If none of the search conditions yields TRUE, theELSE clause is executed. The ELSE clause
is optional. However, if you omit the ELSE clause, PL/SQL adds thefollowing implicit
ELSE clause:
ELSE RAISE CASE_NOT_FOUND;
Exceptions raised during the execution of a searched CASE statement are handled in the
usual way. That is, normal execution stops and control transfers to theexception-handling
part of your PL/SQL block or subprogram.
Controlling Loop Iterations: LOOP and EXIT Statements
LOOP statements execute a sequence of statements multiple times. There are three forms of
LOOP statements:LOOP, WHILE-LOOP, and FOR-LOOP.
Using the LOOP Statement
The simplest form of LOOP statement is the basic loop, which encloses a sequence of
statements between thekeywords LOOP and END LOOP, as follows:
LOOP
sequence_of_statements
END LOOP;
Page 37 of 77
With each iteration of the loop, the sequence of statements is executed, then control
resumes at the top of the loop. You use an EXIT statement to stop loopingand prevent an
infinite loop. You can place one or more EXIT statements anywhereinside a loop, but not
outside a loop. There are two forms of EXIT statements:EXIT and EXIT-WHEN.
Using the EXIT Statement
The EXIT statement forces a loop to complete unconditionally. When an EXIT statement is
encountered, theloop completes immediately and control passes to thenext statement:
LOOP
IF credit_rating < 3 THEN
EXIT; -- exit loop immediately
END IF;
END LOOP;
-- control resumes here
Remember, theEXIT statement must be placed inside a loop. To complete a PL/SQL block
before its normal end is reached, you can use the RETURN statement.
Using the EXIT-WHEN Statement
The EXIT-WHEN statement lets a loop complete conditionally. When the EXIT statement is
encountered, thecondition in the WHEN clause is evaluated. If thecondition is true, the
loop completes and control passes to the next statement after theloop:
LOOP
FETCH c1 INTO ...
EXIT WHEN c1%NOTFOUND; -- exit loop ifcondition is true
...
END LOOP;
CLOSE c1;
Until thecondition is true, the loop cannot complete. A statement inside theloop must
change the value of thecondition. In theprevious example, if the FETCH statement returns
a row, the condition is false. When the FETCH statement fails to return a row, the condition
is true, theloop completes, and control passes to the CLOSE statement.
The EXIT-WHEN statement replaces a simple IF statement. For example, compare the
following statements:
IF count > 100 THEN | EXIT WHEN count > 100;
EXIT; |
END IF; |
These statements are logically equivalent, but the EXIT-WHEN statement is easier to read
and understand.
Labeling a PL/SQL Loop
Like PL/SQL blocks, loops can be labeled. The label, an undeclared identifier enclosed by
double angle brackets, must appear at thebeginning of the LOOP statement, as follows:
<<label_name>>
LOOP
sequence_of_statements
END LOOP;
Optionally, thelabel name can also appear at the end of the LOOP statement, as the
following example shows:
<<my_loop>>
LOOP
...
END LOOP my_loop;
When you nest labeled loops, use ending label names to improve readability.
With either form of EXIT statement, you can complete not only the current loop, but any
enclosing loop. Simply label the enclosing loop that you want to complete. Then, use the
label in an EXIT statement, as follows:
<<outer>>
LOOP
...
LOOP
...
EXIT outer WHEN ... -- exit both loops
END LOOP;
...
END LOOP outer;
Every enclosing loop up to and including the labeled loop is exited.
Using the WHILE-LOOP Statement
The WHILE-LOOP statement executes thestatements in the loop body as long as a condition
is true:
WHILE condition LOOP
sequence_of_statements
END LOOP;
Before each iteration of theloop, thecondition is evaluated. If it is true, the sequence of
statements is executed, then control resumes at the top of the loop. If it is false or null, the
loop is skipped and control passes to the next statement:
WHILE total <= 25000 LOOP
SELECT sal INTO salary FROM emp WHERE ...
total := total + salary;
END LOOP;
The number of iterations depends on the condition and is unknown until the loop
completes. Thecondition is tested at the top of the loop, so the sequence might execute
zero times. In thelast example, if the initial value of total is larger than 25000, the condition
is false and the loop is skipped.
Some languages have a LOOP UNTIL or REPEAT UNTIL structure, which tests thecondition at
the bottomof theloop instead of at the top, so that the sequence of statements is executed
at least once. The equivalent in PL/SQL would be:
LOOP
sequence_of_statements
EXIT WHEN boolean_expression;
END LOOP;
To ensure that a WHILE loop executes at least once, use an initialized Boolean variable in
the condition, as follows:
done := FALSE;
WHILE NOT done LOOP
sequence_of_statements
done := boolean_expression;
END LOOP;
Page 38 of 77
A statement inside the loop must assign a new value to the Boolean variable to avoid an
infinite loop.
Using the FOR-LOOP Statement
Simple FOR loops iterate over a specified range of integers. The number of iterations is
known before the loop is entered. A double dot (..) serves as the range operator:
FOR counter IN [REVERSE] lower_bound..higher_bound LOOP
sequence_of_statements
END LOOP;
The range is evaluated when the FOR loop is first entered and is never re-evaluated.
As the next example shows, the sequence of statements is executed once for each integer
in the range. After each iteration, the loop counter is incremented.
FOR i IN 1..3 LOOP -- assign the values 1,2,3 to i
sequence_of_statements -- executes three times
END LOOP;
If the lower bound equals thehigher bound, the loop body is executed once:
FOR i IN 3..3 LOOP -- assign the value 3 to i
sequence_of_statements -- executes one time
END LOOP;
By default, iteration proceeds upward from the lower bound to thehigher bound. If you
use the keyword REVERSE, iteration proceeds downward from the higher bound to the
lower bound. After each iteration, theloop counter is decremented. You still writethe
range bounds in ascending (not descending) order.
FOR i IN REVERSE 1..3 LOOP -- assign the values 3,2,1 to i
sequence_of_statements -- executes three times
END LOOP;
Inside a FOR loop, the counter can be read but cannot be changed:
FOR ctr IN 1..10 LOOP
IF NOT finished THEN
INSERT INTO ... VALUES (ctr, ...); -- OK
factor := ctr * 2; -- OK
ELSE
ctr := 10; -- not allowed
END IF;
END LOOP;
Tip: A useful variation of the FOR loop uses a SQL query instead of a range of integers.
This technique lets you run a query and process all therows of theresult set with
straightforward syntax
How PL/SQL Loops Iterate
The bounds of a loop range can be literals, variables, or expressions but must evaluate to
numbers. Otherwise, PL/SQL raises the predefined exception VALUE_ERROR. The lower
bound need not be 1, but theloop counter increment or decrement must be 1.
j IN -5..5
k IN REVERSE first..last
step IN 0..TRUNC(high/low) * 2
Internally, PL/SQL assigns the values of thebounds to temporary PLS_INTEGER variables,
and, if necessary, rounds the values to thenearest integer. Themagnitude range of a
PLS_INTEGER is -2**31 .. 2**31. If a bound evaluates to a number outside that range, you
get a numeric overflow error when PL/SQL attempts theassignment.
Some languages provide a STEP clause, which lets you specify a different increment (5
instead of 1 for example). PL/SQL has no such structure, but you can easily build one.
Inside the FOR loop, simply multiply each reference to the loop counter by the new
increment. In the following example, you assign today's date to elements 5, 10, and 15 of
an index-by table:
DECLARE
TYPE DateList IS TABLE OF DATE INDEX BY BINARY_INTEGER;
dates DateList;
k CONSTANT INTEGER := 5; -- set new increment
BEGIN
FOR j IN 1..3 LOOP
dates(j*k) := SYSDATE; -- multiply loop counter by increment
END LOOP;
...
END;
DynamicRanges for Loop Bounds
PL/SQL lets you specify theloop range at run time by using variables for bounds:
SELECT COUNT(empno) INTO emp_count FROM emp;
FOR i IN 1..emp_count LOOP
...
END LOOP;
If the lower bound of a loop range evaluates to a larger integer than theupper bound, the
loop body is not executed and control passes to thenext statement:
-- limit becomes 1
FOR i IN 2..limit LOOP
sequence_of_statements -- executes zero times
END LOOP;
-- control passes here
Scope of the Loop CounterVariable
The loop counter is defined only within the loop. You cannot reference that variable name
outside theloop. After the loop exits, theloop counter is undefined:
FOR ctr IN 1..10 LOOP
...
END LOOP;
sum := ctr - 1; -- not allowed
You do not need to declare theloop counter because it is implicitly declared as a local
variable of typeINTEGER. It is safest not to use thename of an existing variable, because
the local declaration hides any global declaration:
DECLARE
ctr INTEGER := 3;
BEGIN
...
FOR ctr IN 1..25 LOOP
...
IF ctr > 10 THEN ... -- Refers to loop counter
END LOOP;
-- After the loop, ctr refers to the original variable with value 3.
END;
Page 39 of 77
To reference the global variable in this example, you must use a label and dot notation, as
follows:
<<main>>
DECLARE
ctr INTEGER;
...
BEGIN
...
FOR ctr IN 1..25 LOOP
...
IF main.ctr > 10 THEN -- refers to global variable
...
END IF;
END LOOP;
END main;
The same scope rules apply to nested FOR loops. Consider the example below. Both loop
counters have the same name. To reference theouter loop counter from theinner loop, you
use a label and dot notation:
<<outer>>
FOR step IN 1..25 LOOP
FOR step IN 1..10 LOOP
...
IF outer.step > 15 THEN ...
END LOOP;
END LOOP outer;
Using the EXIT Statement in a FORLoop
The EXIT statement lets a FOR loop complete early. For example, thefollowing loop
normally executes ten times, but as soon as the FETCH statement fails to return a row, the
loop completes no matter how many times it has executed:
FOR j IN 1..10 LOOP
FETCH c1 INTO emp_rec;
EXIT WHEN c1%NOTFOUND;
...
END LOOP;
Supposeyou must exit early from a nested FOR loop. To complete not only the current
loop, but also any enclosing loop, label the enclosing loop and use the label in an EXIT
statement:
<<outer>>
FOR i IN 1..5 LOOP
...
FOR j IN 1..10 LOOP
FETCH c1 INTO emp_rec;
EXIT outer WHEN c1%NOTFOUND; -- exit both FOR loops
...
END LOOP;
END LOOP outer;
-- control passes here
Sequential Control: GOTO and NULL Statements
Unlike theIF and LOOP statements, the GOTO and NULL statements are not crucial to
PL/SQL programming. The GOTO statement is seldom needed. Occasionally, it can
simplify logic enough to warrant its use. The NULL statement can improve readability by
making the meaning and action of conditional statements clear.
Overuse of GOTO statements can result in code that is hard to understand and maintain. Use
GOTO statements sparingly. For example, to branch from a deeply nested structureto an
error-handling routine, raise an exception rather than use a GOTO statement.
Using the GOTO Statement
The GOTO statement branches to a label unconditionally. Thelabel must be unique within
its scope and must precede an executable statement or a PL/SQL block. When executed,
the GOTO statement transfers control to the labeled statement or block. In the following
example, you go to an executable statement farther down in a sequence of statements:
BEGIN
...
GOTO insert_row;
...
<<insert_row>>
INSERT INTO emp VALUES ...
END;
In the next example, you go to a PL/SQL block farther up in a sequence of statements:
DECLARE
x NUMBER := 0;
BEGIN
<<increment_x>>
BEGIN
x := x + 1;
END;
IF x < 10 THEN
GOTO increment_x;
END IF;
END;
The label end_loop in the following example is not allowed because it does not precede an
executable statement:
DECLARE
done BOOLEAN;
BEGIN
FOR i IN 1..50 LOOP
IF done THEN
GOTO end_loop;
END IF;
<<end_loop>> -- not allowed
END LOOP; -- not an executable statement
END;
To correct theprevious example, add the NULL statement::
FOR i IN 1..50 LOOP
IF done THEN
GOTO end_loop;
END IF;
...
<<end_loop>>
NULL; -- an executable statement
END LOOP;
As the following example shows, a GOTO statement can branch to an enclosing block from
the current block:
Page 40 of 77
DECLARE
my_ename CHAR(10);
BEGIN
<<get_name>>
SELECT ename INTO my_ename FROM emp WHERE ...
BEGIN
GOTO get_name; -- branch to enclosing block
END;
END;
The GOTO statement branches to thefirst enclosing block in which the referenced label
appears.
Restrictions on the GOTO Statement
Some possible destinations of a GOTO statement are not allowed. Specifically, a GOTO
statement cannot branch into an IF statement, CASE statement, LOOP statement, or sub-
block. For example, the following GOTO statement is not allowed:
BEGIN
GOTO update_row; -- can't branch into IF statement
IF valid THEN
<<update_row>>
UPDATE emp SET ...
END IF;
END;
A GOTO statement cannot branch from one IF statement clause to another, or from one CASE
statement WHEN clause to another.
A GOTO statement cannot branch from an outer block into a sub-block (that is, an inner
BEGIN-END block).
A GOTO statement cannot branch out of a subprogram. To end a subprogram early, you can
use the RETURN statement or use GOTO to branch to a place right before the end of the
subprogram.
A GOTO statement cannot branch from an exception handler back into the current BEGIN-
END block. However, a GOTO statement can branch from an exception handler into an
enclosing block.
Using the NULL Statement
The NULL statement does nothing, and passes controlto thenext statement. (Some
languages refer to such an instruction as a no-op.)
You can use the NULL statement to indicate that you are aware of a possibility, but no
action is necessary. In the following example, the NULL statement shows that you have
chosen not to take any action for unnamed exceptions:
EXCEPTION
WHEN ZERO_DIVIDE THEN
ROLLBACK;
WHEN VALUE_ERROR THEN
INSERT INTO errors VALUES ...
COMMIT;
WHEN OTHERS THEN
NULL;
END;
The NULL statement is a handy way to create placeholders and stub procedures. In the
following example, the NULL statement lets you compile this procedure, then fill in thereal
body later:
PROCEDURE debit_account (acct_id INTEGER, amount REAL) IS
BEGIN
NULL;
END debit_account;
5 – Data Concurrency and Consistency
This chapter explains how Oracle maintains consistent data in a multiuser database
environment.
Introduction to Data Concurrency and Consistency in a
Multiuser Environment
In a single-user database, the user can modify data in thedatabase without concern for
other users modifying the same data at the same time. However, in a multiuser database,
the statements within multiple simultaneous transactions can updatethe same data.
Transactions executing at thesame time need to produce meaningful and consistent
results. Therefore, control of data concurrency and data consistency is vital in a multiuser
database.
 Data concurrency means that many users can access data at thesame time.
 Data consistency means that each user sees a consistent view of the data,
including visible changes made by the user's own transactions and transactions
of other users.
To describe consistent transaction behavior when transactions run at the same time,
database researchers have defined a transaction isolation model called serializability. The
serializable mode of transaction behavior tries to ensure that transactions run in such a way
that they appear to be executed one at a time, or serially, rather than concurrently.
While this degree of isolation between transactions is generally desirable, running many
applications in this mode can seriously compromise application throughput. Complete
isolation of concurrently running transactions could mean that one transaction cannot
perform an insert into a table being queried by another transaction. In short, real-world
considerations usually require a compromise between perfect transaction isolation and
performance.
Oracle offers two isolation levels, providing application developers with operational
modes that preserve consistency and provide high performance.
Overview of Locking Mechanisms
In general, multiuser databases use some form of data locking to solve the problems
associated with data concurrency, consistency, and integrity. Locks are mechanisms that
prevent destructive interaction between transactions accessing the same resource.
Resources include two general types of objects:
 User objects, such as tables and rows (structures and data)
 Systemobjects not visible to users, such as shared data structures in the memory
and data dictionary rows
Row-Level Locking
Both read committed and serializable transactions use row-level locking, and both will
wait if they try to change a row updated by an uncommitted concurrent transaction. The
second transaction that tries to updatea given row waits for the other transaction to
commit or undo and release its lock. If that other transaction rolls back, thewaiting
transaction, regardless of its isolation mode, can proceed to change thepreviously locked
row as if the other transaction had not existed.
Page 41 of 77
However, if the other blocking transaction commits and releases its locks, a read
committed transaction proceeds with its intended update. A serializable transaction,
however, fails with the error "Cannot serialize access", because the other transaction has
committed a change that was made since the serializable transaction began.
How Oracle Locks Data
Locks are mechanisms that prevent destructive interaction between transactions accessing
the same resource—either user objects such as tables and rows or systemobjects not
visible to users, such as shared data structures in memory and data dictionary rows.
In all cases, Oracle automatically obtains necessary locks when executing SQL statements,
so users need not be concerned with such details. Oracle automatically uses thelowest
applicable level of restrictiveness to providethe highest degree of data concurrency yet
also provide fail-safe data integrity. Oracle also allows theuser to lock data manually.
Transactions and Data Concurrency
Oracle provides data concurrency and integrity between transactions using its locking
mechanisms. Because the locking mechanisms of Oracle are tied closely to transaction
control, application designers need only define transactions properly, and Oracle
automatically manages locking.
Keep in mind that Oracle locking is fully automatic and requires no user action. Implicit
locking occurs for all SQL statements so that database users never need to lock any
resource explicitly. Oracle's default locking mechanisms lock data at the lowest level of
restrictiveness to guarantee data integrity while allowing the highest degree of data
concurrency.
Modes of Locking
Oracle uses two modes of locking in a multiuser database:
 Exclusive lock mode prevents theassociates resource from being shared. This
lock mode is obtained to modify data. Thefirst transaction to lock a resource
exclusively is theonly transaction that can alter the resource until theexclusive
lock is released.
 Share lock mode allows the associated resource to be shared, depending on the
operations involved. Multipleusers reading data can share the data, holding
share locks to prevent concurrent access by a writer (who needs an exclusive
lock). Several transactions can acquire share locks on thesame resource.
Lock Duration
All locks acquired by statements within a transaction are held for theduration of the
transaction, preventing destructive interference including dirty reads, lost updates, and
destructive DDLoperations from concurrent transactions. The changes made by theSQL
statements of one transaction become visible only to other transactions that start after the
first transaction is committed.
Oracle releases all locks acquired by the statements within a transaction when you either
commit or undo thetransaction. Oracle also releases locks acquired after a savepoint when
rolling back to the savepoint. However, only transactions not waiting for thepreviously
locked resources can acquire locks on thenow available resources. Waiting transactions
will continue to wait until after the original transaction commits or rolls back completely.
Deadlocks
A deadlock can occur when two or more users are waiting for data locked by each other.
Deadlocks prevent some transactions from continuing to work. Figure 13-3 is a
hypotheticalillustration of two transactions in a deadlock.
In Figure 13-3, no problem exists at time point A, as each transaction has a row lock on the
row it attempts to update. Each transaction proceeds without being terminated. However,
each tries next to updatetherow currently held by the other transaction. Therefore, a
deadlock results at time point B, because neither transaction can obtain the resource it
needs to proceed or terminate. It is a deadlock because no matter how long each
transaction waits, the conflicting locks are held.
Figure 13-3 Two Transactions in a Deadlock
Description of the illustration cncpt068.gif
Deadlock Detection
Oracle automatically detects deadlock situations and resolves them by rolling back one of
the statements involved in thedeadlock, thereby releasing one set of theconflicting row
locks. A corresponding message also is returned to the transaction that undergoes
statement-level rollback. Thestatement rolled back is the one belonging to the transaction
that detects the deadlock. Usually, thesignalled transaction should be rolled back
explicitly, but it can retry therolled-back statement after waiting.
Note:
In distributed transactions, local deadlocks are detected by analyzing a
"waits for" graph, and global deadlocks are detected by a time out. Once
detected, nondistributed and distributed deadlocks are handled by the
database and application in the same way.
Deadlocks most often occur when transactions explicitly override the default locking of
Oracle. Because Oracle itself does no lock escalation and does not use read locks for
queries, but does use row-level locking (rather than page-level locking), deadlocks occur
infrequently in Oracle.
Avoid Deadlocks
Multitabledeadlocks can usually be avoided if transactions accessing the same tables lock
thosetables in thesame order, either through implicit or explicit locks. For example, all
application developers might follow therule that when both a master and detail table are
updated, the master table is locked first and then the detail table. If such rules are properly
designed and then followed in all applications, deadlocks are very unlikely to occur.
When you know you will require a sequence of locks for one transaction, consider
acquiring the most exclusive (least compatible) lock first.
Page 42 of 77
Types of Locks
Oracle automatically uses different types of locks to control concurrent access to data and
to prevent destructive interaction between users. Oracle automatically locks a resource on
behalf of a transaction to prevent other transactions from doing something also requiring
exclusive access to the same resource. The lock is released automatically when some event
occurs so that the transaction no longer requires theresource.
Throughout its operation, Oracle automatically acquires different types of locks at
different levels of restrictiveness depending on the resource being locked and the operation
being performed.
Oracle locks fall into one of three general categories.
Lock Description
DML locks (data
locks)
DMLlocks protect data. For example, table locks lock entire
tables, row locks lock selected rows.
DDL locks
(dictionary locks)
DDL locks protect thestructureof schema objects—for example,
the definitions of tables and views.
Internal locks and
latches
Internal locks and latches protect internal database structures such
as datafiles. Internal locks and latches are entirely automatic.
The following sections discuss DMLlocks, DDLlocks, and internal locks.
DML Locks
The purposeof a DML(data) lock is to guarantee the integrity of data being accessed
concurrently by multiple users. DMLlocks prevent destructive interference of
simultaneous conflicting DMLor DDL operations. For example, Oracle DMLlocks
guarantee that a specific row in a table can be updated by only one transaction at a time
and that a table cannot be dropped if an uncommitted transaction contains an insert into the
table.
DMLoperations can acquire data locks at two different levels: for specific rows and for
entire tables.
Note:
The acronym in parentheses after each typeof lock or lock mode is the
abbreviation used in the Locks Monitor of EnterpriseManager.
EnterpriseManager might display TM for any table lock, rather than
indicate the mode of table lock (such as RS or SRX).
Row Locks (TX)
The only DMLlocks Oracle acquires automatically are row-level locks. There is no limit
to the number of row locks held by a statement or transaction, and Oracle does not escalate
locks from therow level to a coarser granularity. Row locking provides the finest grain
locking possibleand so provides thebest possibleconcurrency and throughput.
The combination of multiversion concurrency control and row-level locking means that
users contend for data only when accessing the same rows, specifically:
 Readers of data do not wait for writers of the same data rows.
 Writers of data do not wait for readers of thesame data rows unless SELECT ...
FOR UPDATE is used, which specifically requests a lock for the reader.
 Writers only wait for other writers if they attempt to updatethesame rows at the
same time.
Note:
Readers of data may have to wait for writers of the same data blocks in
some very special cases of pending distributed transactions.
A transaction acquires an exclusive DMLlock for each individual row modified by one of
the following statements: INSERT, UPDATE, DELETE, and SELECT with the FOR UPDATE
clause.
A modified row is always locked exclusively so that other users cannot modify therow
until the transaction holding the lock is committed or rolled back. However, if the
transaction dies due to instance failure, block-level recovery makes a row available before
the entire transaction is recovered. Row locks are always acquired automatically by Oracle
as a result of the statements listed previously.
If a transaction obtains a row lock for a row, the transaction also acquires a table lock for
the corresponding table. Thetable lock prevents conflicting DDL operations that would
override data changes in a current transaction.
Table Locks (TM)
A transaction acquires a table lock when a table is modified in the following DML
statements:INSERT, UPDATE, DELETE, SELECT with the FOR UPDATE clause, and LOCK TABLE.
These DMLoperations require table locks for two purposes:to reserve DMLaccess to the
table on behalf of a transaction and to prevent DDL operations that would conflict with the
transaction. Any table lock prevents the acquisition of an exclusive DDL lock on the same
table and thereby prevents DDLoperations that require such locks. For example, a table
cannot be altered or dropped if an uncommitted transaction holds a table lock for it.
A table lock can be held in any of several modes: row share (RS), row exclusive (RX),
share (S), share row exclusive (SRX), and exclusive (X). The restrictiveness of a table
lock's mode determines the modes in which other table locks on the same table can be
obtained and held.
Table 13-3 shows the table lock modes that statements acquire and operations that those
locks permit and prohibit.
Table 13-3 Summary of Table Locks
SQLStatement Mode of Table
Lock
Lock Modes Permitted?
RS RX S SRX X
SELECT...FROM table... none Y Y Y Y Y
INSERT INTO table ... RX Y Y N N N
UPDATE table ... RX Y* Y* N N N
DELETE FROM table ... RX Y* Y* N N N
SELECT ... FROM table FOR UPDATE OF ... RS Y* Y* Y* Y* N
LOCK TABLE table IN ROW SHARE MODE RS Y Y Y Y N
LOCK TABLE table IN ROW EXCLUSIVE
MODE
RX Y Y N N N
LOCK TABLE table IN SHARE MODE S Y N Y N N
LOCK TABLE table IN SHARE ROW
EXCLUSIVE MODE
SRX Y N N N N
LOCK TABLE table IN EXCLUSIVE MODE X N N N N N
Page 43 of 77
RS: row share
RX: row exclusive
S: share
SRX: share row exclusive
X: exclusive
*Yes, if no conflicting row locks are held by another transaction. Otherwise, waits occur.
The following sections explain each mode of table lock, from least restrictive to most
restrictive. They also describe the actions that cause the transaction to acquire a table lock
in that mode and which actions are permitted and prohibited in other transactions by a lock
in that mode.
Row Share Table Locks (RS)
A row share table lock (also sometimes called a subshare table lock, SS) indicates that
the transaction holding the lock on the table has locked rows in the table and intends to
updatethem. A row share table lock is automatically acquired for a table when one of the
following SQL statements is run:
SELECT ... FROM table ... FOR UPDATE OF ... ;
LOCK TABLE table IN ROW SHARE MODE;
A row share table lock is theleast restrictive mode of table lock, offering thehighest
degree of concurrency for a table.
Permitted Operations: A row share table lock held by a transaction allows other
transactions to query, insert, update, delete, or lock rows concurrently in the same table.
Therefore, other transactions can obtain simultaneous row share, row exclusive, share, and
share row exclusive table locks for the same table.
Prohibited Operations: A row share table lock held by a transaction prevents other
transactions from exclusive write access to thesame table using only thefollowing
statement:
LOCK TABLE table IN EXCLUSIVE MODE;
Row Exclusive Table Locks (RX)
A row exclusive table lock (also called a subexclusive table lock, SX) generally indicates
that the transaction holding the lock has made one or more updates to rows in the table. A
row exclusive table lock is acquired automatically for a table modified by the following
types of statements:
INSERT INTO table ... ;
UPDATE table ... ;
DELETE FROM table ... ;
LOCK TABLE table IN ROW EXCLUSIVE MODE;
A row exclusive table lock is slightly more restrictive than a row share table lock.
Permitted Operations: A row exclusive table lock held by a transaction allows other
transactions to query, insert, update, delete, or lock rows concurrently in the same table.
Therefore, row exclusive table locks allow multiple transactions to obtain simultaneous
row exclusive and row share table locks for the same table.
Prohibited Operations: A row exclusive table lock held by a transaction prevents other
transactions from manually locking the table for exclusive reading or writing. Therefore,
other transactions cannot concurrently lock the table using the following statements:
LOCK TABLE table IN SHARE MODE;
LOCK TABLE table IN SHARE EXCLUSIVE MODE;
LOCK TABLE table IN EXCLUSIVE MODE;
Share Table Locks (S)
A share table lock is acquired automatically for the table specified in the following
statement:
LOCK TABLE table IN SHARE MODE;
Permitted Operations: A share table lock held by a transaction allows other transactions
only to query the table, to lock specific rows with SELECT ... FOR UPDATE, or to run LOCK
TABLE ... IN SHARE MODE statements successfully. No updates are allowed by other
transactions. Multipletransactions can hold share table locks for the same table
concurrently. In this case, no transaction can updatethe table (even if a transaction holds
row locks as the result of a SELECT statement with the FOR UPDATE clause). Therefore, a
transaction that has a share table lock can updatethe table only if no other transactions also
have a share table lock on the same table.
Prohibited Operations: A share table lock held by a transaction prevents other transactions
from modifying the same table and from executing the following statements:
LOCK TABLE table IN SHARE ROW EXCLUSIVE MODE;
LOCK TABLE table IN EXCLUSIVE MODE;
LOCK TABLE table IN ROW EXCLUSIVE MODE;
Share Row Exclusive Table Locks (SRX)
A share row exclusive table lock (also sometimes called a share-subexclusive table lock,
SSX) is more restrictive than a share table lock. A share row exclusive table lock is
acquired for a table as follows:
LOCK TABLE table IN SHARE ROW EXCLUSIVE MODE;
Permitted Operations: Only one transaction at a time can acquire a share row exclusive
table lock on a given table. A share row exclusive table lock held by a transaction allows
other transactions to query or lock specific rows using SELECT with theFOR UPDATE clause,
but not to updatethe table.
Prohibited Operations: A share row exclusive table lock held by a transaction prevents
other transactions from obtaining row exclusive table locks and modifying thesame table.
A share row exclusive table lock also prohibits other transactions from obtaining share,
share row exclusive, and exclusive table locks, which prevents other transactions from
executing thefollowing statements:
LOCK TABLE table IN SHARE MODE;
LOCK TABLE table IN SHARE ROW EXCLUSIVE MODE;
LOCK TABLE table IN ROW EXCLUSIVE MODE;
LOCK TABLE table IN EXCLUSIVE MODE;
Exclusive Table Locks (X)
An exclusive table lock is the most restrictive mode of table lock, allowing thetransaction
that holds the lock exclusive writeaccess to the table. An exclusive table lock is acquired
for a table as follows:
LOCK TABLE table IN EXCLUSIVE MODE;
Permitted Operations: Only one transaction can obtain an exclusive table lock for a table.
An exclusive table lock permits other transactions only to query the table.
Prohibited Operations: An exclusive table lock held by a transaction prohibits other
transactions from performing any typeof DMLstatement or placing any typeof lock on
the table.
DML Locks Automatically Acquired for DML Statements
The previous sections explained the different types of data locks, the modes in which they
can be held, when they can be obtained, when they are obtained, and what they prohibit.
The following sections summarize how Oracle automatically locks data on behalf of
different DMLoperations.
Table 13-4 summarizes theinformation in the following sections.
Table 13-4 Locks Obtained By DML Statements
DML Statement Row Locks? Mode of Table Lock
Page 44 of 77
SELECT ... FROM table
INSERT INTO table ... X RX
UPDATE table ... X RX
DELETE FROM table ... X RX
SELECT ... FROM table ... FOR UPDATE OF ... X RS
LOCK TABLE table IN ...
ROW SHARE MODE RS
ROW EXCLUSIVE MODE RX
SHARE MODE S
SHARE EXCLUSIVE MODE SRX
EXCLUSIVE MODE X
X: exclusive
RX: row exclusive
RS: row share
S: share
SRX: share row exclusive
Default Locking for Queries
Queries are the SQL statements least likely to interfere with other SQL statements because
they only read data. INSERT, UPDATE, and DELETE statements can have implicit queries as
part of thestatement. Queries include the following kinds of statements:
SELECT
INSERT ... SELECT ... ;
UPDATE ... ;
DELETE ... ;
They do not include thefollowing statement:
SELECT ... FOR UPDATE OF ... ;
The following characteristics are trueof all queries that do not use the FOR UPDATE clause:
 A query acquires no data locks. Therefore, other transactions can query and
updatea table being queried, including thespecific rows being queried. Because
queries lacking FOR UPDATE clauses do not acquire any data locks to block other
operations, such queries are often referred to in Oracle as nonblocking queries.
 A query does not have to wait for any data locks to be released; it can always
proceed. (Queries may have to wait for data locks in some very specific cases of
pending distributed transactions.)
Default Locking for INSERT, UPDATE, DELETE, and SELECT ... FOR UPDATE
The locking characteristics of INSERT, UPDATE, DELETE, and SELECT ... FOR UPDATE
statements are as follows:
 The transaction that contains a DMLstatement acquires exclusive row locks on
the rows modified by the statement. Other transactions cannot updateor delete
the locked rows until the locking transaction either commits or rolls back.
 The transaction that contains a DMLstatement does not need to acquire row
locks on any rows selected by a subquery or an implicit query, such as a query in
a WHERE clause. A subquery or implicit query in a DMLstatement is guaranteed
to be consistent as of thestart of the query and does not see the effects of the
DMLstatement it is part of.
 A query in a transaction can see the changes made by previous DMLstatements
in the same transaction, but cannot see the changes of other transactions begun
after its own transaction.
 In addition to thenecessary exclusive row locks, a transaction that contains a
DMLstatement acquires at least a row exclusive table lock on thetable that
contains the affected rows. If thecontaining transaction already holds a share,
share row exclusive, or exclusive table lock for that table, the row exclusive
table lock is not acquired. If thecontaining transaction already holds a row share
table lock, Oracle automatically converts this lock to a row exclusive table lock.
Explicit (Manual) Data Locking
Oracle always performs locking automatically to ensure data concurrency, data integrity,
and statement-level read consistency. However, you can override the Oracle default
locking mechanisms. Overriding the default locking is useful in situations such as these:
 Applications require transaction-level read consistency or repeatable reads. In
other words, queries in them must produce consistent data for the duration of the
transaction, not reflecting changes by other transactions. You can achieve
transaction-level read consistency by using explicit locking, read-only
transactions, serializable transactions, or by overriding default locking.
 Applications require that a transaction have exclusive access to a resource so that
the transaction does not have to wait for other transactions to complete.
Oracle's automatic locking can be overridden at the transaction level or thesession level.
At the transaction level, transactions that include the following SQL statements override
Oracle's default locking:
 The SET TRANSACTION ISOLATION LEVEL statement
 The LOCK TABLE statement (which locks either a table or, when used with views,
the underlying base tables)
 The SELECT ... FOR UPDATE statement
Locks acquired by these statements are released after the transaction commits or rolls
back.
At the session level, a session can set therequired transaction isolation level with the
ALTER SESSION statement.
Note:
If Oracle's default locking is overridden at any level, thedatabase
administrator or application developer should ensure that the overriding
locking procedures operatecorrectly. Thelocking procedures must
satisfy thefollowing criteria: data integrity is guaranteed, data
concurrency is acceptable, and deadlocks are not possible or are
appropriately handled.
6 - Performing SQL Operations from
PL/SQL
Page 45 of 77
Overview of Implicit Cursor Attributes
Implicit cursor attributes return information about theexecution of an INSERT, UPDATE,
DELETE, or SELECT INTO statement. Thevalues of the cursor attributes always refer to the
most recently executed SQL statement. Before Oracle opens the SQL cursor, the implicit
cursor attributes yield NULL.
Note: The SQL cursor has another attribute, %BULK_ROWCOUNT, designed for use with the
FORALL statement.
%FOUND Attribute: Has a DML Statement ChangedRows?
Until a SQL data manipulation statement is executed, %FOUND yields NULL. Thereafter,
%FOUND yields TRUE if an INSERT, UPDATE, or DELETE statement affected one or more rows,
or a SELECT INTO statement returned one or more rows. Otherwise, %FOUND yields FALSE.
In the following example, you use %FOUND to insert a row if a delete succeeds:
DELETE FROM emp WHERE empno = my_empno;
IF SQL%FOUND THEN -- delete succeeded
INSERT INTO new_emp VALUES (my_empno, my_ename, ...);
%ISOPEN Attribute: Always FALSE for Implicit Cursors
Oracle closes the SQL cursor automatically after executing its associated SQL statement.
As a result, %ISOPEN always yields FALSE.
%NOTFOUND Attribute: Has a DML Statement Failedto Change Rows?
%NOTFOUND is the logical oppositeof %FOUND. %NOTFOUND yields TRUE if an INSERT,
UPDATE, or DELETE statement affected no rows, or a SELECT INTO statement returned no
rows. Otherwise, %NOTFOUND yields FALSE.
%ROWCOUNT Attribute: How Many Rows Affected So Far?
%ROWCOUNT yields the number of rows affected by an INSERT, UPDATE, or DELETE
statement, or returned by a SELECT INTO statement. %ROWCOUNT yields 0 if an INSERT,
UPDATE, or DELETE statement affected no rows, or a SELECT INTO statement returned no
rows. In the following example, you use %ROWCOUNT to take action if more than ten rows
have been deleted:
DELETE FROM emp WHERE ...
IF SQL%ROWCOUNT > 10 THEN -- more than 10 rows were deleted
...
END IF;
If a SELECT INTO statement returns more than one row, PL/SQL raises the predefined
exception TOO_MANY_ROWS and %ROWCOUNT yields 1, not the actual number of rows that
satisfy thequery.
Guidelines forUsing Implicit Cursor Attributes
The values of the cursor attributes always refer to the most recently executed SQL
statement, wherever that statement is. It might be in a different scope (for example, in a
sub-block). To save an attributevalue for later use, assign it to a Boolean variable
immediately. Doing other operations, such as procedure calls, might change thevalue of
%NOTFOUND before you can test it.
The %NOTFOUND attributeis not useful in combination with the SELECT INTO statement:
If a SELECT INTO statement fails to return a row, PL/SQL raises the predefined exception
NO_DATA_FOUND immediately, interrupting theflow of control before you can check
%NOTFOUND.
A SELECT INTO statement that calls a SQL aggregate function always returns a value or a
null. After such a statement, the %NOTFOUND attributeis always FALSE, so checking it is
unnecessary.
Using PL/SQL Records in SQL INSERT and UPDATE
Statements
Instead of listing each field of a PL/SQL record in INSERT and UPDATE statements, you can
use PL/SQL records directly. The most convenient technique is to declare the record using
a %ROWTYPE attribute, so that it has exactly the same fields as the SQL table:
DECLARE
emp_rec emp%ROWTYPE;
BEGIN
emp_rec.eno := 1500;
emp_rec.ename := 'Steven Hill';
emp_rec.sal := '40000';
-- A %ROWTYPE value can fill in all the row fields.
INSERT INTO emp VALUES emp_rec;
-- The fields of a %ROWTYPE can completely replace the table columns.
UPDATE emp SET ROW = emp_rec WHERE eno = 100;
END;
/
Although this technique integrates PL/SQL variables and types with SQLDML
statements, you cannot use PL/SQL records as bind variables in dynamic SQL statements.
Issuing Queries from PL/SQL
PL/SQL lets you performqueries (SELECT statements in SQL) and access individual fields
or entire rows from the result set. Depending on the complexity of the processing that you
want to do on thequery results, you can use various notations.
Selecting At Most One Row: SELECT INTO Statement
If you expect a query to only return one row, you can write a regular SQL SELECT
statement with an additional INTO clause specifying the PL/SQL variable to hold the result:
If the query might return more than one row, but you do not care about values after the
first, you can restrict any result set to a single row by comparing the ROWNUM value:
If the query might return no rows at all, use an exception handler to specify any actions to
take when no data is found:
If you just want to check whether a condition exists in your data, you might be able to
code thequery with the COUNT(*) operator, which always returns a number and never raises
the NO_DATA_FOUND exception:
Selecting Multiple Rows: BULK COLLECT Clause
If you need to bring a large quantity of data into local PL/SQL variables, rather than
looping through a result set one row at a time, you can use the BULK COLLECT clause.
When you query only certain columns, you can store all theresults for each column in a
separatecollection variable:
SELECT employee_id, last_name, salary FROM employees
BULK COLLECT INTO all_employee_ids, all_last_names, all_salaries;
When you query all the columns of a table, you can storethe entire result set in a
collection of records, which makes it convenient to loop through the results and refer to
different columns:
Page 46 of 77
SELECT * FROM employees BULK COLLECT INTO all_employees;
FOR i IN all_employees.FIRST .. all_employees.LAST
LOOP
...
END LOOP;
This technique can be very fast, but also very memory-intensive. If you use it often, you
might be able to improve your code by doing more of the work in SQL:
 If you only need to loop once through theresult set, use a FOR loop as described
in the following sections. This technique avoids thememory overhead of storing
a copy of theresult set.
 If you are looping through theresult set to scan for certain values or filter the
results into a smaller set, do this scanning or filtering in the original query
instead. You can add more WHERE clauses in simple cases, or use set operators
such as INTERSECT and MINUS if you are comparing two or more sets of results.
 If you are looping through theresult set and running another query or a DML
statement for each result row, you can probably find a more efficient technique.
For queries, look at including subqueries or EXISTS or NOT EXISTS clauses in the
original query. For DMLstatements, look at the FORALL statement, which is
much faster than coding thesestatements inside a regular loop.
Looping Through Multiple Rows: Cursor FOR Loop
Perhaps the most common case of a query is one where you issue the SELECT statement,
then immediately loop once through the rows of the result set. PL/SQL lets you use a
simple FOR loop for this kind of query:
The iterator variable for the FOR loop does not need to be declared in advance. It is a
%ROWTYPE record whosefield names match the column names from the query, and that
exists only during the loop. When you use expressions rather than explicit column names,
use column aliases so that you can refer to the corresponding values inside the loop:
Performing Complicated Query Processing:Explicit Cursors
For full control over query processing, you can use explicit cursors in combination with
the OPEN, FETCH, and CLOSE statements.
You might want to specify a query in one place but retrieve therows somewhere else, even
in another subprogram. Or you might want to choose very different query parameters, such
as ORDER BY or GROUP BY clauses, depending on thesituation. Or you might want to
process some rows differently than others, and so need more than a simple loop.
Because explicit cursors are so flexible, you can choose from different notations
depending on your needs. The following sections describe all the query-processing
features that explicit cursors provide.
Querying Data with PL/SQL
In traditional database programming, you process query results using an internal data
structurecalled a cursor. In most situations, PL/SQL can manage thecursor for you, so
that code to process query results is straightforward and compact. This section discusses
how to process both simple queries where PL/SQL manages everything, and complex
queries where you interact with the cursor.
Querying Data with PL/SQL: Implicit Cursor FOR Loop
With PL/SQL, it is very simple to issue a query, retrieve each row of the result into a
%ROWTYPE record, and process each row in a loop:
 You include the text of thequery directly in the FOR loop.
 PL/SQL creates a record variable with fields corresponding to the columns of the
result set.
 You refer to thefields of this record variable inside the loop. You can perform
tests and calculations, display output, or storetheresults somewhere else.
Here is an example that you can run in SQL*Plus. It does a query to get thename and
status of every index that you can access.
BEGIN
FOR item IN
(
SELECT object_name, status FROM user_objects WHERE object_type = 'INDEX'
AND object_name NOT LIKE '%$%'
)
LOOP
dbms_output.put_line('Index = ' || item.object_name ||
', Status = ' || item.status);
END LOOP;
END;
/
Before each iteration of the FOR loop, PL/SQL fetches into theimplicitly declared record.
The sequence of statements inside the loop is executed once for each row that satisfies the
query. When you leave the loop, the cursor is closed automatically. The cursor is closed
even if you use an EXIT or GOTO statement to leave the loop before all rows are fetched, or
an exception is raised inside the loop.
Querying Data with PL/SQL: Explicit Cursor FOR Loops
IIf you need to reference thesame query from different parts of the same procedure, you
can declare a cursor that specifies thequery, and process the results using a FOR loop.
The following PL/SQ block runs two variations of thesame query, first finding all the
tables you can access, then all the indexes you can access:
DECLARE
CURSOR c1 IS
SELECT object_name, status FROM user_objects WHERE object_type = 'TABLE'
AND object_name NOT LIKE '%$%';
BEGIN
FOR item IN c1 LOOP
dbms_output.put_line('Table = ' || item.object_name ||
', Status = ' || item.status);
END LOOP;
END;
/
Overview of Explicit Cursors
When you need precise control over query processing, you can explicitly declare a cursor
in the declarative part of any PL/SQL block, subprogram, or package.
You use three commands to control a cursor: OPEN, FETCH, and CLOSE. First, you initialize
the cursor with the OPEN statement, which identifies the result set. Then, you can execute
FETCH repeatedly until all rows have been retrieved, or you can use the BULK COLLECT
clause to fetch all rows at once. When thelast row has been processed, you release the
cursor with the CLOSE statement.
This technique requires more code than other techniques such as theimplicit cursor FOR
loop. Its advantage is flexibility. You can:
 Process several queries in parallel by declaring and opening multiple cursors.
Page 47 of 77
 Process multiple rows in a single loop iteration, skip rows, or split theprocessing
into more than one loop.
Declaring a Cursor
You must declare a cursor before referencing it in other statements. You give the cursor a
name and associate it with a specific query. You can optionally declare a return typefor
the cursor (such as table_name%ROWTYPE). You can optionally specify parameters that you
use in the WHERE clause instead of referring to local variables. These parameters can have
default values.
For example, you might declare cursors like these:
DECLARE
CURSOR c1 IS SELECT empno, ename, job, sal FROM emp
WHERE sal > 2000;
CURSOR c2 RETURN dept%ROWTYPE IS
SELECT * FROM dept WHERE deptno = 10;
The cursor is not a PL/SQL variable: you cannot assign values to a cursor or use it in an
expression. Cursors and variables follow thesame scoping rules. Naming cursors after
database tables is possible but not recommended.
A cursor can take parameters, which can appear in the associated query wherever constants
can appear. Theformal parameters of a cursor must be IN parameters; they supply values
in the query, but do not return any values from the query. You cannot imposethe
constraint NOT NULL on a cursor parameter.
As the example below shows, you can initialize cursor parameters to default values. You
can pass different numbers of actual parameters to a cursor, accepting or overriding the
default values as you please. Also, you can add new formal parameters without having to
change existing references to thecursor.
DECLARE
CURSOR c1 (low INTEGER DEFAULT 0,
high INTEGER DEFAULT 99) IS SELECT ...
Cursor parameters can be referenced only within the query specified in thecursor
declaration. The parameter values are used by the associated query when thecursor is
opened.
Opening a Cursor
Opening the cursor executes the query and identifies the result set, which consists of all
rows that meet thequery search criteria. For cursors declared using the FOR UPDATE clause,
the OPEN statement also locks thoserows. An example of the OPEN statement follows:
DECLARE
CURSOR c1 IS SELECT ename, job FROM emp WHERE sal < 3000;
...
BEGIN
OPEN c1;
...
END;
Rows in theresult set are retrieved by the FETCH statement, not when the OPEN statement is
executed.
Fetching with a Cursor
Unless you use theBULK COLLECT clause (discussed in the next section), the FETCH
statement retrieves the rows in theresult set one at a time. Each fetch retrieves the current
row and advances the cursor to the next row in the result set.
You can store each column in a separate variable, or store theentire row in a record that
has the appropriatefields (usually declared using %ROWTYPE):
-- This cursor queries 3 columns.
-- Each column is fetched into a separate variable.
FETCH c1 INTO my_empno, my_ename, my_deptno;
-- This cursor was declared as SELECT * FROM employees.
-- An entire row is fetched into the my_employees record, which
-- is declared with the type employees%ROWTYPE.
FETCH c2 INTO my_employees;
For each column value returned by thequery associated with the cursor, there must be a
corresponding, type-compatiblevariable in the INTO list. Typically, you usethe FETCH
statement in the following way:
LOOP
FETCH c1 INTO my_record;
EXIT WHEN c1%NOTFOUND;
-- process data record
END LOOP;
The query can reference PL/SQL variables within its scope. Any variables in the query are
evaluated only when the cursor is opened. In thefollowing example, each retrieved salary
is multiplied by 2, even though factor is incremented after every fetch:
DECLARE
my_sal employees.salary%TYPE;
my_job employees.job_id%TYPE;
factor INTEGER := 2;
CURSOR c1 IS
SELECT factor*salary FROM employees WHERE job_id = my_job;
BEGIN
OPEN c1; -- here factor equals 2
LOOP
FETCH c1 INTO my_sal;
EXIT WHEN c1%NOTFOUND;
factor := factor + 1; -- does not affect FETCH
END LOOP;
END;
/
To change the result set or the values of variables in the query, you must close and reopen
the cursor with the input variables set to their new values.
However, you can use a different INTO list on separate fetches with the same cursor. Each
fetch retrieves another row and assigns values to the target variables, as the following
example shows:
DECLARE
CURSOR c1 IS SELECT last_name FROM employees ORDER BY last_name;
name1 employees.last_name%TYPE;
name2 employees.last_name%TYPE;
name3 employees.last_name%TYPE;
BEGIN
OPEN c1;
FETCH c1 INTO name1; -- this fetches first row
FETCH c1 INTO name2; -- this fetches second row
FETCH c1 INTO name3; -- this fetches third row
CLOSE c1;
Page 48 of 77
END;
/
If you fetch past thelast row in theresult set, the values of thetarget variables are
undefined.
Note: Eventually, the FETCH statement fails to return a row. When that happens, no
exception is raised. To detect the failure, use the cursor attribute %FOUND or %NOTFOUND.
Fetching Bulk Data with a Cursor
The BULK COLLECT clause lets you fetch all rows from the result set at once. In the
following example, you bulk-fetch from a cursor into two collections:
DECLARE
TYPE NumTab IS TABLE OF employees.employee_id%TYPE;
TYPE NameTab IS TABLE OF employees.last_name%TYPE;
nums NumTab;
names NameTab;
CURSOR c1 IS
SELECT employee_id, last_name
FROM employees
WHERE job_id = 'ST_CLERK';
BEGIN
OPEN c1;
FETCH c1 BULK COLLECT INTO nums, names;
-- Here is where you iterate through the elements in the NUMS and
-- NAMES collections.
NULL;
CLOSE c1;
END;
/
Closing a Cursor
The CLOSE statement disables the cursor, and the result set becomes undefined. Once a
cursor is closed, you can reopen it, which runs thequery again with the latest values of any
cursor parameters and variables referenced in the WHERE clause. Any other operation on a
closed cursor raises the predefined exception INVALID_CURSOR.
Writing Maintainable PL/SQL Queries
Instead of referring to local variables, you can declare a cursor that accepts parameters,
and pass values for thoseparameters when you open the cursor. If thequery is usually
issued with certain values, you can make those values the defaults. You can use either
positionalnotation or named notation to pass the parameter values.
Example 6-1 Passing Parameters to a Cursor FOR Loop
The following example computes the totalwages paid to employees in a specified
department.
DECLARE
CURSOR c1 (name VARCHAR2, max_wage NUMBER) IS
SELECT * FROM employees WHERE last_name = name and salary < max_wage;
BEGIN
FOR person IN c1('Austin', 30000)
LOOP
-- process data record
dbms_output.put_line('Name = ' || person.last_name ||
', salary = ' || person.salary);
END LOOP;
END;
/
Example 6-2 Passing Parameters to ExplicitCursors
For example, here are several ways to open a cursor:
DECLARE
emp_name employees.last_name%TYPE := 'Austin';
emp_salary employees.salary%TYPE := 30000;
my_record employees%ROWTYPE;
CURSOR c1 (name VARCHAR2, max_wage NUMBER) IS
SELECT * FROM employees WHERE last_name = name and salary < max_wage;
BEGIN
-- Any ofthe following statements opens the cursor:
-- OPEN c1('Austin', 3000);
-- OPEN c1('Austin', emp_salary);
-- OPEN c1(emp_name, 3000);
-- OPEN c1(emp_name, emp_salary);
OPEN c1(emp_name, emp_salary);
LOOP
FETCH c1 INTO my_record;
EXIT WHEN c1%NOTFOUND;
-- process data record
dbms_output.put_line('Name = ' || my_record.last_name ||
', salary = ' || my_record.salary);
END LOOP;
END;
/
To avoid confusion, use different names for cursor parameters and the PL/SQL variables
that you pass into those parameters.
Formal parameters declared with a default value do not need a corresponding actual
parameter. If you omit them, they assume their default values when the OPEN statement is
executed.
Using Cursor Attributes
Every explicit cursor and cursor variable has four attributes: %FOUND, %ISOPEN
%NOTFOUND, and %ROWCOUNT. When appended to the cursor or cursor variable, these
attributes return useful information about the execution of a data manipulation statement.
You can use cursor attributes in procedural statements but not in SQL statements.
Overview of Explicit Cursor Attributes
Explicit cursor attributes return information about theexecution of a multi-row query.
When an explicit cursor or a cursor variable is opened, therows that satisfy theassociated
query are identified and form theresult set. Rows are fetched from the result set.
%FOUND Attribute: Has a Row Been Fetched?
After a cursor or cursor variable is opened but before the first fetch, %FOUND returns NULL.
After any fetches, it returns TRUE if the last fetch returned a row, or FALSE if thelast fetch
did not return a row. The following example uses %FOUND to select an action:
DECLARE
CURSOR c1 IS SELECT last_name, salary FROM employees WHERE ROWNUM < 11;
my_ename employees.last_name%TYPE;
my_salary employees.salary%TYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO my_ename, my_salary;
IF c1%FOUND THEN -- fetch succeeded
dbms_output.put_line('Name = ' || my_ename || ', salary = ' ||
my_salary);
ELSE -- fetch failed, so exit loop
Page 49 of 77
EXIT;
END IF;
END LOOP;
END;
/
If a cursor or cursor variable is not open, referencing it with %FOUND raises thepredefined
exception INVALID_CURSOR.
%ISOPEN Attribute: Is the Cursor Open?
%ISOPEN returns TRUE if its cursor or cursor variable is open;otherwise, %ISOPEN returns
FALSE. Thefollowing example uses %ISOPEN to select an action:
DECLARE
CURSOR c1 IS SELECT last_name, salary FROM employees WHERE ROWNUM < 11;
the_name employees.last_name%TYPE;
the_salary employees.salary%TYPE;
BEGIN
IF c1%ISOPEN = FALSE THEN -- cursor was not already open
OPEN c1;
END IF;
FETCH c1 INTO the_name, the_salary;
CLOSE c1;
END;
/
%NOTFOUND Attribute: Has a Fetch Failed?
%NOTFOUND is the logical oppositeof %FOUND. %NOTFOUND yields FALSE if thelast fetch
returned a row, or TRUE if thelast fetch failed to return a row. In the following example,
you use %NOTFOUND to exit a loop when FETCH fails to return a row:
DECLARE
CURSOR c1 IS SELECT last_name, salary FROM employees WHERE ROWNUM < 11;
my_ename employees.last_name%TYPE;
my_salary employees.salary%TYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO my_ename, my_salary;
IF c1%NOTFOUND THEN -- fetch failed, so exit loop
-- A shorter form of this test is "EXIT WHEN c1%NOTFOUND;"
EXIT;
ELSE -- fetch succeeded
dbms_output.put_line('Name = ' || my_ename || ', salary = ' ||
my_salary);
END IF;
END LOOP;
END;
/
Before thefirst fetch, %NOTFOUND returns NULL. If FETCH never executes successfully, the
loop is never exited, because the EXIT WHEN statement executes only if its WHEN condition
is true. To be safe, you might want to use the following EXIT statement instead:
EXIT WHEN c1%NOTFOUND OR c1%NOTFOUND IS NULL;
If a cursor or cursor variable is not open, referencing it with %NOTFOUND raises an
INVALID_CURSOR exception.
%ROWCOUNT Attribute: How Many Rows Fetched So Far?
When its cursor or cursor variable is opened, %ROWCOUNT is zeroed. Before thefirst fetch,
%ROWCOUNT yields 0. Thereafter, it yields the number of rows fetched so far. The number
is incremented if thelast fetch returned a row. The following example uses %ROWCOUNT to
test if more than ten rows have been fetched:
DECLARE
CURSOR c1 IS SELECT last_name FROM employees WHERE ROWNUM < 11;
name employees.last_name%TYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO name;
EXIT WHEN c1%NOTFOUND;
dbms_output.put_line(c1%ROWCOUNT || '. ' || name);
IF c1%ROWCOUNT = 5 THEN
dbms_output.put_line('--- Fetched 5th record ---');
END IF;
END LOOP;
CLOSE c1;
END;
/
If a cursor or cursor variable is not open, referencing it with %ROWCOUNT raises
INVALID_CURSOR.
Table 6-1 shows what each cursor attributereturns before and after you execute an OPEN,
FETCH, or CLOSE statement.
Table 6-1 Cursor Attribute Values
%FOUN
D
%ISOPE
N
%NOTFOUN
D
%ROWCOUN
T
OPEN befor
e
exception FALSE exception exception
after NULL TRUE NULL 0
First
FETCH
befor
e
NULL TRUE NULL 0
after TRUE TRUE FALSE 1
Next
FETCH(es
)
befor
e
TRUE TRUE FALSE 1
after TRUE TRUE FALSE data dependent
Last
FETCH
befor
e
TRUE TRUE FALSE data dependent
after FALSE TRUE TRUE data dependent
CLOSE befor
e
FALSE TRUE TRUE data dependent
after exception FALSE exception exception
Notes:
1. Referencing %FOUND, %NOTFOUND, or %ROWCOUNT before a cursor is
opened or after it is closedraises INVALID_CURSOR.
2. After the first FETCH, if the result set was empty, %FOUND yields FALSE,
%NOTFOUND yields TRUE, and %ROWCOUNT yields 0.
Using Cursor Variables (REF CURSORs)
Page 50 of 77
Like a cursor, a cursor variable points to the current row in theresult set of a multi-row
query. A cursor variable is more flexible because it is not tied to a specific query. You can
open a cursor variable for any query that returns the right set of columns.
You pass a cursor variable as a parameter to local and stored subprograms. Opening the
cursor variable in one subprogram, and processing it in a different subprogram, helps to
centralize data retrieval. This technique is also useful for multi-language applications,
where a PL/SQL subprogram might return a result set to a subprogram written in a
different language.
Cursor variables are available to every PL/SQL client. For example, you can declare a
cursor variable in a PL/SQL host environment such as an OCI or Pro*C program, then
pass it as an input host variable (bind variable) to PL/SQL. Application development tools
such as Oracle Forms and Oracle Reports, which have a PL/SQL engine, can use cursor
variables entirely on the client side. Or, you can pass cursor variables back and forth
between a client and the database server through remote procedure calls.
What Are Cursor Variables (REF CURSORs)?
Cursor variables are like pointers to result sets. You use them when you want to performa
query in one subprogram, and process theresults in a different subprogram (possibly one
written in a different language). A cursor variable has datatype REF CURSOR, and you might
see them referred to informally as REF CURSORs.
Unlike an explicit cursor, which always refers to the same query work area, a cursor
variable can refer to different work areas. You cannot use a cursor variable where a cursor
is expected, or vice versa.
Why Use Cursor Variables?
You use cursor variables to pass query result sets between PL/SQL stored subprograms
and various clients. PL/SQL and its clients share a pointer to the query work area in which
the result set is stored. For example, an OCI client, Oracle Forms application, and Oracle
database server can all refer to the same work area.
A query work area remains accessible as long as any cursor variable points to it, as you
pass thevalue of a cursor variable from one scopeto another. For example, if you pass a
host cursor variable to a PL/SQL block embedded in a Pro*C program, the work area to
which the cursor variable points remains accessible after theblock completes.
If you have a PL/SQL engine on the client side, calls from client to server impose no
restrictions. For example, you can declare a cursor variable on the client side, open and
fetch from it on the server side, then continue to fetch from it back on the client side. You
can also reduce network traffic by having a PL/SQL block open or close several host
cursor variables in a single round trip.
Declaring REF CURSOR Types and Cursor Variables
To create cursor variables, you define a REF CURSOR type, then declare cursor variables of
that type. You can define REF CURSOR types in any PL/SQL block, subprogram, or
package. In the following example, you declare a REF CURSOR typethat represents aresult
set from theDEPARTMENTS table:
DECLARE
TYPE DeptCurTyp IS REF CURSOR RETURN departments%ROWTYPE;
REF CURSOR types can be strong (with a return type) or weak (with no return type).
Strong REF CURSOR types areless error pronebecause the PL/SQL compiler lets you
associate a strongly typed cursor variable only with queries that return the right set of
columns. Weak REF CURSOR types aremore flexible because thecompiler lets you
associate a weakly typed cursor variable with any query.
Because there is no typechecking with a weak REF CURSOR, all such types are
interchangeable. Instead of creating a new type, you can use the predefined type
SYS_REFCURSOR.
Once you define a REF CURSOR type, you can declare cursor variables of that typein any
PL/SQL block or subprogram.
DECLARE
TYPE EmpCurTyp IS REF CURSOR RETURN emp%ROWTYPE; -- strong
TYPE GenericCurTyp IS REF CURSOR; -- weak
cursor1 EmpCurTyp;
cursor2 GenericCurTyp;
my_cursor SYS_REFCURSOR; -- didn't need to declare a new type above
The following example declares the cursor variable dept_cv:
DECLARE
TYPE DeptCurTyp IS REF CURSOR RETURN dept%ROWTYPE;
dept_cv DeptCurTyp; -- declare cursor variable
To avoid declaring thesame REF CURSOR typein each subprogram that uses it, you can put
the REF CURSOR declaration in a package spec. You can declare cursor variables of that
typein the corresponding package body, or within your own procedure or function.
Example 6-3 Cursor Variable Returning %ROWTYPE
In the RETURN clause of a REF CURSOR typedefinition, you can use %ROWTYPE to refer to a
strongly typed cursor variable:
DECLARE
TYPE TmpCurTyp IS REF CURSOR RETURN employees%ROWTYPE;
tmp_cv TmpCurTyp; -- declare cursor variable
TYPE EmpCurTyp IS REF CURSOR RETURN tmp_cv%ROWTYPE;
emp_cv EmpCurTyp; -- declare cursor variable
BEGIN
NULL;
END;
/
Example 6-4 Cursor Variable Returning %TYPE
You can also use %TYPE to provide thedatatypeof a record variable:
DECLARE
dept_rec departments%ROWTYPE; -- declare record variable
TYPE DeptCurTyp IS REF CURSOR RETURN dept_rec%TYPE;
dept_cv DeptCurTyp; -- declare cursor variable
BEGIN
NULL;
END;
/
Example 6-5 Cursor Variable Returning Record Type
This example specifies a user-defined RECORD typein the RETURN clause:
DECLARE
TYPE EmpRecTyp IS RECORD (
employee_id NUMBER,
last_name VARCHAR2(30),
salary NUMBER(7,2));
TYPE EmpCurTyp IS REF CURSOR RETURN EmpRecTyp;
emp_cv EmpCurTyp; -- declare cursor variable
BEGIN
NULL;
END;
Page 51 of 77
/
Passing CursorVariables As Parameters
You can declare cursor variables as the formal parameters of functions and procedures.
The following example defines a REF CURSOR type, then declares a cursor variable of that
typeas a formal parameter:
DECLARE
TYPE EmpCurTyp IS REF CURSOR RETURN employees%ROWTYPE;
emp EmpCurTyp;
-- Once we have a result set, we can process all the rows
-- inside a single procedure rather than calling a procedure
-- for each row.
PROCEDURE process_emp_cv (emp_cv IN EmpCurTyp) IS
person employees%ROWTYPE;
BEGIN
dbms_output.put_line('-----');
dbms_output.put_line('Here are the names from the result set:');
LOOP
FETCH emp_cv INTO person;
EXIT WHEN emp_cv%NOTFOUND;
dbms_output.put_line('Name = ' || person.first_name ||
' ' || person.last_name);
END LOOP;
END;
BEGIN
-- First find 10 arbitrary employees.
OPEN emp FOR SELECT * FROM employees WHERE ROWNUM < 11;
process_emp_cv(emp);
CLOSE emp;
-- Then find employees matching a condition.
OPEN emp FOR SELECT * FROM employees WHERE last_name LIKE 'R%';
process_emp_cv(emp);
CLOSE emp;
END;
/
Note: Like all pointers, cursor variables increase the possibility of parameter aliasing.
Controlling Cursor Variables: OPEN-FOR, FETCH, and CLOSE
You use three statements to control a cursor variable: OPEN-FOR, FETCH, and CLOSE. First,
you OPEN a cursor variable FOR a multi-row query. Then, you FETCH rows from the result
set. When all the rows are processed, you CLOSE thecursor variable.
Opening a Cursor Variable
The OPEN-FOR statement associates a cursor variable with a multi-row query, executes the
query, and identifies the result set.
OPEN {cursor_variable | :host_cursor_variable} FOR
{ select_statement
| dynamic_string [USING bind_argument[, bind_argument]...] };
The cursor variable can be declared directly in PL/SQL, or in a PL/SQL host environment
such as an OCI program.
The SELECT statement for the query can be coded directly in thestatement, or can be a
string variable or string literal. When you use a string as thequery, it can include
placeholders for bind variables, and you specify thecorresponding values with a USING
clause.
Note: This section discusses the staticSQL case, in which select_statement is used.
Unlike cursors, cursor variables take no parameters. Instead, you can pass whole queries
(not just parameters) to a cursor variable. Thequery can reference host variables and
PL/SQL variables, parameters, and functions.
The example below opens a cursor variable. Notice that you can apply cursor attributes
(%FOUND, %NOTFOUND, %ISOPEN, and %ROWCOUNT) to a cursor variable.
DECLARE
TYPE EmpCurTyp IS REF CURSOR RETURN employees%ROWTYPE;
emp_cv EmpCurTyp;
BEGIN
IF NOT emp_cv%ISOPEN THEN
/* Open cursor variable. */
OPEN emp_cv FOR SELECT * FROM employees;
END IF;
CLOSE emp_cv;
END;
/
Other OPEN-FOR statements can open thesame cursor variable for different queries. You
need not close a cursor variable before reopening it. (Recall that consecutive OPENs of a
staticcursor raise the predefined exception CURSOR_ALREADY_OPEN.) When you reopen a
cursor variable for a different query, theprevious query is lost.
Fetching from a Cursor Variable
The FETCH statement retrieves rows from the result set of a multi-row query. It works the
same with cursor variables as with explicit cursors.
Example 6-9 Fetching from a Cursor Variable into a Record
The following example fetches rows one at a time from a cursor variable into a record:
DECLARE
TYPE EmpCurTyp IS REF CURSOR RETURN employees%ROWTYPE;
emp_cv EmpCurTyp;
emp_rec employees%ROWTYPE;
BEGIN
OPEN emp_cv FOR SELECT * FROM employees WHERE salary < 3000;
LOOP
/* Fetch from cursor variable. */
FETCH emp_cv INTO emp_rec;
EXIT WHEN emp_cv%NOTFOUND; -- exit when last row is fetched
-- process data record
dbms_output.put_line('Name = ' || emp_rec.first_name || ' ' ||
emp_rec.last_name);
END LOOP;
CLOSE emp_cv;
END;
/
Example 6-10 Fetching from a Cursor Variable into Collections
Using theBULK COLLECT clause, you can bulk fetch rows from a cursor variable into one
or more collections:
DECLARE
TYPE EmpCurTyp IS REF CURSOR;
TYPE NameList IS TABLE OF employees.last_name%TYPE;
TYPE SalList IS TABLE OF employees.salary%TYPE;
emp_cv EmpCurTyp;
names NameList;
sals SalList;
BEGIN
OPEN emp_cv FOR SELECT last_name, salary FROM employees WHERE salary < 3000;
FETCH emp_cv BULK COLLECT INTO names, sals;
CLOSE emp_cv;
Page 52 of 77
-- Now loop through the NAMES and SALS collections.
FOR i IN names.FIRST .. names.LAST
LOOP
dbms_output.put_line('Name = ' || names(i) || ', salary = ' ||
sals(i));
END LOOP;
END;
/
Any variables in the associated query are evaluated only when thecursor variable is
opened. To change the result set or thevalues of variables in thequery, reopen the cursor
variable with the variables set to new values. You can use a different INTO clause on
separatefetches with thesame cursor variable. Each fetch retrieves another row from the
same result set.
PL/SQL makes sure thereturn typeof thecursor variable is compatible with the INTO
clause of the FETCH statement. If there is a mismatch, an error occurs at compile time if the
cursor variable is strongly typed, or at run time if it is weakly typed. At run time, PL/SQL
raises the predefined exception ROWTYPE_MISMATCH before thefirst fetch. If you trap the
error and execute the FETCH statement using a different (compatible) INTO clause, no rows
are lost.
When you declare a cursor variable as the formal parameter of a subprogram that fetches
from the cursor variable, you must specify the IN or IN OUT mode. If the subprogram also
opens the cursor variable, you must specify the IN OUT mode.
If you try to fetch from a closed or never-opened cursor variable, PL/SQL raises the
predefined exception INVALID_CURSOR.
Closing a CursorVariable
The CLOSE statement disables a cursor variable and makes the associated result set
undefined. Close the cursor variable after thelast row is processed.
When declaring a cursor variable as the formal parameter of a subprogram that closes the
cursor variable, you must specify the IN or IN OUT mode.
If you try to close an already-closed or never-opened cursor variable, PL/SQL raises the
predefined exception INVALID_CURSOR.
7 - Overview of Transaction Processing in PL/SQL
This section explains how to do transaction processing with PL/SQL.
You should already be familiar with theidea of transactions, and how to ensure the
consistency of a database, such as the COMMIT, SAVEPOINT, and ROLLBACK statements.
These are Oracle features, available through all programming languages, that let multiple
users work on the database concurrently, and ensure that each user sees a consistent
version of data and that all changes are applied in theright order.
You usually do not need to writeextra code to prevent problems with multiple users
accessing data concurrently. Oracle uses locks to control concurrent access to data, and
locks only theminimum amount of data necessary, for as little time as possible. You can
request locks on tables or rows if you really do need this level of control. You can choose
from several modes of locking such as row share and exclusive.
Using COMMIT, SAVEPOINT, and ROLLBACK in PL/SQL
You can include COMMIT, SAVEPOINT, and ROLLBACK statements directly in your PL/SQL
programs.
The COMMIT statement ends the current transaction, making any changes made during that
transaction permanent, and visible to other users.
The ROLLBACK statement ends the current transaction and undoes any changes made
during that transaction. If you make a mistake, such as deleting the wrong row from a
table, a rollback restores the original data. If you cannot finish a transaction because an
exception is raised or a SQL statement fails, a rollback lets you take corrective action and
perhaps start over.
SAVEPOINT names and marks the current point in the processing of a transaction.
Savepoints let you roll back part of a transaction instead of thewhole transaction.
Consider a transaction that transfers money from one bank account to another. It is
important that themoney come out of one account, and into the other, at exactly thesame
moment. Otherwise, a problem partway through might make the money be lost from both
accounts or be duplicated in both accounts.
BEGIN
UPDATE accts SET bal = my_bal - debit
WHERE acctno = 7715;
UPDATE accts SET bal = my_bal + credit
WHERE acctno = 7720;
COMMIT WORK;
END;
Transactions are not tied to PL/SQL BEGIN-END blocks. A block can contain multiple
transactions, and a transaction can span multiple blocks.
The optionalCOMMENT clause lets you specify a comment to be associated with a
distributed transaction. If a network or machine fails during the commit, thestateof the
distributed transaction might be unknown or in doubt. In that case, Oracle stores the text
specified by COMMENT in the data dictionary along with thetransaction ID. Thetext must
be a quoted literal up to 50 characters long:
COMMIT COMMENT 'In-doubt order transaction; notify Order Entry';
PL/SQL does not support the FORCE clause of SQL, which manually commits an in-doubt
distributed transaction.
The following example inserts information about an employee into three different database
tables. If an INSERT statement tries to storea duplicate employeenumber, the predefined
exception DUP_VAL_ON_INDEX is raised. To make sure that changes to all three tables are
undone, the exception handler executes a ROLLBACK.
DECLARE
emp_id INTEGER;
BEGIN
SELECT empno, ... INTO emp_id, ... FROM new_emp WHERE ...
INSERT INTO emp VALUES (emp_id, ...);
INSERT INTO tax VALUES (emp_id, ...);
INSERT INTO pay VALUES (emp_id, ...);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
ROLLBACK;
END;
Statement-Level Rollbacks
Before executing a SQL statement, Oracle marks an implicit savepoint. Then, if the
statement fails, Oracle rolls it back automatically. For example, if an INSERT statement
raises an exception by trying to insert a duplicate value in a unique index, the statement is
rolled back. Only work started by the failed SQL statement is lost. Work done before that
statement in the current transaction is kept.
Page 53 of 77
Oracle can also roll back single SQL statements to break deadlocks. Oracle signals an
error to one of the participatingtransactions and rolls back the current statement in that
transaction.
Before executing a SQL statement, Oracle must parseit, that is, examine it to make sure it
follows syntaxrules and refers to valid schema objects. Errors detected while executing a
SQL statement cause a rollback, but errors detected while parsing the statement do not.
The following example marks a savepoint before doing an insert. If theINSERT statement
tries to storea duplicate value in the empno column, the predefined exception
DUP_VAL_ON_INDEX is raised. In that case, you roll back to the savepoint, undoing just the
insert.
DECLARE
emp_id emp.empno%TYPE;
BEGIN
UPDATE emp SET ... WHERE empno = emp_id;
DELETE FROM emp WHERE ...
SAVEPOINT do_insert;
INSERT INTO emp VALUES (emp_id, ...);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
ROLLBACK TO do_insert;
END;
When you roll back to a savepoint, any savepoints marked after that savepoint are erased.
The savepoint to which you roll back is not erased. A simple rollback or commit erases all
savepoints.
If you mark a savepoint within a recursive subprogram, new instances of the SAVEPOINT
statement are executed at each level in therecursive descent, but you can only roll back to
the most recently marked savepoint.
Savepoint names are undeclared identifiers. Reusing a savepoint name within a transaction
moves the savepoint from its old position to the current point in thetransaction. Thus, a
rollback to thesavepoint affects only thecurrent part of your transaction:
BEGIN
SAVEPOINT my_point;
UPDATE emp SET ... WHERE empno = emp_id;
SAVEPOINT my_point; -- move my_point to current point
INSERT INTO emp VALUES (emp_id, ...);
EXCEPTION
WHEN OTHERS THEN
ROLLBACK TO my_point;
END;
The number of active savepoints for each session is unlimited.
Committing a Transaction
A transaction is made permanent by issuing the SQL command COMMIT. Thegeneral
syntaxfor the COMMITcommand is:
COMMIT;
For example,
INSERT INTO CUSTOMERS(ID,NAME,AGE,ADDRESS,SALARY)
VALUES (1, 'Ramesh', 32, 'Ahmedabad', 2000.00 );
INSERT INTO CUSTOMERS(ID,NAME,AGE,ADDRESS,SALARY)
VALUES (6, 'Komal', 22, 'MP', 4500.00 );
COMMIT;
Rolling Back Transactions
Changes made to thedatabase without COMMIT could be undone using the ROLLBACK
command.
The general syntaxfor the ROLLBACK command is:
ROLLBACK [TO SAVEPOINT < savepoint_name>];
When a transaction is aborted due to some unprecedented situation, like systemfailure, the
entire transaction since a commit is automatically rolled back. if you are not using
savepoint then simply use the following statement to rollback all the changes:
ROLLBACK;
Savepoints
Savepoints are sort of markers that help in splittinga long transaction into smaller units by
setting some checkpoints. By setting savepoints within a long transaction, you can roll
back to a checkpoint if required. This is done by issuing the SAVEPOINT command.
The general syntaxfor the SAVEPOINT command is:
SAVEPOINT < savepoint_name >;
For example:
INSERT INTO CUSTOMERS(ID,NAME,AGE,ADDRESS,SALARY)
VALUES (7, 'Rajnish', 27, 'HP', 9500.00 );
SAVEPOINT sav1;
UPDATECUSTOMERS
SET SALARY = SALARY + 1000;
ROLLBACK TO sav1;
UPDATECUSTOMERS
SET SALARY = SALARY + 1000
WHERE ID = 8;
COMMIT;
Here ROLLBACK TO sav1; statement rollback the changes upto thepoint where you
had marked savepoint sav1 and after that new changes will start.
How Oracle Does Implicit Rollbacks
Before executing an INSERT, UPDATE, or DELETE statement, Oracle marks an implicit
savepoint (unavailable to you). If thestatement fails, Oracle rolls back to the savepoint.
Normally, just the failed SQL statement is rolled back, not the whole transaction. If the
statement raises an unhandled exception, the host environment determines what is rolled
back.
If you exit a stored subprogram with an unhandled exception, PL/SQL does not assign
values to OUT parameters, and does not do any rollback.
Ending Transactions
You should explicitly commit or roll back every transaction. Whether you issue the
commit or rollback in your PL/SQL program or from a client program depends on the
application logic. If you do not commit or roll back a transaction explicitly, the client
environment determines its final state.
For example, in theSQL*Plus environment, if your PL/SQL block does not include a
COMMIT or ROLLBACK statement, thefinal stateof your transaction depends on what you do
after running the block. If you execute a data definition, data control, or COMMIT statement
Page 54 of 77
or if you issue the EXIT, DISCONNECT, or QUIT command, Oracle commits the transaction. If
you execute a ROLLBACK statement or abort the SQL*Plus session, Oracle rolls back the
transaction.
Oracle precompiler programs roll back thetransaction unless the program explicitly
commits or rolls back work, and disconnects using the RELEASE parameter:
EXEC SQL COMMIT WORK RELEASE;
Setting Transaction Properties with SET TRANSACTION
You use theSET TRANSACTION statement to begin a read-only or read-write transaction,
establish an isolation level, or assign your current transaction to a specified rollback
segment. Read-only transactions are useful for running multiple queries while other users
updatethe same tables.
During a read-only transaction, all queries refer to thesame snapshot of the database,
providing a multi-table, multi-query, read-consistent view. Other users can continue to
query or updatedata as usual. A commit or rollback ends the transaction. In theexample
below a storemanager uses a read-only transaction to gather sales figures for the day, the
past week, and the past month. Thefigures are unaffected by other users updating the
database during thetransaction.
DECLARE
daily_sales REAL;
weekly_sales REAL;
monthly_sales REAL;
BEGIN
COMMIT; -- ends previous transaction
SET TRANSACTION READ ONLY NAME 'Calculate sales figures';
SELECT SUM(amt) INTO daily_sales FROM sales
WHERE dte = SYSDATE;
SELECT SUM(amt) INTO weekly_sales FROM sales
WHERE dte > SYSDATE - 7;
SELECT SUM(amt) INTO monthly_sales FROM sales
WHERE dte > SYSDATE - 30;
COMMIT; -- ends read-only transaction
END;
The SET TRANSACTION statement must be the first SQL statement in a read-only transaction
and can only appear once in a transaction. If you set a transaction to READ ONLY,
subsequent queries see only changes committed before the transaction began. The use of
READ ONLY does not affect other users or transactions.
Restrictions on SET TRANSACTION
Only the SELECT INTO, OPEN, FETCH, CLOSE, LOCK TABLE, COMMIT, and ROLLBACK
statements are allowed in a read-only transaction. Queries cannot be FOR UPDATE.
Overriding Default Locking
By default, Oracle locks data structures for you automatically, which is a major strength of
the Oracle database: different applications can read and write to the same data without
harming each other's data or coordinating with each other.
You can request data locks on specific rows or entire tables if you need to override default
locking. Explicit locking lets you deny access to data for the duration of a transaction.:
 With the LOCK TABLE statement, you can explicitly lock entire tables.
 With the SELECT FOR UPDATE statement, you can explicitly lock specific rows of
a table to make sure they do not change after you have read them. That way, you
can check which or how many rows will be affected by an UPDATEor
DELETE statement before issuing the statement, and no other application can
change the rows in the meantime.
Using FOR UPDATE
When you declare a cursor that will be referenced in the CURRENT OF clause of an UPDATE
or DELETE statement, you must use the FOR UPDATE clause to acquire exclusive row locks.
An example follows:
DECLARE
CURSOR c1 IS SELECT empno, sal FROM emp
WHERE job = 'SALESMAN' AND comm > sal
FOR UPDATE NOWAIT;
The SELECT ... FOR UPDATE statement identifies therows that will be updated or deleted,
then locks each row in the result set. This is useful when you want to base an updateon the
existing values in a row. In that case, you must make sure the row is not changed by
another user before theupdate.
The optionalkeyword NOWAIT tells Oracle not to wait if requested rows have been locked
by another user. Control is immediately returned to your program so that it can do other
work before trying again to acquire thelock. If you omit the keyword NOWAIT, Oracle
waits until the rows are available.
All rows are locked when you open thecursor, not as they are fetched. The rows are
unlocked when you commit or roll back the transaction. Since the rows are no longer
locked, you cannot fetch from a FOR UPDATE cursor after a commit.
When querying multiple tables, you can use the FOR UPDATE clause to confine row locking
to particular tables. Rows in a table are locked only if the FOR UPDATE OF clause refers to a
column in that table. For example, the following query locks rows in the emp table but not
in the dept table:
DECLARE
CURSOR c1 IS SELECT ename, dname FROM emp, dept
WHERE emp.deptno = dept.deptno AND job = 'MANAGER'
FOR UPDATE OF sal;
As the next example shows, you use the CURRENT OF clause in an UPDATE or DELETE
statement to refer to the latest row fetched from a cursor:
DECLARE
CURSOR c1 IS SELECT empno, job, sal FROM emp FOR UPDATE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO ...
UPDATE emp SET sal = new_sal WHERE CURRENT OF c1;
END LOOP;
Using LOCK TABLE
You use theLOCK TABLE statement to lock entire database tables in a specified lock mode
so that you can share or deny access to them.. Row share locks allow concurrent access to
a table; they prevent other users from locking the entire table for exclusive use. Table
locks are released when your transaction issues a commit or rollback.
LOCK TABLE emp IN ROW SHARE MODE NOWAIT;
The lock mode determines what other locks can be placed on the table. For example, many
users can acquire row share locks on a table at the same time, but only one user at a time
can acquire an exclusive lock. While one user has an exclusive lock on a table, no other
Page 55 of 77
users can insert, delete, or updaterows in that table. A table lock never keeps other users
from querying a table, and a query never acquires a table lock. Only if two different
transactions try to modify thesame row will one transaction wait for the other to complete.
Fetching Across Commits
PL/SQL raises an exception if you try to fetch from a FOR UPDATE cursor after doing a
commit. TheFOR UPDATE clause locks therows when you open the cursor, and unlocks
them when you commit.
DECLARE
CURSOR c1 IS SELECT ename FROM emp FOR UPDATE OF sal;
BEGIN
FOR emp_rec IN c1 LOOP -- FETCH fails on the second iteration
INSERT INTO temp VALUES ('still going');
COMMIT; -- releases locks
END LOOP;
END;
If you want to fetch across commits, use the ROWID pseudocolumn to mimic the CURRENT
OF clause. Select the rowid of each row into a UROWID variable, then use the rowid to
identify the current row during subsequent updates and deletes:
DECLARE
CURSOR c1 IS SELECT ename, job, rowid FROM emp;
my_ename emp.ename%TYPE;
my_job emp.job%TYPE;
my_rowid UROWID;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO my_ename, my_job, my_rowid;
EXIT WHEN c1%NOTFOUND;
UPDATE emp SET sal = sal * 1.05 WHERE rowid = my_rowid;
-- this mimics WHERE CURRENT OF c1
COMMIT;
END LOOP;
CLOSE c1;
END;
Because the fetched rows are not locked by a FOR UPDATE clause, other users might
unintentionally overwrite your changes. The extra space needed for read consistency is not
released until the cursor is closed, which can slow down processing for large updates.
The next example shows that you can use the %ROWTYPE attributewith cursors that
reference theROWID pseudocolumn:
DECLARE
CURSOR c1 IS SELECT ename, sal, rowid FROM emp;
emp_rec c1%ROWTYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO emp_rec;
EXIT WHEN c1%NOTFOUND;
IF ... THEN
DELETE FROM emp WHERE rowid = emp_rec.rowid;
END IF;
END LOOP;
CLOSE c1;
END;
8 - Using PL/SQL Subprograms
This chapter shows you how to turn sets of statements into reusable subprograms.
Subprograms are like building blocks for modular, maintainable applications.
What Are Subprograms?
Subprograms are named PL/SQL blocks that can be called with a set of parameters.
PL/SQL has two types of subprograms, procedures and functions. Generally, you use a
procedure to perform an action and a function to computea value.
Like anonymous blocks, subprograms have:
 A declarative part, with declarations of types, cursors, constants, variables,
exceptions, and nested subprograms. These items are local and cease to exist
when the subprogram ends.
 An executable part, with statements that assign values, control execution, and
manipulate Oracle data.
 An optionalexception-handling part, which deals with runtime error conditions.
Example 8-1 SimplePL/SQL Procedure
The following example shows a string-manipulation procedure that accepts both input and
output parameters, and handles potentialerrors:
CREATE OR REPLACE PROCEDURE double
(
original IN VARCHAR2, new_string OUT VARCHAR2
)
AS
BEGIN
new_string := original || original;
EXCEPTION
WHEN VALUE_ERROR THEN
dbms_output.put_line('Output buffer not long enough.');
END;
/
Example 8-2 SimplePL/SQL Function
The following example shows a numeric function that declares a local variable to hold
temporary results, and returns a value when finished:
CREATE OR REPLACE FUNCTION square(original NUMBER)
RETURN NUMBER
AS
original_squared NUMBER;
BEGIN
original_squared := original * original;
RETURN original_squared;
END;
/
Advantages of PL/SQL Subprograms
Subprograms let you extend the PL/SQL language. Procedures act like new statements.
Functions act like new expressions and operators.
Subprograms let you break a program down into manageable, well-defined modules. You
can use top-down design and the stepwiserefinement approach to problem solving.
Subprograms promotereusability. Once tested, a subprogram can be reused in any number
of applications. You can call PL/SQL subprograms from many different environments, so
that you do not have to reinvent thewheel each time you use a new language or API to
access thedatabase.
Page 56 of 77
Subprograms promotemaintainability. You can change the internals of a subprogram
without changing other subprograms that call it. Subprograms play a big part in other
maintainability features, such as packages and object types.
Dummy subprograms (stubs) let you defer thedefinition of procedures and functions until
after testing the main program. You can design applications from the top down, thinking
abstractly, without worryingabout implementation details.
When you use PL/SQL subprograms to define an API, you can make your code even more
reusable and maintainable by grouping thesubprograms into a PL/SQL package.
Understanding PL/SQL Procedures
A procedure is a subprogram that performs a specific action. You write procedures using
the SQL CREATE PROCEDURE statement. You specify the name of theprocedure, its
parameters, its local variables, and the BEGIN-END block that contains its code and handles
any exceptions.
For each parameter, you specify:
 Its name.
 Its parameter mode (IN, OUT, or IN OUT). If you omit the mode, the default is IN.
The optionalNOCOPY keyword speeds up processing of large OUT or IN OUT
parameters.
 Its datatype. You specify only the type, not any length or precision constraints.
 Optionally, its default value.
You can specify whether theprocedure executes using theschema and permissions of the
user who defined it, or the user who calls it. You can specify whether it should be part of
the current transaction, or execute in its own transaction where it can COMMIT or
ROLLBACK without ending thetransaction of the caller. Procedures created this way are
stored in thedatabase. You can execute the CREATE PROCEDURE statement interactively
from SQL*Plus, or from a program using native dynamic .
A procedure has two parts:the specification (spec for short) and the body. The procedure
specbegins with the keyword PROCEDURE and ends with the procedure name or a
parameter list. Parameter declarations are optional. Procedures that take no parameters are
written without parentheses.
The procedure body begins with the keyword IS (or AS) and ends with the keyword END
followed by an optionalprocedure name. The procedure body has three parts:a declarative
part, an executable part, and an optionalexception-handling part.
The declarative part contains local declarations. The keyword DECLARE is used for
anonymous PL/SQL blocks, but not procedures. The executable part contains statements,
which are placed between the keywords BEGIN and EXCEPTION (or END). At least one
statement must appear in theexecutable part of a procedure. You can use the NULL
statement to define a placeholder procedure or specify that theprocedure does nothing.
The exception-handling part contains exception handlers, which are placed between the
keywords EXCEPTION and END.
A procedure is called as a PL/SQL statement. For example, you might call the procedure
raise_salary as follows:
raise_salary(emp_id, amount);
Understanding PL/SQL Functions
A function is a subprogram that computes a value. Functions and procedures are structured
alike, except that functions have a RETURN clause.
Functions have a number of optionalkeywords, used to declare a special class of functions
known as table functions. They are typically used for transforming large amounts of data
in data warehousing applications.
The CREATE clause lets you create standalone functions, which are stored in an Oracle
database. You can execute the CREATE FUNCTION statement interactively from SQL*Plus
or from a program using native dynamic SQL.
The AUTHID clause determines whether a stored function executes with the privileges of its
owner (the default) or current user and whether its unqualified references to schema
objects are resolved in theschema of the owner or current user. You can override the
default behavior by specifying CURRENT_USER.
Like a procedure, a function has two parts:thespecand thebody. The function spec
begins with thekeyword FUNCTION and ends with the RETURN clause, which specifies the
datatypeof thereturn value. Parameter declarations are optional. Functions that take no
parameters are written without parentheses.
The function body begins with the keyword IS (or AS) and ends with the keyword END
followed by an optionalfunction name. The function body has three parts:a declarative
part, an executable part, and an optionalexception-handling part.
The declarative part contains local declarations, which are placed between thekeywords IS
and BEGIN. The keyword DECLARE is not used. The executable part contains statements,
which are placed between the keywords BEGIN and EXCEPTION (or END). One or more
RETURN statements must appear in theexecutable part of a function. Theexception-
handling part contains exception handlers, which are placed between the keywords
EXCEPTION and END.
A function is called as part of an expression:
IF sal_ok(new_sal, new_title) THEN ...
Using the RETURN Statement
The RETURN statement immediately ends the execution of a subprogram and returns
control to the caller. Execution continues with thestatement following the subprogram
call. (Do not confuse the RETURN statement with the RETURN clause in a function spec,
which specifies the datatypeof thereturn value.)
A subprogram can contain several RETURN statements. The subprogram does not have to
conclude with a RETURN statement. Executing any RETURN statement completes the
subprogram immediately.
In procedures, a RETURN statement does not return a value and so cannot contain an
expression. The statement returns control to thecaller before theend of theprocedure.
In functions, a RETURN statement must contain an expression, which is evaluated when the
RETURN statement is executed. The resulting value is assigned to the function identifier,
which acts like a variable of the typespecified in the RETURN clause. Observe how the
function balance returns the balance of a specified bank account:
FUNCTION balance (acct_id INTEGER) RETURN REAL IS
acct_bal REAL;
BEGIN
SELECT bal INTO acct_bal FROM accts
WHERE acct_no = acct_id;
RETURN acct_bal;
END balance;
/
Page 57 of 77
The following example shows that the expression in a function RETURN statement can be
arbitrarily complex:
FUNCTION compound (
years NUMBER,
amount NUMBER,
rate NUMBER) RETURN NUMBER IS
BEGIN
RETURN amount * POWER((rate / 100) + 1, years);
END compound;
/
In a function, there must be at least one execution path that leads to a RETURN statement.
Otherwise, you get a function returned without value error at run time.
Declaring NestedPL/SQL Subprograms
You can declare subprograms in any PL/SQL block, subprogram, or package. The
subprograms must go at the end of the declarative section, after all other items.
You must declare a subprogram before calling it. This requirement can make it difficult to
declare several nested subprograms that call each other.
You can declare interrelated nested subprograms using a forward declaration: a
subprogram spec terminated by a semicolon, with no body.
Although the formal parameter list appears in theforward declaration, it must also appear
in the subprogram body. You can place the subprogram body anywhere after theforward
declaration, but they must appear in the same program unit.
Example 8-3 Forward Declaration for a Nested Subprogram
DECLARE
PROCEDURE proc1(arg_list); -- forward declaration
PROCEDURE proc2(arg_list); -- calls proc1
PROCEDURE proc1(arg_list) IS BEGIN proc2; END; -- calls proc2
BEGIN
NULL;
END;
/
Passing Parameters to PL/SQL Subprograms
Actual Versus Formal Subprogram Parameters
Subprograms pass information using parameters:
 The variables declared in a subprogram specand referenced in the subprogram
body are formal parameters.
 The variables or expressions passed from the calling subprogram are actual
parameters.
A good programming practice is to use different names for actual and formal parameters.
When you call a procedure, the actual parameters are evaluated and the results are
assigned to thecorresponding formal parameters. If necessary, before assigning the value
of an actual parameter to a formal parameter, PL/SQL converts the datatypeof thevalue.
For example, if you pass a number when the procedure expects a string, PL/SQL converts
the parameter so that the procedure receives a string.
The actual parameter and its corresponding formal parameter must have compatible
datatypes. For instance, PL/SQL cannot convert between the DATE and REAL datatypes, or
convert a string to a number if the string contains extra characters such as dollar signs.
Example 8-4 Formal Parameters and Actual Parameters
The following procedure declares two formal parameters named emp_id and amount:
PROCEDURE raise_salary (emp_id INTEGER, amount REAL) IS
BEGIN
UPDATE emp SET sal = sal + amount WHERE empno = emp_id;
END raise_salary;
/
This procedure call specifies theactual parameters emp_num and amount:
raise_salary(emp_num, amount);
Expressions can be used as actual parameters:
raise_salary(emp_num, merit + cola);
Using Positional, Named, or Mixed Notation for Subprogram Parameters
When calling a subprogram, you can writethe actual parameters using either:
 Positional notation. You specify thesame parameters in the same order as they
are declared in theprocedure.
This notation is compact, but if you specify theparameters (especially literals) in
the wrong order, thebug can be hard to detect. You must change your code if the
procedure's parameter list changes.
 Named notation. You specify thename of each parameter along with its value.
An arrow (=>) serves as the association operator. The order of the parameters is
not significant.
This notation is more verbose, but makes your code easier to read and maintain.
You can sometimes avoid changing your code if the procedure's parameter list
changes, for example if theparameters are reordered or a new optional
parameter is added. Named notation is a good practice to use for any code that
calls someone else's API, or defines an API for someone else to use.
 Mixed notation. You specify the first parameters with positionalnotation, then
switch to named notation for the last parameters.
You can use this notation to call procedures that have some required parameters,
followed by some optionalparameters.
Example 8-5 Subprogram CallsUsing Positional, Named, and Mixed Notation
DECLARE
acct INTEGER := 12345;
amt REAL := 500.00;
PROCEDURE credit_acct (acct_no INTEGER, amount REAL) IS
BEGIN NULL; END;
BEGIN
-- The following calls are all equivalent.
credit_acct(acct, amt); -- positional
credit_acct(amount => amt, acct_no => acct); -- named
credit_acct(acct_no => acct, amount => amt); -- named
credit_acct(acct, amount => amt); -- mixed
END;
/
Specifying Subprogram Parameter Modes
You use parameter modes to define the behavior of formal parameters. Thethree
parameter modes are IN (the default), OUT, and IN OUT.
Any parameter mode can be used with any subprogram. Avoid using the OUT and IN OUT
modes with functions. To have a function return multiple values is a poor programming
practice. Also, functions should be free from side effects, which change the values of
variables not local to the subprogram.
Using the IN Mode
Page 58 of 77
An IN parameter lets you pass values to the subprogram being called. Inside the
subprogram, an IN parameter acts like a constant. It cannot be assigned a value.
You can pass a constant, literal, initialized variable, or expression as an IN parameter.
IN parameters can be initialized to default values, which are used if those parameters are
omitted from the subprogram call.
Using the OUT Mode
An OUT parameter returns a value to the caller of a subprogram. Inside the subprogram, an
OUT parameter acts like a variable. You can change its value, and reference thevalue after
assigning it:
PROCEDURE split_name
(
phrase IN VARCHAR2, first OUT VARCHAR2, last OUT VARCHAR2
)
IS
first := SUBSTR(phrase, 1, INSTR(phrase, ' ')-1);
last := SUBSTR(phrase, INSTR(phrase, ' ')+1);
IF first = 'John' THEN
DBMS_OUTPUT.PUT_LINE('That is a common first name.');
END IF;
END;
/
You must pass a variable, not a constant or an expression, to an OUT parameter. Its
previous value is lost unless you specify the NOCOPY or the subprogram exits with an
unhandled exception.
Like variables, OUT formal parameters are initialized to NULL. The datatypeof an OUT
formal parameter cannot be a subtypedefined as NOT NULL, such as the built-in subtypes
NATURALN and POSITIVEN. Otherwise, when you call thesubprogram, PL/SQL raises
VALUE_ERROR.
Before exiting a subprogram, assign values to all OUT formal parameters. Otherwise, the
corresponding actual parameters will be null. If you exit successfully, PL/SQL assigns
values to the actual parameters. If you exit with an unhandled exception, PL/SQL does not
assign values to the actual parameters.
Using the IN OUT Mode
An IN OUT parameter passes initial values to a subprogram and returns updated values to
the caller. It can be assigned a value and its value can be read. Typically, an IN OUT
parameter is a string buffer or numeric accumulator, that is read inside the subprogram and
then updated.
The actual parameter that corresponds to an IN OUT formal parameter must be a variable; it
cannot be a constant or an expression.
If you exit a subprogram successfully, PL/SQL assigns values to the actual parameters. If
you exit with an unhandled exception, PL/SQL does not assign values to theactual
parameters.
Summary of Subprogram Parameter Modes
Table 8-1 summarizes all you need to know about the parameter modes.
Table 8-1 Parameter Modes
IN OUT IN OUT
The default Must bespecified Must bespecified
Passes values to a
subprogram
Returns values to the caller Passes initial values to a
subprogram and returns
updated values to the caller
Formal parameter acts
like a constant
Formal parameter acts like
an uninitialized variable
Formal parameter acts like an
initialized variable
Formal parameter
cannot be assigneda
value
Formal parameter must be
assigned a value
Formal parameter should be
assigned a value
Actual parameter can be
a constant, initialized
variable, literal, or
expression
Actual parameter must be a
variable
Actual parameter must be a
variable
Actual parameter is
passed by reference (a
pointerto the value is
passed in)
Actual parameter is passed
by value (a copy of the
value is passed out) unless
NOCOPY is specified
Actual parameter is passed
by value (a copy of thevalue
is passed in and out) unless
NOCOPY is specified
Using Default Values for Subprogram Parameters
By initializing IN parameters to default values, you can pass different numbers of actual
parameters to a subprogram, accepting thedefault values for any parameters you omit.
You can also add new formal parameters without having to change every call to the
subprogram.
Example 8-6 Procedure with DefaultParameter Values
PROCEDURE create_dept (
new_dname VARCHAR2 DEFAULT 'TEMP',
new_loc VARCHAR2 DEFAULT 'TEMP') IS
BEGIN
NULL;
END;
/
If a parameter is omitted, the default value of its corresponding formal parameter is used.
Consider the following calls to create_dept:
create_dept; -- Same as create_dept('TEMP','TEMP');
create_dept('SALES'); -- Same as create_dept('SALES','TEMP');
create_dept('SALES', 'NY');
You cannot skip a formal parameter by leaving out its actual parameter. To omit the first
parameter and specify the second, use named notation:
create_dept(new_loc => 'NEW YORK');
You cannot assign a null to an uninitialized formal parameter by leaving out its actual
parameter. You must pass thenull explicitly, or you can specify a default value of NULL in
the declaration.
Overloading Subprogram Names
PL/SQL lets you overload subprogram names and typemethods. You can use the same
name for several different subprograms as long as their formal parameters differ in
number, order, or datatypefamily.
Supposeyou want to initialize the first n rows in two index-by tables that were declared as
follows:
DECLARE
TYPE DateTabTyp IS TABLE OF DATE INDEX BY BINARY_INTEGER;
TYPE RealTabTyp IS TABLE OF REAL INDEX BY BINARY_INTEGER;
hiredate_tab DateTabTyp;
sal_tab RealTabTyp;
Page 59 of 77
BEGIN
NULL;
END;
/
You might writea procedure to initialize one kind of collection:
PROCEDURE initialize (tab OUT DateTabTyp, n INTEGER) IS
BEGIN
FOR i IN 1..n LOOP
tab(i) := SYSDATE;
END LOOP;
END initialize;
/
You might also write a procedure to initialize another kind of collection:
PROCEDURE initialize (tab OUT RealTabTyp, n INTEGER) IS
BEGIN
FOR i IN 1..n LOOP
tab(i) := 0.0;
END LOOP;
END initialize;
/
Because the processing in these two procedures is thesame, it is logical to give them the
same name.
You can place thetwo overloaded initialize procedures in the same block, subprogram,
package, or object type. PL/SQLdetermines which procedure to call by checking their
formal parameters. In thefollowing example, theversion of initialize that PL/SQLuses
depends on whether you call the procedure with a DateTabTyp or RealTabTyp parameter:
DECLARE
TYPE DateTabTyp IS TABLE OF DATE INDEX BY BINARY_INTEGER;
TYPE RealTabTyp IS TABLE OF REAL INDEX BY BINARY_INTEGER;
hiredate_tab DateTabTyp;
comm_tab RealTabTyp;
indx BINARY_INTEGER;
PROCEDURE initialize (tab OUT DateTabTyp, n INTEGER) IS
BEGIN
NULL;
END;
PROCEDURE initialize (tab OUT RealTabTyp, n INTEGER) IS
BEGIN
NULL;
END;
BEGIN
indx := 50;
initialize(hiredate_tab, indx); -- calls first version
initialize(comm_tab, indx); -- calls second version
END;
/
Using Invoker's Rights Versus Definer's Rights (AUTHID
Clause)
By default, stored procedures and SQL methods execute with the privileges of their owner,
not their current user. Such definer's rights subprograms are bound to theschema in which
they reside, allowing you to refer to objects in the same schema without qualifying their
names. For example, if schemas SCOTT and BLAKE both have a table called dept, a
procedure owned by SCOTT can refer to dept rather than SCOTT.DEPT. If user BLAKE calls
SCOTT's procedure, the procedure still accesses the dept table owned by SCOTT.
If you compile thesame procedure in both schemas, you can define the schema name as a
variable in SQL*Plus and refer to the table like &schema..dept. Thecode is portable, but if
you change it, you must recompile it in each schema.
A more maintainable way is to use the AUTHID clause, which makes stored procedures and
SQL methods execute with the privileges and schema context of the calling user. You can
create one instance of the procedure, and many users can call it to access their own data.
Such invoker's rights subprograms are not bound to a particular schema. The following
version of procedure create_dept executes with the privileges of the calling user and inserts
rows into that user's dept table:
CREATE PROCEDURE create_dept (
my_deptno NUMBER,
my_dname VARCHAR2,
my_loc VARCHAR2) AUTHID CURRENT_USER AS
BEGIN
INSERT INTO dept VALUES (my_deptno, my_dname, my_loc);
END;
/
Advantages of Invoker's Rights
Invoker's rights subprograms let you reuse code and centralize application logic. They are
especially useful in applications that storedata using identical tables in different schemas.
All the schemas in one instance can call procedures owned by a central schema. You can
even have schemas in different instances call centralized procedures using a database link.
Consider a company that uses a stored procedure to analyze sales. If the company has
several schemas, each with a similar SALES table, normally it would also need several
copies of thestored procedure, one in each schema.
To solve theproblem, the company installs an invoker's rights version of thestored
procedure in a central schema. Now, all theother schemas can call thesame procedure,
which queries the appropriateto SALES table in each case.
You can restrict access to sensitive data by calling from an invoker's rights subprogram to
a definer's rights subprogram that queries or updates thetable containing the sensitive data.
Although multiple users can call the invoker's rights subprogram, they do not have direct
access to the sensitive data.
Specifying the Privileges for a Subprogram with the AUTHID Clause
To implement invoker's rights, use the AUTHID clause, which specifies whether a
subprogram executes with the privileges of its owner or its current user. It also specifies
whether external references (that is, references to objects outsidethe subprogram) are
resolved in the schema of theowner or the current user.
The AUTHID clause is allowed only in the header of a standalone subprogram, a package
spec, or an object typespec. In the CREATE FUNCTION, CREATE PROCEDURE, CREATE
PACKAGE, or CREATE TYPE statement, you can include either AUTHID CURRENT_USER or
AUTHID DEFINER immediately before the IS or AS keyword that begins the declaration
section.
DEFINER is the default option. In a package or object type, the AUTHID clause applies to all
subprograms.
Note: Most supplied PL/SQL packages (such as DBMS_LOB, DBMS_PIPE, DBMS_ROWID,
DBMS_SQL, and UTL_REF) are invoker's rights packages.
Who Is the Current User During Subprogram Execution?
In a sequence of calls, whenever control is inside an invoker's rights subprogram, the
current user is the session user. When a definer's rights subprogram is called, theowner of
Page 60 of 77
that subprogram becomes the current user. The current user might change as new
subprograms are called or as subprograms exit.
To verify who thecurrent user is at any time, you can check the USER_USERS data
dictionary view. Inside an invoker's rights subprogram, the value from this view might be
different from thevalue of the USER built-in function, which always returns the name of
the session user.
How External References Are Resolved in Invoker's Rights Subprograms
If you specify AUTHID CURRENT_USER, theprivileges of thecurrent user are checked at run
time, and external references are resolved in the schema of the current user. However, this
applies only to external references in:
 SELECT, INSERT, UPDATE, and DELETE data manipulation statements
 The LOCK TABLE transaction control statement
 OPEN and OPEN-FOR cursor control statements
 EXECUTE IMMEDIATE and OPEN-FOR-USING dynamic SQL statements
 SQL statements parsed using DBMS_SQL.PARSE()
For all other statements, the privileges of the owner are checked at compile time, and
external references are resolved in theschema of the owner. For example, the assignment
statement below refers to the packaged function balance. This external reference is resolved
in the schema of the owner of procedure reconcile.
CREATE PROCEDURE reconcile (acc_id IN INTEGER)
AUTHID CURRENT_USER AS
bal NUMBER;
BEGIN
bal := bank_ops.balance(acct_id);
...
END;
/
Overriding Default Name Resolution in Invoker's Rights Subprograms
Occasionally, you might want an unqualified name to refer to some particular schema, not
the schema of thecaller. In the same schema as the invoker's rights subprogram, create a
public synonymfor the table, procedure, function, or other object using the CREATE
SYNONYM statement:
CREATE PUBLIC SYNONYM emp FOR hr.employees;
When theinvoker's rights subprogram refers to this name, it will match the synonymin its
own schema, which resolves to theobject in the specified schema. This technique does not
work if the calling schema already has a schema object or private synonymwith thesame
name. In that case, theinvoker's rights subprogram must fully qualify thereference.
Granting Privileges on Invoker's Rights Subprograms
To call a subprogram directly, users must have the EXECUTE privilege on that subprogram.
By granting the privilege, you allow a user to:
 Call the subprogram directly
 Compile functions and procedures that call the subprogram
For external references resolved in thecurrent user's schema (such as those in DML
statements), thecurrent user must have the privileges needed to access schema objects
referenced by thesubprogram. For all other external references (such as function calls), the
owner's privileges are checked at compile time, and no run-time check is done.
A definer's rights subprogram operates under thesecurity domain of its owner, no matter
who is executing it. The owner must have theprivileges needed to access schema objects
referenced by thesubprogram.
You can write a program consisting of multiple subprograms, some with definer's rights
and others with invoker's rights. Then, you can use the EXECUTE privilege to restrict
program entry points. That way, users of an entry-point subprogramcan execute theother
subprograms indirectly but not directly.
Granting Privileges on an Invoker's Rights Subprogram: Example
Supposeuser UTIL grants the EXECUTE privilege on subprogram FFT to user APP:
GRANT EXECUTE ON util.fft TO app;
Now, user APP can compile functions and procedures that call subprogram FFT. At run
time, no privilege checks on thecalls are done. As Figure 8-2 shows, user UTIL need not
grant theEXECUTE privilege to every user who might call FFT indirectly.
Since subprogram util.fft is called directly only from invoker's rights subprogram app.entry,
user util must grant the EXECUTE privilege only to user APP. When UTIL.FFT is executed, its
current user could be APP, SCOTT, or BLAKE even though SCOTT and BLAKE were not
granted the EXECUTE privilege.
Figure 8-2 Indirect Calls to an Invoker's Rights Subprogram
Description of the illustration lnpls026.gif
Using Roles with Invoker's Rights Subprograms
The use of roles in a subprogram depends on whether it executes with definer's rights or
invoker's rights. Within a definer's rights subprogram, all roles are disabled. Roles are not
used for privilege checking, and you cannot set roles.
Within an invoker's rights subprogram, roles are enabled (unless the subprogram was
called directly or indirectly by a definer's rights subprogram). Roles are used for privilege
checking, and you can use native dynamic SQL to set roles for thesession. However, you
cannot use roles to grant privileges on templateobjects because roles apply at run time, not
at compile time.
Using Views and Database Triggers with Invoker's Rights Subprograms
For invoker's rights subprograms executed within a view expression, the schema that
created theview, not the schema that is querying the view, is considered to be the current
user.
This rule also applies to database triggers.
Page 61 of 77
Using Database Links with Invoker's Rights Subprograms
You can create a database link to use invoker's rights:
CREATE DATABASE LINK link_name CONNECT TO CURRENT_USER
USING connect_string;
A current-user link lets you connect to a remote database as another user, with that user's
privileges. To connect, Oracle uses theusername of the current user (who must be a global
user). Supposean invoker's rights subprogram owned by user BLAKE references the
database link below. If global user SCOTT calls the subprogram, it connects to the Dallas
database as user SCOTT, who is thecurrent user.
CREATE DATABASE LINK dallas CONNECT TO CURRENT_USER USING ...
If it were a definer's rights subprogram, the current user would be BLAKE, and the
subprogram would connect to the Dallas database as global user BLAKE.
Using Object Types with Invoker's Rights Subprograms
To define object types for usein any schema, specify the AUTHID CURRENT_USER clause.
Supposeuser BLAKE creates the following object type:
CREATE TYPE NumAUTHID CURRENT_USER AS OBJECT (
x NUMBER,
STATIC PROCEDURE new_num(
n NUMBER, schema_name VARCHAR2, table_name VARCHAR2)
);
/
CREATE TYPE BODY NumAS
STATIC PROCEDURE new_num(
n NUMBER, schema_name VARCHAR2, table_name VARCHAR2) IS
sql_stmt VARCHAR2(200);
BEGIN
sql_stmt := 'INSERT INTO ' || schema_name || '.'
|| table_name || ' VALUES (blake.Num(:1))';
EXECUTE IMMEDIATE sql_stmt USING n;
END;
END;
/
Then, user BLAKE grants the EXECUTE privilege on object type Num to user SCOTT:
GRANT EXECUTE ON Num TO scott;
Finally, user SCOTT creates an object table to store objects of type Num, then calls
procedure new_num to populatethetable:
CONNECT scott/tiger;
CREATE TABLE num_tab OF blake.Num;
/
BEGIN
blake.Num.new_num(1001, 'scott', 'num_tab');
blake.Num.new_num(1002, 'scott', 'num_tab');
blake.Num.new_num(1003, 'scott', 'num_tab');
END;
/
The calls succeed because theprocedure executes with the privileges of its current user
(SCOTT), not its owner (BLAKE).
For subtypes in an object typehierarchy, thefollowing rules apply:
 If a subtypedoes not explicitly specify an AUTHID clause, it inherits the AUTHID
of its supertype.
 If a subtypedoes specify an AUTHID clause, its AUTHID must match the AUTHID of
its supertype. Also, if the AUTHID is DEFINER, both the supertypeand subtype
must have been created in thesame schema.
Calling Invoker's Rights Instance Methods
An invoker's rights instance method executes with theprivileges of theinvoker, not the
creator of the instance. Supposethat Person is an invoker's rights object type, and that user
SCOTT creates p1, an object of typePerson. If user BLAKE calls instance method change_job to
operateon object p1, the current user of themethod is BLAKE, not SCOTT. Consider the
following example:
-- user blake creates a definer-rights procedure
CREATE PROCEDURE reassign (p Person, new_job VARCHAR2) AS
BEGIN
-- user blake calls method change_job, so the
-- method executes with the privileges ofblake
p.change_job(new_job);
...
END;
/
-- user scott passes a Person object to the procedure
DECLARE
p1 Person;
BEGIN
p1 := Person(...);
blake.reassign(p1, 'CLERK');
...
END;
/
Using Recursion with PL/SQL
Recursion is a powerfultechnique for simplifying the design of algorithms. Basically,
recursion means self-reference. In a recursive mathematical sequence, each term is derived
by applyinga formula to preceding terms. TheFibonacci sequence (0, 1, 1, 2, 3, 5, 8, 13,
21, ...), is an example. Each term in the sequence (after the second) is the sum of thetwo
terms that immediately precede it.
In a recursive definition, something is defined as simpler versions of itself. Consider the
definition of n factorial (n!), theproduct of all integers from 1 to n:
n! = n * (n - 1)!
What Is a Recursive Subprogram?
A recursive subprogram is one that calls itself. Each recursive call creates a new instance
of any items declared in the subprogram, including parameters, variables, cursors, and
exceptions. Likewise, new instances of SQL statements are created at each level in the
recursive descent.
Be careful where you place a recursive call. If you place it inside a cursor FOR loop or
between OPEN and CLOSE statements, another cursor is opened at each call, which might
exceed the limit set by the Oracle initialization parameter OPEN_CURSORS.
There must be at least two paths through a recursive subprogram: one that leads to the
recursive call and one that does not. At least one path must lead to a terminating condition.
Otherwise, the recursion would go on until PL/SQL runs out of memory and raises the
predefined exception STORAGE_ERROR.
Calling External Subprograms
Page 62 of 77
Although PL/SQL is a powerful, flexible language, some tasks are more easily done in
another language. Low-level languages such as C are very fast. Widely used languages
such as Java have reusable libraries for common design patterns.
You can use PL/SQL call specs to invoke external subprograms written in other
languages, making their capabilities and libraries available from PL/SQL.
For example, you can call Java stored procedures from any PL/SQL block, subprogram, or
package. Supposeyou storethe following Java class in thedatabase:
import java.sql.*;
import oracle.jdbc.driver.*;
public class Adjuster {
public static void raiseSalary (int empNo, float percent)
throws SQLException {
Connection conn = new OracleDriver().defaultConnection();
String sql = "UPDATE emp SET sal = sal * ? WHERE empno = ?";
try {
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setFloat(1, (1 + percent / 100));
pstmt.setInt(2, empNo);
pstmt.executeUpdate();
pstmt.close();
} catch (SQLException e) {System.err.println(e.getMessage());}
}
}
The class Adjuster has one method, which raises the salary of an employee by a given
percentage. Because raiseSalary is a void method, you publish it as a procedure using this call
spec:
CREATE PROCEDURE raise_salary (empno NUMBER, pct NUMBER)
AS LANGUAGE JAVA
NAME 'Adjuster.raiseSalary(int, float)';
You might call procedure raise_salary from an anonymous PL/SQL block:
DECLARE
emp_id NUMBER;
percent NUMBER;
BEGIN
-- get values for emp_id and percent
raise_salary(emp_id, percent); -- call external subprogram
END;
/
External C subprograms are used to interface with embedded systems, solve engineering
problems, analyzedata, or control real-time devices and processes. External C
subprograms extend the functionality of the database server, and move computation-bound
programs from client to server, where they execute faster.
9 - Using PL/SQL Packages
This chapter shows how to bundle related PL/SQL code and data into a package. The
package might include a set of procedures that forms an API, or a poolof typedefinitions
and variable declarations. The package is compiled and stored in thedatabase, where its
contents can be shared by many applications.
What Is a PL/SQL Package?
A package is a schema object that groups logically related PL/SQL types, variables, and
subprograms. Packages usually have two parts, a specification and a body;sometimes the
body is unnecessary. Thespecification (specfor short) is the interface to the package. It
declares the types, variables, constants, exceptions, cursors, and subprograms that can be
referenced from outside thepackage. The body defines the queries for thecursors and the
code for thesubprograms.
The specholds public declarations, which are visible to stored procedures and other code
outside thepackage. You must declare subprograms at the end of the specafter all other
items (except pragmas that name a specific function; such pragmas must follow the
function spec).
The body holds implementation details and private declarations, which are hidden from
code outsidethe package. Following thedeclarative part of thepackage body is the
optionalinitialization part, which holds statements that initialize package variables and do
any other one-time setup steps.
The AUTHID clause determines whether all thepackaged subprograms execute with the
privileges of their definer (the default) or invoker, and whether their unqualified references
to schema objects are resolved in the schema of thedefiner or invoker. A call spec lets you
map a package subprogram to a Java method or external C function. The call spec maps
the Java or C name, parameter types, and return typeto their SQL counterparts.
What Goes In a PL/SQL Package?
 "Get" and "Set" methods for the package variables, if you want to avoid letting
other procedures read and write them directly.
 Cursor declarations with the text of SQL queries. Reusing exactly the same
query text in multiple locations is faster than retypingthe same query each time
with slight differences. It is also easier to maintain if you need to change a query
that is used in many places.
 Declarations for exceptions. Typically, you need to be able to reference these
from different procedures, so that you can handle exceptions within called
subprograms.
 Declarations for procedures and functions that call each other. You do not need
to worry about compilation order for packaged procedures and functions,
making them more convenient than standalone stored procedures and functions
when they call back and forth to each other.
 Declarations for overloaded procedures and functions. You can create multiple
variations of a procedure or function, using thesame names but different sets of
parameters.
 Variables that you want to remain available between procedure calls in thesame
session. You can treat variables in a package like global variables.
 Typedeclarations for PL/SQL collection types. To pass acollection as a
parameter between stored procedures or functions, you must declare thetypein a
package so that both the calling and called subprogram can refer to it.
Example of a PL/SQL Package
The example below packages a record type, acursor, and two employment procedures.
The procedure hire_employee uses thesequence empno_seq and the function SYSDATE to insert
a new employee number and hire date.
CREATE OR REPLACE PACKAGE emp_actions AS -- spec
TYPE EmpRecTyp IS RECORD (emp_id INT, salary REAL);
CURSOR desc_salary RETURN EmpRecTyp;
PROCEDURE hire_employee (
Page 63 of 77
ename VARCHAR2,
job VARCHAR2,
mgr NUMBER,
sal NUMBER,
comm NUMBER,
deptno NUMBER);
PROCEDURE fire_employee (emp_id NUMBER);
END emp_actions;
/
CREATE OR REPLACE PACKAGE BODY emp_actions AS -- body
CURSOR desc_salary RETURN EmpRecTyp IS
SELECT empno, sal FROM emp ORDER BY sal DESC;
PROCEDURE hire_employee (
ename VARCHAR2,
job VARCHAR2,
mgr NUMBER,
sal NUMBER,
comm NUMBER,
deptno NUMBER) IS
BEGIN
INSERT INTO emp VALUES (empno_seq.NEXTVAL, ename, job,
mgr, SYSDATE, sal, comm, deptno);
END hire_employee;
PROCEDURE fire_employee (emp_id NUMBER) IS
BEGIN
DELETE FROM emp WHERE empno = emp_id;
END fire_employee;
END emp_actions;
/
Only the declarations in the package specare visible and accessible to applications.
Implementation details in thepackage body are hidden and inaccessible. You can change
the body (implementation) without having to recompile calling programs.
Advantages of PL/SQL Packages
Packages have a long history in softwareengineering, offering important features for
reliable, maintainable, reusable code, often in team development efforts for large systems.
Modularity
Packages let you encapsulate logically related types, items, and subprograms in a named
PL/SQL module. Each package is easy to understand, and the interfaces between packages
are simple, clear, and well defined. This aids application development.
Easier Application Design
When designing an application, all you need initially is the interface information in the
package specs. You can code and compile a specwithout its body. Then, stored
subprograms that reference thepackage can be compiled as well. You need not define the
package bodies fully until you are ready to complete the application.
Information Hiding
With packages, you can specify which types, items, and subprograms are public (visible
and accessible) or private(hidden and inaccessible). For example, if a package contains
four subprograms, three might be public and one private. The package hides the
implementation of the privatesubprogram so that only thepackage (not your application)
is affected if the implementation changes. This simplifies maintenance and enhancement.
Also, by hiding implementation details from users, you protect theintegrity of the
package.
Added Functionality
Packaged public variables and cursors persist for theduration of a session. They can be
shared by all subprograms that execute in the environment. They let you maintain data
across transactions without storing it in the database.
Better Performance
When you call a packaged subprogram for the first time, the whole package is loaded into
memory. Later calls to related subprograms in thepackage require no disk I/O.
Packages stop cascading dependencies and avoid unnecessary recompiling. For example, if
you change the body of a packaged function, Oracle does not recompile other subprograms
that call the function; these subprograms only depend on the parameters and return value
that are declared in thespec, so they are only recompiled if the specchanges.
Understanding The Package Specification
The package specification contains public declarations. The declared items are accessible
from anywhere in the package and to any other subprograms in the same schema. Figure 9-
1 illustrates thescoping.
Figure 9-1 Package Scope
Description of the illustration lnpls014.gif
The speclists the package resources available to applications. All theinformation your
application needs to use the resources is in the spec. For example, thefollowing
declaration shows that the function named fac takes one argument of type INTEGER and
returns a value of type INTEGER:
FUNCTION fac (n INTEGER) RETURN INTEGER; -- returns n!
That is all the information you need to call thefunction. You need not consider its
underlying implementation (whether it is iterative or recursive for example).
If a spec declares only types, constants, variables, exceptions, and call specs, thepackage
body is unnecessary. Only subprograms and cursors have an underlying implementation.
In the following example, the package needs no body because it declares types, exceptions,
and variables, but no subprograms or cursors. Such packages let you define global
variables—usable by stored procedures and functions and triggers—that persist throughout
a session.
CREATE PACKAGE trans_data AS -- bodiless package
TYPE TimeRec IS RECORD (
Page 64 of 77
minutes SMALLINT,
hours SMALLINT);
TYPE TransRec IS RECORD (
category VARCHAR2,
account INT,
amount REAL,
time_of TimeRec);
minimum_balance CONSTANT REAL := 10.00;
number_processed INT;
insufficient_funds EXCEPTION;
END trans_data;
/
Package Body
The package body has the codes for various methods declared in the package specification
and other privatedeclarations, which are hidden from code outside thepackage.
The CREATEPACKAGEBODY Statement is used for creating thepackage body. The
following code snippet shows thepackage body declaration for the cust_sal package
created above.
CREATE OR REPLACE PACKAGE BODY cust_sal AS
PROCEDURE find_sal(c_id customers.id%TYPE) IS
c_sal customers.salary%TYPE;
BEGIN
SELECT salary INTO c_sal
FROM customers
WHERE id = c_id;
dbms_output.put_line('Salary: '|| c_sal);
END find_sal;
END cust_sal;
/
When theabove code is executed at SQL prompt, it produces following result:
Package body created.
Using the Package Elements
The package elements ( variables, procedures or functions) are accessed with the following
syntax:
package_name.element_name;
Consider, we already have created above package in our database schema, thefollowing
program uses the find_sal method of thecust_sal package:
DECLARE
code customers.id%type := &cc_id;
BEGIN
cust_sal.find_sal(code);
END;
/
When theabove code is executed at SQL prompt, it prompt to enter customer ID and when
you enter an ID, it displays corresponding salary as follows:
Enter value for cc_id: 1
Salary: 3000
PL/SQL procedure successfully completed.
Example:
The following program provides a more complete package. We will use the CUSTOMERS
table stored in our database with thefollowing records:
Select * from customers;
+----+----------+-----+-----------+----------+
| ID | NAME | AGE | ADDRESS | SALARY |
+----+----------+-----+-----------+----------+
| 1 | Ramesh | 32 | Ahmedabad | 3000.00 |
| 2 | Khilan | 25 | Delhi | 3000.00 |
| 3 | kaushik | 23 | Kota | 3000.00 |
| 4 | Chaitali | 25 | Mumbai | 7500.00 |
| 5 | Hardik | 27 | Bhopal | 9500.00 |
| 6 | Komal | 22 | MP | 5500.00 |
+----+----------+-----+-----------+----------+
The package specification:
CREATE OR REPLACE PACKAGE c_package AS
-- Adds a customer
PROCEDURE addCustomer(c_id customers.id%type,
c_name customers.name%type,
c_age customers.age%type,
c_addr customers.address%type,
c_sal customers.salary%type);
-- Removes a customer
PROCEDURE delCustomer(c_id customers.id%TYPE);
--Lists all customers
PROCEDURE listCustomer;
END c_package;
/
When theabove code is executed at SQL prompt, it creates the above package and
displays following result:
Package created.
Creatingthe package body:
CREATE OR REPLACE PACKAGE BODY c_package AS
PROCEDURE addCustomer(c_id customers.id%type,
c_name customers.name%type,
c_age customers.age%type,
c_addr customers.address%type,
c_sal customers.salary%type)
IS
BEGIN
INSERT INTO customers (id,name,age,address,salary)
VALUES(c_id, c_name, c_age, c_addr, c_sal);
END addCustomer;
PROCEDURE delCustomer(c_id customers.id%type) IS
BEGIN
DELETE FROM customers
WHERE id = c_id;
END delCustomer;
PROCEDURE listCustomer IS
CURSOR c_customers is
SELECT name FROM customers;
TYPE c_list is TABLE OF customers.name%type;
name_list c_list := c_list();
counter integer :=0;
BEGIN
FOR n IN c_customers LOOP
counter := counter +1;
name_list.extend;
name_list(counter) := n.name;
dbms_output.put_line('Customer(' ||counter|| ')'||name_list(counter));
END LOOP;
END listCustomer;
Page 65 of 77
END c_package;
/
Above example makes use of nestedtable which we will discuss in the next chapter.
When theabove code is executed at SQL prompt, it produces following result:
Package body created.
Usingthe Package:
The following program uses themethods declared and defined in the package c_package.
DECLARE
code customers.id%type:= 8;
BEGIN
c_package.addcustomer(7, 'Rajnish', 25, 'Chennai', 3500);
c_package.addcustomer(8, 'Subham', 32, 'Delhi', 7500);
c_package.listcustomer;
c_package.delcustomer(code);
c_package.listcustomer;
END;
/
When theabove code is executed at SQL prompt, it produces following result:
Customer(1): Ramesh
Customer(2): Khilan
Customer(3): kaushik
Customer(4): Chaitali
Customer(5): Hardik
Customer(6): Komal
Customer(7): Rajnish
Customer(8): Subham
Customer(1): Ramesh
Customer(2): Khilan
Customer(3): kaushik
Customer(4): Chaitali
Customer(5): Hardik
Customer(6): Komal
Customer(7): Rajnish
PL/SQL procedure successfully completed
Referencing Package Contents
To reference the types, items, subprograms, and call specs declared within a package spec,
use dot notation:
package_name.type_name
package_name.item_name
package_name.subprogram_name
package_name.call_spec_name
You can reference package contents from database triggers, stored subprograms, 3GL
application programs, and various Oracle tools. For example, you might call the packaged
procedure hire_employee from SQL*Plus, as follows:
CALL emp_actions.hire_employee('TATE', 'CLERK', ...);
The following example calls the same procedure from an anonymous block in a Pro*C
program. Theactual parameters emp_name and job_title arehost variables.
EXEC SQL EXECUTE
BEGIN
emp_actions.hire_employee(:emp_name, :job_title, ...);
Restrictions
You cannot reference remote packaged variables, either directly or indirectly. For
example, you cannot call the a procedure through a database link if the procedure refers to
a packaged variable.
Inside a package, you cannot reference host variables.
Understanding The Package Body
The package body contains the implementation of every cursor and subprogram declared
in the package spec. Subprograms defined in a package body are accessible outside the
package only if their specs also appear in thepackage spec. If a subprogram specis not
included in thepackage spec, that subprogram can only be called by other subprograms in
the same package.
To match subprogram specs and bodies, PL/SQL does a token-by-token comparison of
their headers. Except for white space, the headers must match word for word. Otherwise,
PL/SQL raises an exception, as the following example shows:
CREATE PACKAGE emp_actions AS
...
PROCEDURE calc_bonus (date_hired emp.hiredate%TYPE, ...);
END emp_actions;
/
CREATE PACKAGE BODY emp_actions AS
...
PROCEDURE calc_bonus (date_hired DATE, ...) IS
-- parameter declaration raises an exception because 'DATE'
-- does not match 'emp.hiredate%TYPE' word for word
BEGIN ... END;
END emp_actions;
/
The package body can also contain private declarations, which define types and items
necessary for theinternal workings of thepackage. Thescope of thesedeclarations is local
to the package body. Therefore, thedeclared types and items are inaccessible except from
within thepackage body. Unlike a package spec, the declarative part of a package body
can contain subprogram bodies.
Following the declarative part of a package body is the optionalinitialization part, which
typically holds statements that initialize some of the variables previously declared in the
package.
The initialization part of a package plays aminor role because, unlike subprograms, a
package cannot be called or passed parameters. As a result, the initialization part of a
package is run only once, the first time you reference the package.
Remember, if a package specdeclares only types, constants, variables, exceptions, and call
specs, the package body is unnecessary. However, thebody can still be used to initialize
items declared in the package spec.
Private Versus Public Items in Packages
In the package emp_actions, the package body declares a variable named number_hired, which
is initialized to zero. Items declared in thebody are restricted to use within thepackage.
PL/SQL code outside the package cannot reference the variable number_hired. Such items are
called private.
Items declared in thespec of emp_actions, such as the exception invalid_salary, arevisible
outside thepackage. Any PL/SQL code can reference the exception invalid_salary. Such
items are called public.
To maintain items throughout a session or across transactions, place them in the
declarative part of the package body. For example, thevalue of number_hired is kept between
calls to hire_employee within thesame session. Thevalue is lost when thesession ends.
Page 66 of 77
To make the items public, place them in thepackage spec. For example, the constant
minimum_balance declared in the specof the package bank_transactions is available for general
use.
Overloading Packaged Subprograms
PL/SQL allows two or more packaged subprograms to have the same name. This option is
useful when you want a subprogram to accept similar sets of parameters that have different
datatypes. For example, thefollowing package defines two procedures named journalize:
CREATE PACKAGE journal_entries AS
...
PROCEDURE journalize (amount REAL, trans_date VARCHAR2);
PROCEDURE journalize (amount REAL, trans_date INT);
END journal_entries;
/
CREATE PACKAGE BODY journal_entries AS
...
PROCEDURE journalize (amount REAL, trans_date VARCHAR2) IS
BEGIN
INSERT INTO journal
VALUES (amount, TO_DATE(trans_date, 'DD-MON-YYYY'));
END journalize;
PROCEDURE journalize (amount REAL, trans_date INT) IS
BEGIN
INSERT INTO journal
VALUES (amount, TO_DATE(trans_date, 'J'));
END journalize;
END journal_entries;
/
The first procedure accepts trans_date as a character string, while the second procedure
accepts it as a number (the Julian day). Each procedure handles the data appropriately.
Overview of Product-Specific Packages
Oracle and various Oracle tools are supplied with product-specificpackages that define
APIs you can call from PL/SQL, SQL, Java, or other programming environments. Here we
mention a few of themore widely used ones.
About the DBMS_ALERT Package
Package DBMS_ALERT lets you use database triggers to alert an application when specific
database values change. The alerts are transaction based and asynchronous (that is, they
operateindependently of any timing mechanism). For example, a company might use this
package to updatethevalue of its investment portfolio as new stock and bond quotes
arrive.
About the DBMS_OUTPUT Package
Package DBMS_OUTPUT enables you to display output from PL/SQL blocks and
subprograms, which makes it easier to test and debug them. The procedure put_line outputs
information to a buffer in the SGA. You display the information by calling the procedure
get_line or by setting SERVEROUTPUT ON in SQL*Plus. For example, supposeyou create the
following stored procedure:
CREATE OR REPLACE PROCEDURE list_tables AS
BEGIN
dbms_output.put_line('These are the tables you own:');
FOR itemIN (SELECT table_name FROM user_tables)
LOOP
dbms_output.put_line(item.table_name);
END LOOP;
END;
/
When you issue thefollowing commands, SQL*Plus displays theoutput fromthe
procedure:
SQL> SET SERVEROUTPUT ON
SQL> EXEC list_tables;
If the output is long, you might need to issue SET SERVEROUTPUT ON SIZE 1000000 to usea
bigger output buffer.
About the DBMS_PIPE Package
Package DBMS_PIPE allows different sessions to communicate over named pipes. (A pipe is
an area of memory used by one process to pass information to another.) You can use the
procedures pack_message and send_message to pack a message into a pipe, then send it to
another session in the same instance or to a waiting application such as a UNIX program.
At the other end of thepipe, you can use theprocedures receive_message and unpack_message to
receive and unpack (read) the message. Named pipes are useful in many ways. For
example, you can write a C program to collect data, then send it through pipes to stored
procedures in an Oracle database.
About the UTL_FILE Package
Package UTL_FILE lets PL/SQL programs read and writeoperating system(OS) text files. It
provides a restricted version of standard OS stream file I/O, including open, put, get, and
close operations.
When you want to read or write a text file, you call the function fopen, which returns a file
handle for use in subsequent procedure calls. For example, the procedure put_line writes a
text string and line terminator to an open file, and the procedure get_line reads a line of text
from an open file into an output buffer.
About the UTL_HTTP Package
Package UTL_HTTP allows your PL/SQL programs to make hypertext transfer protocol
(HTTP) callouts. It can retrieve data from the Internet or call Oracle Web Server
cartridges. The package has two entry points, each of which accepts a URL (uniform
resource locator) string, contacts thespecified site, and returns the requested data, which is
usually in hypertext markup language (HTML) format.
10 - Handling PL/SQL Errors
Run-time errors arise from design faults, coding mistakes, hardware failures, and many
other sources. Although you cannot anticipate all possibleerrors, you can plan to handle
certain kinds of errors meaningful to your PL/SQL program.
With many programming languages, unless you disable error checking, a run-time error
such as stack overflow or division by zero stops normal processing and returns control to
the operating system. With PL/SQL, a mechanism called exception handling lets you
"bulletproof" your program so that it can continue operating in the presence of errors.
Overview of PL/SQL Runtime Error Handling
In PL/SQL, an error condition is called an exception. Exceptions can be internally defined
(by the runtime system) or user defined. Examples of internally defined exceptions include
division by zero and out of memory. Some common internal exceptions have predefined
Page 67 of 77
names, such as ZERO_DIVIDE and STORAGE_ERROR. The other internal exceptions can be
given names.
When an error occurs, an exception is raised. That is, normal execution stops and control
transfers to theexception-handling part of your PL/SQL block or subprogram. Internal
exceptions are raised implicitly (automatically) by therun-time system. User-defined
exceptions must be raised explicitly by RAISE statements, which can also raise predefined
exceptions.
To handle raised exceptions, you write separateroutines called exception handlers. After
an exception handler runs, thecurrent block stops executing and theenclosing block
resumes with thenext statement. If there is no enclosing block, control returns to the host
environment.
The following example calculates a price-to-earnings ratio for a company. If the company
has zero earnings, the division operation raises the predefined exception ZERO_DIVIDE, the
execution of theblock is interrupted, and control is transferred to the exception handlers.
The optionalOTHERS handler catches all exceptions that the block does not name
specifically.
SET SERVEROUTPUT ON;
DECLARE
stock_price NUMBER := 9.73;
net_earnings NUMBER := 0;
pe_ratio NUMBER;
BEGIN
-- Calculation might cause division-by-zero error.
pe_ratio := stock_price / net_earnings;
dbms_output.put_line('Price/earnings ratio = ' || pe_ratio);
EXCEPTION -- exception handlers begin
-- Only one ofthe WHEN blocks is executed.
WHEN ZERO_DIVIDE THEN -- handles 'division by zero' error
dbms_output.put_line('Company must have had zero earnings.');
pe_ratio := null;
WHEN OTHERS THEN -- handles all other errors
dbms_output.put_line('Some other kind oferror occurred.');
pe_ratio := null;
END; -- exception handlers and block end here
/
The last example illustrates exception handling. With some better error checking, we could
have avoided theexception entirely, by substitutinga null for theanswer if the
denominator was zero:
DECLARE
stock_price NUMBER := 9.73;
net_earnings NUMBER := 0;
pe_ratio NUMBER;
BEGIN
pe_ratio :=
case net_earnings
when 0 then null
else stock_price / net_earnings
end;
END;
/
Advantages of PL/SQL Exceptions
Using exceptions for error handling has several advantages.
With exceptions, you can reliably handle potentialerrors from many statements with a
single exception handler:
BEGIN
SELECT ...
SELECT ...
procedure_that_performs_select();
...
EXCEPTION
WHEN NO_DATA_FOUND THEN -- catches all 'no data found' errors
Instead of checking for an error at every point it might occur, just add an exception handler
to your PL/SQL block. If the exception is ever raised in that block (or any sub-block), you
can be sure it will be handled.
Sometimes the error is not immediately obvious, and could not be detected until later when
you perform calculations using bad data. Again, a single exception handler can trap all
division-by-zero errors, bad array subscripts, and so on.
If you need to check for errors at a specific spot, you can enclose a single statement or a
group of statements inside its own BEGIN-END block with its own exception handler.
You can make the checking as general or as precise as you like.
Isolating error-handling routines makes therest of theprogram easier to read and
understand.
Summary of Predefined PL/SQL Exceptions
You can use the pragma EXCEPTION_INIT to associate exception names with other Oracle
error codes that you can anticipate. To handle unexpected Oracle errors, you can use the
OTHERS handler. Within this handler, you can call the functions SQLCODE and SQLERRM to
return theOracle error code and message text. Once you know the error code, you can use
it with pragma EXCEPTION_INIT and writea handler specifically for that error.
PL/SQL declares predefined exceptions globally in package STANDARD. You need not
declare them yourself. You can write handlers for predefined exceptions using the names
in the following list:
Exception Oracle Error SQLCODEValue
ACCESS_INTO_NULL ORA-06530 -6530
CASE_NOT_FOUND ORA-06592 -6592
COLLECTION_IS_NULL ORA-06531 -6531
CURSOR_ALREADY_OPEN ORA-06511 -6511
DUP_VAL_ON_INDEX ORA-00001 -1
INVALID_CURSOR ORA-01001 -1001
INVALID_NUMBER ORA-01722 -1722
LOGIN_DENIED ORA-01017 -1017
NO_DATA_FOUND ORA-01403 +100
NOT_LOGGED_ON ORA-01012 -1012
PROGRAM_ERROR ORA-06501 -6501
ROWTYPE_MISMATCH ORA-06504 -6504
SELF_IS_NULL ORA-30625 -30625
STORAGE_ERROR ORA-06500 -6500
SUBSCRIPT_BEYOND_COUNT ORA-06533 -6533
SUBSCRIPT_OUTSIDE_LIMIT ORA-06532 -6532
Page 68 of 77
SYS_INVALID_ROWID ORA-01410 -1410
TIMEOUT_ON_RESOURCE ORA-00051 -51
TOO_MANY_ROWS ORA-01422 -1422
VALUE_ERROR ORA-06502 -6502
ZERO_DIVIDE ORA-01476 -1476
Brief descriptions of thepredefined exceptions follow:
Exception Raised when ...
ACCESS_INTO_NULL A program attempts to assign values to the attributes of an
uninitialized object.
CASE_NOT_FOUND None of the choices in the WHEN clauses of a CASE
statement is selected, and there is no ELSE clause.
COLLECTION_IS_NULL A program attempts to apply collection methods other than
EXISTS to an uninitialized nested table or varray, or the
program attempts to assign values to theelements of an
uninitialized nested table or varray.
CURSOR_ALREADY_OPEN A program attempts to open an already open cursor. A
cursor must be closed before it can be reopened. A cursor
FOR loop automatically opens the cursor to which it refers,
so your program cannot open that cursor inside the loop.
DUP_VAL_ON_INDEX A program attempts to storeduplicate values in a database
column that is constrained by a unique index.
INVALID_CURSOR A program attempts acursor operation that is not allowed,
such as closing an unopened cursor.
INVALID_NUMBER In a SQL statement, the conversion of a character string
into a number fails because the string does not represent a
valid number. (In procedural statements, VALUE_ERROR is
raised.) This exception is also raised when the LIMIT-clause
expression in a bulk FETCH statement does not evaluate to a
positivenumber.
LOGIN_DENIED A program attempts to log on to Oracle with an invalid
username or password.
NO_DATA_FOUND A SELECT INTO statement returns no rows, or your program
references a deleted element in a nested table or an
uninitialized element in an index-by table.
Because this exception is used internally by some SQL
functions to signal that they are finished, you should not
rely on this exception being propagated if you raise it
within a function that is called as part of a query.
NOT_LOGGED_ON A program issues a database call without being connected
to Oracle.
PROGRAM_ERROR PL/SQL has an internal problem.
ROWTYPE_MISMATCH The host cursor variable and PL/SQL cursor variable
involved in an assignment have incompatible return types.
For example, when an open host cursor variable is passed
to a stored subprogram, the return types of theactual and
formal parameters must be compatible.
SELF_IS_NULL A program attempts to call a MEMBER method, but the
instance of the object typehas not been initialized. The
built-in parameter SELF points to theobject, and is always
the first parameter passed to a MEMBER method.
STORAGE_ERROR PL/SQL runs out of memory or memory has been
corrupted.
SUBSCRIPT_BEYOND_COUNT A program references a nested table or varray element
using an index number larger than the number of elements
in the collection.
SUBSCRIPT_OUTSIDE_LIMIT A program references a nested table or varray element
using an index number (-1 for example) that is outside the
legal range.
SYS_INVALID_ROWID The conversion of a character string into a universal rowid
fails because thecharacter string does not represent a valid
rowid.
TIMEOUT_ON_RESOURCE A time-out occurs while Oracle is waiting for a resource.
TOO_MANY_ROWS A SELECT INTO statement returns more than one row.
VALUE_ERROR An arithmetic, conversion, truncation, or size-constraint
error occurs. For example, when your program selects a
column value into a character variable, if the value is
longer than the declared length of thevariable, PL/SQL
aborts the assignment and raises VALUE_ERROR. In
procedural statements, VALUE_ERROR is raised if the
conversion of a character string into a number fails. (In
SQL statements, INVALID_NUMBER is raised.)
ZERO_DIVIDE A program attempts to divide a number by zero.
Defining Your Own PL/SQL Exceptions
PL/SQL lets you define exceptions of your own. Unlike predefined exceptions, user-
defined exceptions must be declared and must be raised explicitly by RAISE statements.
Declaring PL/SQL Exceptions
Exceptions can be declared only in thedeclarative part of a PL/SQL block, subprogram, or
package. You declare an exception by introducing its name, followed by the keyword
EXCEPTION. In the following example, you declare an exception named past_due:
DECLARE
past_due EXCEPTION;
Exception and variable declarations are similar. But remember, an exception is an error
condition, not a data item. Unlike variables, exceptions cannot appear in assignment
statements or SQL statements. However, the same scope rules apply to variables and
exceptions.
Scope Rules for PL/SQL Exceptions
You cannot declare an exception twice in thesame block. You can, however, declare the
same exception in two different blocks.
Page 69 of 77
Exceptions declared in a block are considered local to that block and global to all its sub-
blocks. Because a block can reference only local or global exceptions, enclosing blocks
cannot reference exceptions declared in a sub-block.
If you redeclare a global exception in a sub-block, the local declaration prevails. The sub-
block cannot reference the global exception, unless theexception is declared in a labeled
block and you qualify its name with theblock label:
block_label.exception_name
The following example illustrates thescope rules:
DECLARE
past_due EXCEPTION;
acct_num NUMBER;
BEGIN
DECLARE ---------- sub-block begins
past_due EXCEPTION; -- this declaration prevails
acct_num NUMBER;
due_date DATE := SYSDATE - 1;
todays_date DATE := SYSDATE;
BEGIN
IF due_date < todays_date THEN
RAISE past_due; -- this is not handled
END IF;
END; ------------- sub-block ends
EXCEPTION
WHEN past_due THEN -- does not handle RAISEd exception
dbms_output.put_line('Handling PAST_DUE exception.');
WHEN OTHERS THEN
dbms_output.put_line('Could not recognize PAST_DUE_EXCEPTION in this scope.');
END;
/
The enclosing block does not handle theraised exception because thedeclaration of
past_due in thesub-block prevails. Though they share the same name, the two past_due
exceptions are different, just as thetwo acct_num variables share the same name but are
different variables. Thus, the RAISE statement and the WHEN clause refer to different
exceptions. To have the enclosing block handle theraised exception, you must remove its
declaration from thesub-block or define an OTHERS handler.
Associating a PL/SQL Exception with a Number: Pragma
EXCEPTION_INIT
To handle error conditions (typically ORA- messages) that have no predefined name, you
must use the OTHERS handler or the pragma EXCEPTION_INIT. A pragma is a compiler
directive that is processed at compile time, not at run time.
In PL/SQL, the pragma EXCEPTION_INIT tells the compiler to associate an exception name
with an Oracle error number. That lets you refer to any internal exception by name and to
write a specific handler for it. When you see an error stack, or sequence of error
messages, the one on top is theone that you can trap and handle.
You code the pragma EXCEPTION_INIT in thedeclarative part of a PL/SQL block,
subprogram, or package using thesyntax
PRAGMA EXCEPTION_INIT(exception_name, -Oracle_error_number);
where exception_name is thename of a previously declared exception and the number is a
negative value corresponding to an ORA- error number. The pragma must appear
somewhere after theexception declaration in thesame declarative section, as shown in the
following example:
DECLARE
deadlock_detected EXCEPTION;
PRAGMA EXCEPTION_INIT(deadlock_detected, -60);
BEGIN
null; -- Some operation that causes an ORA-00060 error
EXCEPTION
WHEN deadlock_detected THEN
null; -- handle the error
END;
/
Defining Your Own Error Messages: Procedure
RAISE_APPLICATION_ERROR
The procedure RAISE_APPLICATION_ERROR lets you issueuser-defined ORA- error messages
from stored subprograms. That way, you can report errors to your application and avoid
returning unhandled exceptions.
To call RAISE_APPLICATION_ERROR, usethesyntax
raise_application_error(error_number, message[, {TRUE | FALSE}]);
where error_number is a negative integer in the range -20000 .. -20999 and message is a
character string up to 2048 bytes long. If the optionalthird parameter is TRUE, the error is
placed on the stack of previous errors. If the parameter is FALSE (the default), the error
replaces all previous errors. RAISE_APPLICATION_ERROR is part of package
DBMS_STANDARD, and as with package STANDARD, you do not need to qualify references to
it.
An application can call raise_application_error only froman executing stored subprogram (or
method). When called, raise_application_error ends thesubprogram and returns a user-defined
error number and message to the application. The error number and message can be
trapped like any Oracle error.
In the following example, you call raise_application_error if an error condition of your
choosing happens (in this case, if the current schema owns less than 1000 tables):
DECLARE
num_tables NUMBER;
BEGIN
SELECT COUNT(*) INTO num_tables FROM USER_TABLES;
IF num_tables < 1000 THEN
/* Issue your own error code (ORA-20101) with your own error message. */
raise_application_error(-20101, 'Expecting at least 1000 tables');
ELSE
NULL; -- Do the rest ofthe processing (for the non-error case).
END IF;
END;
/
The calling application gets a PL/SQL exception, which it can process using theerror-
reporting functions SQLCODE and SQLERRM in an OTHERS handler. Also, it can use the
pragma EXCEPTION_INIT to map specific error numbers returned by raise_application_error to
exceptions of its own, as the following Pro*C example shows:
EXEC SQL EXECUTE
/* Execute embedded PL/SQL block using host
variables my_emp_id and my_amount, which were
assigned values in the host environment. */
DECLARE
null_salary EXCEPTION;
/* Map error number returned by raise_application_error
to user-defined exception. */
Page 70 of 77
PRAGMA EXCEPTION_INIT(null_salary, -20101);
BEGIN
raise_salary(:my_emp_id, :my_amount);
EXCEPTION
WHEN null_salary THEN
INSERT INTO emp_audit VALUES (:my_emp_id, ...);
END;
END-EXEC;
This technique allows the calling application to handle error conditions in specific
exception handlers.
Redeclaring Predefined Exceptions
Remember, PL/SQL declares predefined exceptions globally in package STANDARD, so
you need not declare them yourself. Redeclaring predefined exceptions is error prone
because your local declaration overrides the global declaration. For example, if you
declare an exception named invalid_number and then PL/SQL raises the predefined
exception INVALID_NUMBER internally, a handler written for INVALID_NUMBER will not
catch the internal exception. In such cases, you must use dot notation to specify the
predefined exception, as follows:
EXCEPTION
WHEN invalid_number OR STANDARD.INVALID_NUMBER THEN
-- handle the error
END;
How PL/SQL Exceptions Are Raised
Internal exceptions are raised implicitly by therun-time system, as are user-defined
exceptions that you have associated with an Oracle error number using EXCEPTION_INIT.
However, other user-defined exceptions must be raised explicitly by RAISE statements.
Raising Exceptions with the RAISE Statement
PL/SQL blocks and subprograms should raise an exception only when an error makes it
undesirable or impossible to finish processing. You can place RAISE statements for a given
exception anywherewithin the scopeof that exception. In thefollowing example, you alert
your PL/SQL block to a user-defined exception named out_of_stock:
DECLARE
out_of_stock EXCEPTION;
number_on_hand NUMBER := 0;
BEGIN
IF number_on_hand < 1 THEN
RAISE out_of_stock; -- raise an exception that we defined
END IF;
EXCEPTION
WHEN out_of_stock THEN
-- handle the error
dbms_output.put_line('Encountered out-of-stock error.');
END;
/
You can also raise a predefined exception explicitly. That way, an exception handler
written for the predefined exception can process other errors, as the following example
shows:
DECLARE
acct_type INTEGER := 7;
BEGIN
IF acct_type NOT IN (1, 2, 3) THEN
RAISE INVALID_NUMBER; -- raise predefined exception
END IF;
EXCEPTION
WHEN INVALID_NUMBER THEN
dbms_output.put_line('Handling invalid input by rolling back.');
ROLLBACK;
END;
/
How PL/SQL Exceptions Propagate
When an exception is raised, if PL/SQL cannot find a handler for it in the current block or
subprogram, the exception propagates. That is, theexception reproduces itself in
successive enclosing blocks until a handler is found or there are no more blocks to search.
If no handler is found, PL/SQL returns an unhandled exception error to the host
environment.
Exceptions cannot propagate across remote procedure calls done through database links.
An exception can propagatebeyond its scope, that is, beyond theblock in which it was
declared. Consider the following example:
BEGIN
DECLARE ---------- sub-block begins
past_due EXCEPTION;
due_date DATE := trunc(SYSDATE) - 1;
todays_date DATE := trunc(SYSDATE);
BEGIN
IF due_date < todays_date THEN
RAISE past_due;
END IF;
END; ------------- sub-block ends
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END;
/
Because the block that declares theexception past_due has no handler for it, the exception
propagates to the enclosing block. But the enclosing block cannot reference the name
PAST_DUE, because thescope where it was declared no longer exists. Once the exception
name is lost, only an OTHERS handler can catch the exception. If there is no handler for a
user-defined exception, thecalling application gets this error:
ORA-06510: PL/SQL: unhandled user-defined exception
Reraising a PL/SQL Exception
Sometimes, you want to reraise an exception, that is, handle it locally, then pass it to an
enclosing block. For example, you might want to roll back a transaction in the current
block, then log theerror in an enclosing block.
To reraise an exception, use a RAISE statement without an exception name, which is
allowed only in an exception handler:
DECLARE
salary_too_high EXCEPTION;
current_salary NUMBER := 20000;
max_salary NUMBER := 10000;
erroneous_salary NUMBER;
BEGIN
BEGIN ---------- sub-block begins
IF current_salary > max_salary THEN
RAISE salary_too_high; -- raise the exception
END IF;
EXCEPTION
WHEN salary_too_high THEN
Page 71 of 77
-- first step in handling the error
dbms_output.put_line('Salary ' || erroneous_salary ||
' is out ofrange.');
dbms_output.put_line('Maximumsalary is ' || max_salary || '.');
RAISE; -- reraise the current exception
END; ------------ sub-block ends
EXCEPTION
WHEN salary_too_high THEN
-- handle the error more thoroughly
erroneous_salary := current_salary;
current_salary := max_salary;
dbms_output.put_line('Revising salary from' || erroneous_salary ||
'to ' || current_salary || '.');
END;
/
Handling RaisedPL/SQL Exceptions
When an exception is raised, normal execution of your PL/SQL block or subprogram stops
and control transfers to its exception-handling part, which is formatted as follows:
EXCEPTION
WHEN exception_name1 THEN -- handler
sequence_of_statements1
WHEN exception_name2 THEN -- another handler
sequence_of_statements2
...
WHEN OTHERS THEN -- optional handler
sequence_of_statements3
END;
To catch raised exceptions, you writeexception handlers. Each handler consists of a WHEN
clause, which specifies an exception, followed by a sequence of statements to be executed
when that exception is raised. These statements complete execution of the block or
subprogram; control does not return to where the exception was raised. In other words, you
cannot resume processing where you left off.
The optionalOTHERS exception handler, which is always thelast handler in a block or
subprogram, acts as the handler for all exceptions not named specifically. Thus, a block or
subprogram can have only one OTHERS handler.
As the following example shows, use of the OTHERS handler guarantees that no exception
will go unhandled:
EXCEPTION
WHEN ... THEN
-- handle the error
WHEN ... THEN
-- handle the error
WHEN OTHERS THEN
-- handle all other errors
END;
If you want two or more exceptions to execute the same sequence of statements, list the
exception names in the WHEN clause, separating them by thekeyword OR, as follows:
EXCEPTION
WHEN over_limit OR under_limit OR VALUE_ERROR THEN
-- handle the error
If any of the exceptions in thelist is raised, theassociated sequence of statements is
executed. Thekeyword OTHERS cannot appear in the list of exception names; it must
appear by itself. You can have any number of exception handlers, and each handler can
associate a list of exceptions with a sequence of statements. However, an exception name
can appear only once in the exception-handling part of a PL/SQL block or subprogram.
The usual scoping rules for PL/SQL variables apply, so you can reference local and global
variables in an exception handler. However, when an exception is raised inside a cursor
FOR loop, thecursor is closed implicitly before the handler is invoked. Therefore, the
values of explicit cursor attributes are not available in the handler.
Handling Exceptions Raised in Declarations
Exceptions can be raised in declarations by faulty initialization expressions. For example,
the following declaration raises an exception because the constant credit_limit cannot store
numbers larger than 999:
DECLARE
credit_limit CONSTANT NUMBER(3) := 5000; -- raises an exception
BEGIN
NULL;
EXCEPTION
WHEN OTHERS THEN
-- Cannot catch the exception. This handler is never called.
dbms_output.put_line('Can''t handle an exception in a declaration.');
END;
/
Handlers in thecurrent block cannot catch the raised exception because an exception
raised in a declaration propagates immediately to theenclosing block.
Handling Exceptions Raised in Handlers
When an exception occurs within an exception handler, that same handler cannot catch the
exception. An exception raised inside a handler propagates immediately to the enclosing
block, which is searched to find a handler for this new exception. From there on, the
exception propagates normally. For example:
EXCEPTION
WHEN INVALID_NUMBER THEN
INSERT INTO ... -- might raise DUP_VAL_ON_INDEX
WHEN DUP_VAL_ON_INDEX THEN ... -- cannot catch the exception
END;
11 – Triggers
This chapter discusses triggers, which are procedures stored in PL/SQL or Java that run
(fire) implicitly whenever a table or view is modified or when some user actions or
database systemactions occur.
Introduction to Triggers
You can write triggers that fire whenever one of the following operations occurs:
1. DMLstatements (INSERT, UPDATE, DELETE) on a particular table or view, issued
by any user
2. DDL statements (CREATE or ALTER primarily) issued either by a particular
schema/user or by any schema/user in the database
3. Database events, such as logon/logoff, errors, or startup/shutdown, also issued
either by a particular schema/user or by any schema/user in the database
Triggers are similar to stored procedures. A trigger stored in the database can include SQL
and PL/SQL or Java statements to run as a unit and can invoke stored procedures.
Page 72 of 77
However, procedures and triggers differ in theway that they are invoked. A procedure is
explicitly run by a user, application, or trigger. Triggers are implicitly fired by Oracle
when a triggering event occurs, no matter which user is connected or which application is
being used.
Figure 22-1 shows a database application with some SQL statements that implicitly fire
several triggers stored in the database. Notice that the database stores triggers separately
from their associated tables.
Figure 22-1 Triggers
Description of the illustration cncpt076.gif
A trigger can also call out to a C procedure, which is useful for computationally intensive
operations.
The events that fire a trigger include thefollowing:
 DMLstatements that modify data in a table (INSERT, UPDATE, or DELETE)
 DDL statements
 Systemevents such as startup, shutdown, and error messages
 User events such as logon and logoff
Note:
Oracle Forms can define, store, and run triggers of a different sort.
However, do not confuse Oracle Forms triggers with the triggers
discussed in this chapter.
How Triggers Are Used
Triggers supplement thestandard capabilities of Oracle to provide a highly customized
database management system. For example, a trigger can restrict DMLoperations against
a table to thoseissued during regular business hours. You can also use triggers to:
 Automatically generate derived column values
 Prevent invalid transactions
 Enforce complex security authorizations
 Enforce referential integrity across nodes in a distributed database
 Enforce complex business rules
 Provide transparent event logging
 Provide auditing
 Maintain synchronous table replicates
 Gather statistics on table access
 Modify tabledata when DMLstatements are issued against views
 Publish information about database events, user events, and SQL statements to
subscribing applications
Some Cautionary Notes about Triggers
Although triggers are useful for customizing a database, use them only when necessary.
Excessive use of triggers can result in complex interdependencies, which can be difficult
to maintain in a large application. For example, when a trigger fires, a SQL statement
within its trigger action potentially can fire other triggers, resulting in cascading triggers.
This can produce unintended effects. Figure 22-2 illustrates cascading triggers.
Figure 22-2 Cascading Triggers
Description of the illustration cncpt077.gif
Triggers Compared with Declarative Integrity Constraints
You can use both triggers and integrity constraints to define and enforce any typeof
integrity rule. However, Oracle strongly recommends that you use triggers to constrain
data input only in the following situations:
 To enforce referential integrity when child and parent tables are on different
nodes of a distributed database
 To enforce complex business rules not definable using integrity constraints
 When a required referential integrity rule cannot be enforced using the following
integrity constraints:
o NOT NULL, UNIQUE
o PRIMARY KEY
o FOREIGN KEY
o CHECK
o DELETE CASCADE
o DELETE SET NULL
Parts of a Trigger
Page 73 of 77
A trigger has three basic parts:
 A triggering event or statement
 A trigger restriction
 A trigger action
Figure 22-3 represents each of these parts of a trigger and is not meant to show exact
syntax. The sections that follow explain each part of a trigger in greater detail.
Figure 22-3 The REORDER Trigger
Description of the illustration cncpt078.gif
The Triggering Event or Statement
A triggering event or statement is the SQL statement, database event, or user event that
causes a trigger to fire. A triggering event can be one or more of thefollowing:
 An INSERT, UPDATE, or DELETE statement on a specific table (or view, in
some cases)
 A CREATE, ALTER, or DROP statement on any schema object
 A database startup or instance shutdown
 A specific error message or any error message
 A user logon or logoff
For example, in Figure 22-3, the triggering statement is:
... UPDATE OF parts_on_hand ON inventory ...
This statement means that when the parts_on_hand column of a row in the inventory tableis
updated, fire thetrigger. When the triggering event is an UPDATE statement, you can
include a column list to identify which columns must be updated to fire thetrigger. You
cannot specify a column list for INSERT and DELETE statements, because they affect entire
rows of information.
A triggering event can specify multipleSQL statements:
... INSERT OR UPDATE OR DELETE OF inventory ...
This part means that when an INSERT, UPDATE, or DELETE statement is issued against the
inventory table, fire thetrigger. When multiple types of SQL statements can fire a trigger,
you can use conditional predicates to detect the typeof triggering statement. In this way,
you can create a single trigger that runs different code based on the typeof statement that
fires the trigger.
Trigger Restriction
A trigger restriction specifies a Boolean expression that must be true for the trigger to fire.
The trigger action is not run if thetrigger restriction evaluates to false or unknown. In the
example, thetrigger restriction is:
new.parts_on_hand < new.reorder_point
Consequently, the trigger does not fire unless thenumber of available parts is less than a
present reorder amount.
Trigger Action
A trigger action is the procedure (PL/SQL block, Java program, or C callout) that contains
the SQL statements and code to be run when thefollowing events occur:
 A triggering statement is issued.
 The trigger restriction evaluates to true.
Like stored procedures, a trigger action can:
 Contain SQL, PL/SQL, or Java statements
 Define PL/SQL language constructs such as variables, constants, cursors,
exceptions
 Define Java language constructs
 Call stored procedures
If the triggers are row triggers, the statements in a trigger action have access to column
values of therow being processed by the trigger. Correlation names provide access to the
old and new values for each column.
Types of Triggers
Row Triggers and Statement Triggers
When you define a trigger, you can specify thenumber of times thetrigger action is to be
run:
 Once for every row affected by the triggering statement, such as a trigger fired
by an UPDATE statement that updates many rows
 Once for the triggering statement, no matter how many rows it affects
Row Triggers
A row triggeris fired each time the table is affected by thetriggering statement. For
example, if an UPDATE statement updates multiple rows of a table, a row trigger is fired
once for each row affected by the UPDATE statement. If a triggering statement affects no
rows, a row trigger is not run.
Row triggers are useful if thecode in the trigger action depends on data provided by the
triggering statement or rows that are affected. For example, Figure 22-3 illustrates a row
trigger that uses thevalues of each row affected by the triggering statement.
Statement Triggers
A statement trigger is fired once on behalf of the triggering statement, regardless of the
number of rows in the table that the triggering statement affects, even if no rows are
affected. For example, if a DELETE statement deletes several rows from a table, a
statement-level DELETE trigger is fired only once.
Statement triggers are useful if the code in thetrigger action does not depend on thedata
provided by thetriggering statement or therows affected. For example, use a statement
trigger to:
Page 74 of 77
 Makea complex security check on the current time or user
 Generate a single audit record
BEFORE and AFTER Triggers
When defining a trigger, you can specify thetriggertiming—whether thetrigger action is
to be run before or after the triggering statement. BEFORE and AFTER apply to both
statement and row triggers.
BEFORE and AFTER triggers fired by DMLstatements can be defined only on tables, not on
views. However, triggers on thebase tables of a view are fired if an INSERT, UPDATE, or
DELETE statement is issued against the view. BEFORE and AFTER triggers fired by DDL
statements can be defined only on the database or a schema, not on particular tables.
BEFORE Triggers
BEFORE triggers run the trigger action before the triggering statement is run. This typeof
trigger is commonly used in thefollowing situations:
 When thetrigger action determines whether the triggering statement should be
allowed to complete. Using a BEFORE trigger for this purpose, you can eliminate
unnecessary processing of the triggering statement and its eventual rollback in
cases where an exception is raised in thetrigger action.
 To derive specific column values before completing a triggering INSERT or
UPDATE statement.
AFTER Triggers
AFTER triggers run the trigger action after the triggering statement is run.
TriggerType Combinations
Using theoptions listed previously, you can create four types of row and statement
triggers:
 BEFORE statement trigger
Before executing the triggering statement, the trigger action is run.
 BEFORE row trigger
Before modifying each row affected by the triggering statement and before
checking appropriateintegrity constraints, thetrigger action is run, if the trigger
restriction was not violated.
 AFTER statement trigger
After executing the triggering statement and applyingany deferred integrity
constraints, the trigger action is run.
 AFTER row trigger
After modifying each row affected by thetriggering statement and possibly
applyingappropriateintegrity constraints, thetrigger action is run for the current
row provided the trigger restriction was not violated. Unlike BEFORE row triggers,
AFTER row triggers lock rows.
You can have multiple triggers of thesame typefor thesame statement for any given
table. For example, you can have two BEFORE statement triggers for UPDATE statements on
the employees table. Multipletriggers of the same typepermit modular installation of
applications that have triggers on the same tables. Also, Oracle materialized view logs use
AFTER row triggers, so you can design your own AFTER row trigger in addition to the Oracle-
defined AFTER row trigger.
You can create as many triggers of the preceding different types as you need for each type
of DMLstatement, (INSERT, UPDATE, or DELETE).
INSTEAD OF Triggers
INSTEAD OF triggers providea transparent way of modifying views that cannot be modified
directly through DMLstatements (INSERT, UPDATE, and DELETE). These triggers are called
INSTEAD OF triggers because, unlike other types of triggers, Oracle fires thetrigger instead
of executing thetriggering statement.
You can write normal INSERT, UPDATE, and DELETE statements against theview and the
INSTEAD OF trigger is fired to updatethe underlying tables appropriately. INSTEAD OF
triggers are activated for each row of the view that gets modified.
INSTEAD OF Triggers on NestedTables
You cannot modify the elements of a nested table column in a view directly with the
TABLE clause. However, you can do so by defining an INSTEAD OF trigger on thenested
table column of the view. The triggers on thenested tables fire if a nested table element is
updated, inserted, or deleted and handle the actual modifications to theunderlying tables.
Triggers on System Events and User Events
You can use triggers to publish information about database events to subscribers.
Applications can subscribe to database events just as they subscribe to messages from
other applications. Thesedatabase events can include:
 Systemevents
o Database startup and shutdown
o Server error message events
 User events
o User logon and logoff
o DDL statements (CREATE, ALTER, and DROP)
o DMLstatements (INSERT, DELETE, and UPDATE)
Triggers on systemevents can be defined at the database level or
schema level. The DBMS_AQ package is one example of using database triggers to perform
certain actions. For example, a database shutdown trigger is defined at the database level:
CREATE TRIGGER register_shutdown
ON DATABASE
SHUTDOWN
BEGIN
...
DBMS_AQ.ENQUEUE(...);
...
END;
Triggers on DDLstatements or logon/logoff events can also be defined at thedatabase
level or schema level. Triggers on DMLstatements can be defined on a table or view. A
trigger defined at thedatabase level fires for all users, and a trigger defined at the schema
or table level fires only when the triggering event involves that schema or table.
Event Publication
Event publication uses thepublish-subscribe mechanism of Oracle Streams Advanced
Queuing. A queue serves as a message repository for subjects of interest to various
subscribers. Triggers use the DBMS_AQ package to enqueue a message when specific
systemor user events occur.
Event Attributes
Each event allows the use of attributes within the trigger text. For example, the database
startup and shutdown triggers have attributes for theinstance number and thedatabase
name, and the logon and logoff triggers have attributes for the user name. You can specify
a function with thesame name as an attributewhen you create a trigger if you want to
Page 75 of 77
publish that attributewhen the event occurs. The attribute's value is then passed to the
function or payload when the trigger fires. For triggers on DMLstatements, the:OLD
column values pass theattribute's value to the :NEW column value.
System Events
Systemevents that can fire triggers are related to instance startup and shutdown and error
messages. Triggers created on startup and shutdown events have to be associated with the
database. Triggers created on error events can be associated with the database or with a
schema.
 STARTUP triggers fire when the database is opened by an instance. Their
attributes include the systemevent, instance number, and database name.
 SHUTDOWN triggers fire just before theserver starts shuttingdown an instance.
You can use these triggers to make subscribing applications shut down
completely when the database shuts down. For abnormal instance shutdown,
these triggers cannot be fired. The attributes of SHUTDOWN triggers include the
systemevent, instance number, and database name.
 SERVERERROR triggers fire when a specified error occurs, or when any error
occurs if no error number is specified. Their attributes include the systemevent
and error number.
User Events
User events that can fire triggers are related to user logon and logoff, DDL statements, and
DMLstatements.
Triggers on LOGON and LOGOFF Events
LOGON and LOGOFF triggers can be associated with thedatabase or with a schema. Their
attributes include the systemevent and user name, and they can specify simple conditions
on USERID and USERNAME.
 LOGON triggers fire after a successful logon of a user.
 LOGOFF triggers fire at the start of a user logoff.
Triggers on DDL Statements
DDL triggers can be associated with the database or with a schema. Their attributes
include the systemevent, the typeof schema object, and its name. They can specify simple
conditions on the typeand name of theschema object, as well as functions like USERID and
USERNAME. DDL triggers include the following types of triggers:
 BEFORE CREATE and AFTER CREATE triggers fire when a schema object is created
in the database or schema.
 BEFORE ALTER and AFTER ALTER triggers fire when a schema object is altered in
the database or schema.
 BEFORE DROP and AFTER DROP triggers fire when a schema object is dropped
from the database or schema.
Triggers on DML Statements
DMLtriggers for event publication are associated with a table. They can be either BEFORE
or AFTER triggers that fire for each row on which the specified DMLoperation occurs. You
cannot use INSTEAD OF triggers on views to publish events related to DMLstatements—
instead, you can publish events using BEFORE or AFTER triggers for theDMLoperations on
a view's underlying tables that are caused by INSTEAD OF triggers.
The attributes of DMLtriggers for event publication include the systemevent and the
columns defined by theuser in the SELECT list. They can specify simpleconditions on the
typeand name of the schema object, as well as functions (such as UID, USER, USERENV, and
SYSDATE), pseudocolumns, and columns. Thecolumns can be prefixed by :OLD and :NEW
for old and new values. Triggers on DMLstatements include thefollowing triggers:
 BEFORE INSERT and AFTER INSERT triggers fire for each row inserted into the
table.
 BEFORE UPDATE and AFTER UPDATE triggers fire for each row updated in the
table.
 BEFORE DELETE and AFTER DELETE triggers fire for each row deleted from the
table.
Trigger Execution
A trigger is in either of two distinct modes:
Trigger
Mode
Definition
Enabled An enabled trigger runs its trigger action if a triggering statement is issued
and the trigger restriction (if any) evaluates to true.
Disabled A disabled trigger does not run its trigger action, even if a triggering
statement is issued and the trigger restriction (if any) would evaluate to true.
For enabled triggers, Oracle automatically performs the following actions:
 Oracle runs triggers of each typein a planned firing sequence when more than
one trigger is fired by a single SQL statement. First, statement level triggers are
fired, and then row level triggers are fired.
 Oracle performs integrity constraint checking at a set point in time with respect
to the different types of triggers and guarantees that triggers cannot compromise
integrity constraints.
 Oracle provides read-consistent views for queries and constraints.
 Oracle manages the dependencies among triggers and schema objects referenced
in the code of thetrigger action
 Oracle uses two-phasecommit if a trigger updates remote tables in a distributed
database.
 Oracle fires multiple triggers in an unspecified, random order, if more than one
trigger of the same typeexists for a given statement; that is, triggers of the same
typefor thesame statement are not guaranteed to fire in any specific order.
The Execution Model for Triggers and Integrity Constraint Checking
A single SQL statement can potentially fire up to four types of triggers:
 BEFORE row triggers
 BEFORE statement triggers
 AFTER row triggers
 AFTER statement triggers
A triggering statement or a statement within a trigger can cause one or more integrity
constraints to be checked. Also, triggers can contain statements that cause other triggers to
fire (cascading triggers).
Oracle uses thefollowing execution model to maintain the proper firing sequence of
multiple triggers and constraint checking:
1. Run all BEFORE statement triggers that apply to thestatement.
Page 76 of 77
2. Loop for each row affected by theSQL statement.
a. Run all BEFORE row triggers that apply to thestatement.
b. Lock and change row, and perform integrity constraint checking. (The
lock is not released until the transaction is committed.)
c. Run all AFTER row triggers that apply to thestatement.
3. Complete deferred integrity constraint checking.
4. Run all AFTER statement triggers that apply to thestatement.
The definition of the execution model is recursive. For example, a given SQL statement
can cause a BEFORE row trigger to be fired and an integrity constraint to be checked. That
BEFORE row trigger, in turn, might perform an updatethat causes an integrity constraint to
be checked and an AFTER statement trigger to be fired. The AFTER statement trigger causes an
integrity constraint to be checked. In this case, the execution model runs the steps
recursively, as follows:
Original SQL statement issued.
1. BEFORE row triggers fired.
a. AFTER statement triggers fired by UPDATE in BEFORE row trigger.
i. Statements of AFTER statement triggers run.
ii. Integrity constraint checked on tables changed by AFTER statement
triggers.
b. Statements of BEFORE row triggers run.
c. Integrity constraint checked on tables changed by BEFORE row triggers.
2. SQL statement run.
3. Integrity constraint from SQL statement checked.
There are two exceptions to this recursion:
 When a triggering statement modifies one table in a referential constraint (either
the primary key or foreign key table), and a triggered statement modifies the
other, only the triggering statement will check theintegrity constraint. This
allows row triggers to enhance referential integrity.
 Statement triggers fired due to DELETE CASCADE and DELETE SET NULL are fired
before and after the user DELETE statement, not before and after the individual
enforcement statements. This prevents thosestatement triggers from
encountering mutating errors.
An important property of theexecution model is that all actions and checks done as a
result of a SQL statement must succeed. If an exception is raised within a trigger, and the
exception is not explicitly handled, all actions performed as a result of the original SQL
statement, including the actions performed by fired triggers, are rolled back. Thus,
integrity constraints cannot be compromised by triggers. The execution model takes into
account integrity constraints and disallows triggers that violate declarative integrity
constraints.
For example, in thepreviously outlined scenario, supposethat theintegrity constraint is
violated. As a result of this violation, all changes made by theSQL statement, the fired
BEFORE row trigger, and thefired AFTER statement trigger are rolled back.
Note:
Although triggers of different types arefired in a specific order, triggers
of the same typefor the same statement are not guaranteed to fire in any
specific order. For example, all BEFORE row triggers for a single UPDATE
statement may not always fire in the same order. Design your
applications so they do not rely on the firing order of multiple triggers
of the same type.
Data Access for Triggers
When a trigger is fired, thetables referenced in the trigger action might be currently
undergoing changes by SQL statements in other users' transactions. In all cases, the SQL
statements run within triggers follow the common rules used for standalone SQL
statements. In particular, if an uncommitted transaction has modified values that a trigger
being fired either needs to read (query) or write (update), then theSQL statements in the
body of the trigger being fired use the following guidelines:
 Queries see thecurrent read-consistent materialized view of referenced tables
and any data changed within the same transaction.
 Updates wait for existing data locks to be released before proceeding.
Storage of PL/SQL Triggers
Oracle stores PL/SQL triggers in compiled form, just like stored procedures. When a
CREATE TRIGGER statement commits, the compiled PL/SQL code, called P code (for
pseudocode), is stored in the database and thesource code of the trigger is flushed from
the shared pool.
Execution of Triggers
Oracle runs a trigger internally using the same steps used for procedure execution. The
only subtle difference is that a user has the right to fire a trigger if he or she has the
privilege to run the triggering statement. Other than this, triggers are validated and run the
same way as stored procedures.
Dependency Maintenance for Triggers
Like procedures, triggers depend on referenced objects. Oracle automatically manages the
dependencies of a trigger on the schema objects referenced in its trigger action. The
dependency issues for triggers are the same as thosefor stored procedures. Triggers are
treated like stored procedures. They are inserted into thedata dictionary.
Creating Triggers
The Syntax for creating a trigger is:
CREATE [OR REPLACE ] TRIGGER trigger_name
{BEFORE | AFTER | INSTEAD OF }
{INSERT [OR] | UPDATE[OR] | DELETE}
[OF col_name]
ON table_name
[REFERENCING OLD AS o NEW AS n]
[FOR EACH ROW]
WHEN (condition)
DECLARE
Declaration-statements
BEGIN
Executable-statements
EXCEPTION
Exception-handling-statements
Page 77 of 77
END;
Where,
 CREATE [OR REPLACE] TRIGGER trigger_name : Creates or replace an
existing trigger with the trigger_name.
 {BEFORE | AFTER | INSTEAD OF} : This specifies when thetrigger would be
executed. TheINSTEAD OF clause is used for creating trigger on a view.
 {INSERT [OR] | UPDATE[OR] | DELETE}: This specifies the DMLoperation.
 [OF col_name]: This specifies thecolumn name that would be updated.
 [ON table_name]: This specifies the name of the table associated with the
trigger.
 [REFERENCING OLD AS o NEW AS n]: This allows you to refer new and old
values for various DMLstatements, like INSERT, UPDATE, and DELETE.
 [FOR EACH ROW]: This specifies a row level trigger, i.e., thetrigger would be
executed for each row being affected. Otherwisethe trigger will execute just
once when theSQL statement is executed, which is called a table level trigger.
 WHEN (condition): This provides a condition for rows for which thetrigger
would fire. This clause is valid only for row level triggers.
Example:
To start with, we will be using the CUSTOMERStable we had created and used in the
previous chapters:
Select * from customers;
+----+----------+-----+-----------+----------+
| ID | NAME | AGE | ADDRESS | SALARY |
+----+----------+-----+-----------+----------+
| 1 | Ramesh | 32 | Ahmedabad | 2000.00 |
| 2 | Khilan | 25 | Delhi | 1500.00 |
| 3 | kaushik | 23 | Kota | 2000.00 |
| 4 | Chaitali | 25 | Mumbai | 6500.00 |
| 5 | Hardik | 27 | Bhopal | 8500.00 |
| 6 | Komal | 22 | MP | 4500.00 |
+----+----------+-----+-----------+----------+
The following program creates a row level trigger for the customers table that would fire
for INSERT or UPDATEor DELETE operations performed on theCUSTOMERStable.
This trigger will display thesalary difference between theold values and new values:
CREATE OR REPLACE TRIGGER display_salary_changes
BEFORE DELETE OR INSERT OR UPDATEON customers
FOR EACH ROW
WHEN (NEW.ID > 0)
DECLARE
sal_diff number;
BEGIN
sal_diff := :NEW.salary - :OLD.salary;
dbms_output.put_line('Old salary:' || :OLD.salary);
dbms_output.put_line('New salary:' || :NEW.salary);
dbms_output.put_line('Salary difference: ' || sal_diff);
END;
/
When theabove code is executed at SQL prompt, it produces thefollowing result:
Trigger created.
Here following two points are important and should be noted carefully:
 OLD and NEW references are not available for table level triggers, rather you
can use them for record level triggers.
 If you want to query the table in the same trigger, then you should use the
AFTER keyword, because triggers can query thetable or change it again only
after the initial changes are applied and thetable is back in a consistent state.
 Above trigger has been written in such a way that it will fire before any
DELETE or INSERT or UPDATEoperation on the table, but you can write your
trigger on a single or multiple operations, for example BEFORE DELETE,
which will fire whenever a record will be deleted using DELETE operation on
the table.
Triggering a Trigger
Let us performsome DMLoperations on the CUSTOMERStable. Here is one INSERT
statement which will create a new record in the table:
INSERT INTO CUSTOMERS(ID,NAME,AGE,ADDRESS,SALARY)
VALUES (7, 'Kriti', 22, 'HP', 7500.00 );
When a record is created in CUSTOMERStable, above create trigger
display_salary_changes willbe fired and it will display following result:
Old salary:
New salary: 7500
Salary difference:
Because this is a new record so old salary is not available and above result is coming as
null. Now, let us performone more DMLoperation on the CUSTOMERS table. Here is
one UPDATEstatement which will updatean existing record in the table:
UPDATEcustomers
SET salary = salary + 500
WHERE id = 2;
When a record is updated in CUSTOMERStable, above create trigger
display_salary_changes willbe fired and it will display following result:
Old salary: 1500
New salary: 2000
Salary difference: 500

More Related Content

PPT
Sql
jyothislides
 
PPTX
Sql practise for beginners
ISsoft
 
DOCX
SQL Tutorial for BCA-2
Raj vardhan
 
PPTX
Avinash database
avibmas
 
DOC
A must Sql notes for beginners
Ram Sagar Mourya
 
PDF
Sql notes, sql server,sql queries,introduction of SQL, Beginner in SQL
Prashant Kumar
 
PPT
Introduction to structured query language (sql)
Sabana Maharjan
 
PPTX
Sql basics
Aman Lalpuria
 
Sql practise for beginners
ISsoft
 
SQL Tutorial for BCA-2
Raj vardhan
 
Avinash database
avibmas
 
A must Sql notes for beginners
Ram Sagar Mourya
 
Sql notes, sql server,sql queries,introduction of SQL, Beginner in SQL
Prashant Kumar
 
Introduction to structured query language (sql)
Sabana Maharjan
 
Sql basics
Aman Lalpuria
 

What's hot (20)

PPTX
SQL
Shyam Khant
 
DOCX
Sql
Archana Rout
 
PDF
Sql ch 12 - creating database
Mukesh Tekwani
 
PPTX
DDL(Data defination Language ) Using Oracle
Farhan Aslam
 
PDF
Chapter 4 Structured Query Language
Eddyzulham Mahluzydde
 
PDF
Basic SQL
Durgesh Tripathi
 
PDF
Introduction to sq lite
punu_82
 
PPTX
Introduction to SQL (for Chicago Booth MBA technology club)
Jennifer Berk
 
PPTX
Basic SQL and History
SomeshwarMoholkar
 
PDF
SImple SQL
John Cutajar
 
PPTX
Introduction to SQL
Amin Choroomi
 
PPTX
MySQL Essential Training
HudaRaghibKadhim
 
PPTX
SQL Server Learning Drive
TechandMate
 
PPTX
Introduction to SQL
Ehsan Hamzei
 
PPT
Introduction to-sql
BG Java EE Course
 
PDF
SQL Overview
Stewart Rogers
 
PPTX
Presentation slides of Sequence Query Language (SQL)
Punjab University
 
PDF
Steps towards of sql server developer
Ahsan Kabir
 
PPTX
SQL Basics
Hammad Rasheed
 
DOCX
Sql intro
Mohit Gupta
 
Sql ch 12 - creating database
Mukesh Tekwani
 
DDL(Data defination Language ) Using Oracle
Farhan Aslam
 
Chapter 4 Structured Query Language
Eddyzulham Mahluzydde
 
Basic SQL
Durgesh Tripathi
 
Introduction to sq lite
punu_82
 
Introduction to SQL (for Chicago Booth MBA technology club)
Jennifer Berk
 
Basic SQL and History
SomeshwarMoholkar
 
SImple SQL
John Cutajar
 
Introduction to SQL
Amin Choroomi
 
MySQL Essential Training
HudaRaghibKadhim
 
SQL Server Learning Drive
TechandMate
 
Introduction to SQL
Ehsan Hamzei
 
Introduction to-sql
BG Java EE Course
 
SQL Overview
Stewart Rogers
 
Presentation slides of Sequence Query Language (SQL)
Punjab University
 
Steps towards of sql server developer
Ahsan Kabir
 
SQL Basics
Hammad Rasheed
 
Sql intro
Mohit Gupta
 
Ad

Similar to SQL & PLSQL (20)

PPTX
Data Definition Language Commands in DBMS
agrawalmonikacomp
 
PDF
RDBMS Lab03 applying constraints (UIU)
Muhammad T Q Nafis
 
PPTX
apply Integrity constraints on database table
prachi gat
 
PPTX
Data base.ppt
TeklayBirhane
 
PPT
introdution concept on _ _ sql_basic.ppt
riscomputersir
 
PPTX
Sql commands
Pooja Dixit
 
PDF
Relational database management system
Praveen Soni
 
PPTX
Sql Constraints
I L0V3 CODING DR
 
PPT
CSE311_IAH_Slide07_SQL Advanced Quries.ppt
noshinnawar31
 
PPTX
oracle Sql constraint
home
 
PPTX
MS Sql Server: Customizing Your Data Base Design
DataminingTools Inc
 
PPTX
MS SQL SERVER: Customizing Your D Base Design
sqlserver content
 
PPTX
MS SQLSERVER:Customizing Your D Base Design
sqlserver content
 
PPTX
Constraints in Structure Query Language.
anjanasharma77573
 
PPTX
basic of SQL constraints in database management system
anjanasharma77573
 
PPTX
Database Akjljljlkjlkjkljlkjldiministration.pptx
EliasPetros
 
PDF
Introduction to SQL..pdf
mayurisonawane29
 
PPTX
SQL _UNIT_DBMS_PRESENTSTATION_SQL _UNIT_DBMS_PRESENTSTATION
deeptanshudas100
 
PPTX
DBMS: Week 09 - SQL Constraints and Indexing
RashidFaridChishti
 
PPTX
SQL Commands
Sachidananda M H
 
Data Definition Language Commands in DBMS
agrawalmonikacomp
 
RDBMS Lab03 applying constraints (UIU)
Muhammad T Q Nafis
 
apply Integrity constraints on database table
prachi gat
 
Data base.ppt
TeklayBirhane
 
introdution concept on _ _ sql_basic.ppt
riscomputersir
 
Sql commands
Pooja Dixit
 
Relational database management system
Praveen Soni
 
Sql Constraints
I L0V3 CODING DR
 
CSE311_IAH_Slide07_SQL Advanced Quries.ppt
noshinnawar31
 
oracle Sql constraint
home
 
MS Sql Server: Customizing Your Data Base Design
DataminingTools Inc
 
MS SQL SERVER: Customizing Your D Base Design
sqlserver content
 
MS SQLSERVER:Customizing Your D Base Design
sqlserver content
 
Constraints in Structure Query Language.
anjanasharma77573
 
basic of SQL constraints in database management system
anjanasharma77573
 
Database Akjljljlkjlkjkljlkjldiministration.pptx
EliasPetros
 
Introduction to SQL..pdf
mayurisonawane29
 
SQL _UNIT_DBMS_PRESENTSTATION_SQL _UNIT_DBMS_PRESENTSTATION
deeptanshudas100
 
DBMS: Week 09 - SQL Constraints and Indexing
RashidFaridChishti
 
SQL Commands
Sachidananda M H
 
Ad

More from Prakash Poudel (20)

PPTX
Web applications vulnerabilities and threats
Prakash Poudel
 
PPTX
Earliest Due Date Algorithm for Task scheduling for cloud computing
Prakash Poudel
 
PPTX
Recent and-future-trends spm
Prakash Poudel
 
DOCX
Locking base concurrency control
Prakash Poudel
 
PPTX
Cocomo ( cot constrictive model) and capability maturity model
Prakash Poudel
 
PDF
Microprocessor
Prakash Poudel
 
DOCX
Maximum power transfer theorem
Prakash Poudel
 
DOCX
Linux technology
Prakash Poudel
 
PDF
Java PU solution
Prakash Poudel
 
PPTX
System administration
Prakash Poudel
 
PPTX
Telephone call-simulation
Prakash Poudel
 
PPTX
General Online Health Information System Proposed Application
Prakash Poudel
 
PPTX
Nepal Doorsanchar Company Limited Internship Experience
Prakash Poudel
 
DOCX
Software engineering
Prakash Poudel
 
DOCX
Multimedia Technology in computer
Prakash Poudel
 
PPTX
File permission in linux
Prakash Poudel
 
DOC
organization Management
Prakash Poudel
 
DOC
Organization Management Concept
Prakash Poudel
 
PPTX
Java Programming concept
Prakash Poudel
 
PPTX
Letest
Prakash Poudel
 
Web applications vulnerabilities and threats
Prakash Poudel
 
Earliest Due Date Algorithm for Task scheduling for cloud computing
Prakash Poudel
 
Recent and-future-trends spm
Prakash Poudel
 
Locking base concurrency control
Prakash Poudel
 
Cocomo ( cot constrictive model) and capability maturity model
Prakash Poudel
 
Microprocessor
Prakash Poudel
 
Maximum power transfer theorem
Prakash Poudel
 
Linux technology
Prakash Poudel
 
Java PU solution
Prakash Poudel
 
System administration
Prakash Poudel
 
Telephone call-simulation
Prakash Poudel
 
General Online Health Information System Proposed Application
Prakash Poudel
 
Nepal Doorsanchar Company Limited Internship Experience
Prakash Poudel
 
Software engineering
Prakash Poudel
 
Multimedia Technology in computer
Prakash Poudel
 
File permission in linux
Prakash Poudel
 
organization Management
Prakash Poudel
 
Organization Management Concept
Prakash Poudel
 
Java Programming concept
Prakash Poudel
 

Recently uploaded (20)

PPTX
Introduction-to-Python-Programming-Language (1).pptx
dhyeysapariya
 
PDF
oop_java (1) of ice or cse or eee ic.pdf
sabiquntoufiqlabonno
 
PPTX
The whitetiger novel review for collegeassignment.pptx
DhruvPatel754154
 
PPT
2009worlddatasheet_presentation.ppt peoole
umutunsalnsl4402
 
PPTX
Probability systematic sampling methods.pptx
PrakashRajput19
 
PDF
The_Future_of_Data_Analytics_by_CA_Suvidha_Chaplot_UPDATED.pdf
CA Suvidha Chaplot
 
PPTX
Purple and Violet Modern Marketing Presentation (1).pptx
SanthoshKumar229321
 
PDF
345_IT infrastructure for business management.pdf
LEANHTRAN4
 
PPTX
Web dev -ppt that helps us understand web technology
shubhragoyal12
 
PDF
Key_Statistical_Techniques_in_Analytics_by_CA_Suvidha_Chaplot (1).pdf
CA Suvidha Chaplot
 
PPTX
Data-Driven Machine Learning for Rail Infrastructure Health Monitoring
Sione Palu
 
PPTX
Economic Sector Performance Recovery.pptx
yulisbaso2020
 
PPTX
Introduction to Biostatistics Presentation.pptx
AtemJoshua
 
PDF
Blue Futuristic Cyber Security Presentation.pdf
tanvikhunt1003
 
PDF
Chad Readey - An Independent Thinker
Chad Readey
 
PDF
Technical Writing Module-I Complete Notes.pdf
VedprakashArya13
 
PPTX
Introduction to Data Analytics and Data Science
KavithaCIT
 
PPTX
Data Security Breach: Immediate Action Plan
varmabhuvan266
 
PPTX
Web_Engineering_Assignment_Clean.pptxfor college
HUSNAINAHMAD39
 
PPT
Grade 5 PPT_Science_Q2_W6_Methods of reproduction.ppt
AaronBaluyut
 
Introduction-to-Python-Programming-Language (1).pptx
dhyeysapariya
 
oop_java (1) of ice or cse or eee ic.pdf
sabiquntoufiqlabonno
 
The whitetiger novel review for collegeassignment.pptx
DhruvPatel754154
 
2009worlddatasheet_presentation.ppt peoole
umutunsalnsl4402
 
Probability systematic sampling methods.pptx
PrakashRajput19
 
The_Future_of_Data_Analytics_by_CA_Suvidha_Chaplot_UPDATED.pdf
CA Suvidha Chaplot
 
Purple and Violet Modern Marketing Presentation (1).pptx
SanthoshKumar229321
 
345_IT infrastructure for business management.pdf
LEANHTRAN4
 
Web dev -ppt that helps us understand web technology
shubhragoyal12
 
Key_Statistical_Techniques_in_Analytics_by_CA_Suvidha_Chaplot (1).pdf
CA Suvidha Chaplot
 
Data-Driven Machine Learning for Rail Infrastructure Health Monitoring
Sione Palu
 
Economic Sector Performance Recovery.pptx
yulisbaso2020
 
Introduction to Biostatistics Presentation.pptx
AtemJoshua
 
Blue Futuristic Cyber Security Presentation.pdf
tanvikhunt1003
 
Chad Readey - An Independent Thinker
Chad Readey
 
Technical Writing Module-I Complete Notes.pdf
VedprakashArya13
 
Introduction to Data Analytics and Data Science
KavithaCIT
 
Data Security Breach: Immediate Action Plan
varmabhuvan266
 
Web_Engineering_Assignment_Clean.pptxfor college
HUSNAINAHMAD39
 
Grade 5 PPT_Science_Q2_W6_Methods of reproduction.ppt
AaronBaluyut
 

SQL & PLSQL

  • 1. Page 1 of 77 Features of SQL  SQL stands for Structured Query Language  SQL lets you access and manipulate databases  SQL is an ANSI (American National Standards Institute) standard  SQL can execute queries against a database  SQL can retrieve data from a database  SQL can insert records in a database  SQL can updaterecords in a database  SQL can delete records from a database  SQL can create new databases  SQL can create new tables in a database  SQL can create stored procedures in a database  SQL can create views in a database  SQL can set permissions on tables, procedures, and views SQLDML and DDL SQL can be divided into two parts:The DataManipulation Language (DML) and the Data Definition Language (DDL). The query and updatecommands form theDMLpart of SQL:  SELECT - extracts data from a database  UPDATE - updates data in a database  DELETE - deletes data from a database  INSERT INTO - inserts new data into a database The DDLpart of SQL permits database tables to be created or deleted. It also defines indexes (keys), specifies links between tables, and imposes constraints between tables. The most important DDL statements in SQL are:  CREATE DATABASE - creates a new database  ALTER DATABASE - modifies a database  CREATE TABLE - creates a new table  ALTER TABLE - modifies a table  DROP TABLE - deletes a table  CREATE INDEX - creates an index (search key)  DROP INDEX - deletes an index SQLDDL Statements The CREATE TABLE Statement The CREATETABLE statement is used to create a table in a database. SQLCREATE TABLE Syntax CREATE TABLEtable_name ( column_name1 data_type, column_name2 data_type, column_name3 data_type, .... ) The data typespecifies what typeof datathe column can hold. CREATE TABLE Example Now we want to create a table called "Persons" that contains five columns: P_Id, LastName, FirstName, Address, and City. We use thefollowing CREATE TABLEstatement: CREATE TABLEPersons ( P_Id int, LastName varchar(255), FirstName varchar(255), Address varchar(255), City varchar(255) ) The P_Id column is of typeint and will hold a number. The LastName, FirstName, Address, and City columns are of typevarchar with a maximum length of 255 characters. The empty "Persons" tablewill now look like this: P_Id LastName FirstName Address City The empty tablecan be filled with data with the INSERT INTO statement. SQLConstraints Constraints are used to limit the typeof data that can go into a table. Constraints can be specified when a table is created (with theCREATE TABLE statement) or after the table is created (with the ALTER TABLE statement). We will focus on the following constraints:  NOT NULL  UNIQUE  PRIMARYKEY  FOREIGN KEY  CHECK  DEFAULT The next chapters will describe each constraint in detail. SQLNOT NULL Constraint By default, a table column can hold NULL values. SQLNOT NULL Constraint The NOT NULLconstraint enforces a column to NOT accept NULL values. The NOT NULLconstraint enforces a field to always contain a value. This means that you cannot insert a new record, or updatea record without adding a value to this field. The following SQL enforces the"P_Id" column and the "LastName" column to not accept NULL values: CREATE TABLEPersons ( P_Id int NOT NULL,
  • 2. Page 2 of 77 LastName varchar(255) NOT NULL, FirstName varchar(255), Address varchar(255), City varchar(255) ) SQLUNIQUE Constraint The UNIQUEconstraint uniquely identifies each record in a database table. The UNIQUEand PRIMARYKEY constraints both providea guarantee for uniqueness for a column or set of columns. A PRIMARYKEY constraint automatically has a UNIQUE constraint defined on it. Notethat you can have many UNIQUEconstraints per table, but only one PRIMARY KEY constraint per table. SQLUNIQUE Constraint on CREATE TABLE The following SQL creates a UNIQUEconstraint on the "P_Id" column when the "Persons" table is created: MySQL: CREATE TABLEPersons ( P_Id int NOT NULL, LastName varchar(255) NOT NULL, FirstName varchar(255), Address varchar(255), City varchar(255), UNIQUE(P_Id) ) CREATE TABLEPersons ( P_Id int NOT NULL UNIQUE, LastName varchar(255) NOT NULL, FirstName varchar(255), Address varchar(255), City varchar(255) ) To allow naming of a UNIQUEconstraint, and for defining a UNIQUEconstraint on multiple columns, use thefollowing SQL syntax: CREATE TABLEPersons ( P_Id int NOT NULL, LastName varchar(255) NOT NULL, FirstName varchar(255), Address varchar(255), City varchar(255), CONSTRAINT uc_PersonID UNIQUE(P_Id,LastName) ) SQLUNIQUE Constraint on ALTER TABLE To create a UNIQUEconstraint on the"P_Id" column when the table is already created, use the following SQL: MySQL/ SQLServer/ Oracle / MS Access: ALTER TABLEPersons ADD UNIQUE(P_Id) To allow naming of a UNIQUEconstraint, and for defining a UNIQUEconstraint on multiple columns, use thefollowing SQL syntax: MySQL/ SQLServer/ Oracle / MS Access: ALTER TABLEPersons ADD CONSTRAINT uc_PersonID UNIQUE(P_Id,LastName) To DROP a UNIQUE Constraint To drop a UNIQUEconstraint, use the following SQL: MySQL: ALTER TABLEPersons DROP INDEX uc_PersonID SQLServer/ Oracle / MS Access: ALTER TABLEPersons DROP CONSTRAINT uc_PersonID SQLPRIMARY KEY Constraint The PRIMARYKEY constraint uniquely identifies each record in a database table. Primary keys must contain unique values. A primary key column cannot contain NULL values. Each table should have a primary key, and each table can have only ONE primary key. SQLPRIMARY KEY Constraint on CREATE TABLE The following SQL creates a PRIMARYKEY on the "P_Id" column when the "Persons" table is created: CREATE TABLEPersons ( P_Id int NOT NULL PRIMARYKEY, LastName varchar(255) NOT NULL, FirstName varchar(255), Address varchar(255), City varchar(255) ) To allow naming of a PRIMARYKEY constraint, and for defining a PRIMARYKEY constraint on multiple columns, use the following SQL syntax: CREATE TABLEPersons ( P_Id int NOT NULL, LastName varchar(255) NOT NULL, FirstName varchar(255), Address varchar(255), City varchar(255),
  • 3. Page 3 of 77 CONSTRAINT pk_PersonID PRIMARYKEY (P_Id,LastName) ) Note: In the example above there is only ONE PRIMARYKEY (pk_PersonID). However, the value of thepk_PersonID is made up of two columns (P_Id and LastName). SQLPRIMARY KEY Constraint on ALTER TABLE To create a PRIMARYKEY constraint on the "P_Id" column when the table is already created, use thefollowing SQL: MySQL/ SQLServer/ Oracle / MS Access: ALTER TABLEPersons ADD PRIMARYKEY (P_Id) To allow naming of a PRIMARYKEY constraint, and for defining a PRIMARYKEY constraint on multiple columns, use the following SQL syntax: MySQL/ SQLServer/ Oracle / MS Access: ALTER TABLEPersons ADD CONSTRAINT pk_PersonID PRIMARYKEY (P_Id,LastName) Note: If you use the ALTER TABLE statement to add a primary key, the primary key column(s) must already have been declared to not contain NULL values (when the table was first created). To DROP a PRIMARY KEY Constraint To drop a PRIMARYKEY constraint, use the following SQL: MySQL: ALTER TABLEPersons DROP PRIMARYKEY SQLServer/ Oracle / MS Access: ALTER TABLEPersons DROP CONSTRAINT pk_PersonID SQLFOREIGN KEY Constraint A FOREIGN KEY in one table points to a PRIMARYKEY in another table. Let's illustrate the foreign key with an example. Look at the following two tables: The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger The "Orders" table: O_Id OrderNo P_Id 1 77895 3 2 44678 3 3 22456 2 4 24562 1 Notethat the "P_Id" column in the "Orders" table points to the "P_Id" column in the "Persons" table. The "P_Id" column in the"Persons" table is thePRIMARYKEY in the "Persons" table. The "P_Id" column in the"Orders" table is a FOREIGN KEY in the "Orders" table. The FOREIGN KEY constraint is used to prevent actions that would destroy links between tables. The FOREIGN KEY constraint also prevents invalid data from being inserted into the foreign key column, because it has to be one of thevalues contained in the table it points to. SQLFOREIGN KEY Constraint on CREATE TABLE The following SQL creates a FOREIGN KEY on the "P_Id" column when the"Orders" table is created: MySQL: CREATE TABLEOrders ( O_Id int NOT NULL, OrderNo int NOT NULL, P_Id int, PRIMARYKEY (O_Id), FOREIGN KEY (P_Id) REFERENCES Persons(P_Id) ) SQLServer/ Oracle / MS Access: CREATE TABLEOrders ( O_Id int NOT NULL PRIMARYKEY, OrderNo int NOT NULL, P_Id int FOREIGN KEY REFERENCES Persons(P_Id) ) To allow naming of a FOREIGN KEY constraint, and for defining a FOREIGN KEY constraint on multiple columns, use the following SQL syntax: MySQL/ SQLServer/ Oracle / MS Access: CREATE TABLEOrders ( O_Id int NOT NULL, OrderNo int NOT NULL, P_Id int, PRIMARYKEY (O_Id), CONSTRAINT fk_PerOrders FOREIGN KEY (P_Id) REFERENCES Persons(P_Id) ) SQLFOREIGN KEY Constraint on ALTER TABLE To create a FOREIGN KEY constraint on the "P_Id" column when the"Orders" table is already created, use the following SQL: MySQL/ SQLServer/ Oracle / MS Access: ALTER TABLEOrders ADD FOREIGN KEY (P_Id) REFERENCES Persons(P_Id) To allow naming of a FOREIGN KEY constraint, and for defining a FOREIGN KEY constraint on multiple columns, use the following SQL syntax:
  • 4. Page 4 of 77 MySQL/ SQLServer/ Oracle / MS Access: ALTER TABLEOrders ADD CONSTRAINT fk_PerOrders FOREIGN KEY (P_Id) REFERENCES Persons(P_Id) To DROP a FOREIGN KEY Constraint To drop a FOREIGN KEY constraint, use thefollowing SQL: MySQL: ALTER TABLEOrders DROP FOREIGN KEY fk_PerOrders SQLServer/ Oracle / MS Access: ALTER TABLEOrders DROP CONSTRAINT fk_PerOrders SQLCHECK Constraint The CHECK constraint is used to limit thevalue range that can be placed in a column. If you define a CHECK constraint on a single column it allows only certain values for this column. If you define a CHECK constraint on a table it can limit the values in certain columns based on values in other columns in the row. SQLCHECK Constraint on CREATE TABLE The following SQL creates a CHECK constraint on the"P_Id" column when the "Persons" table is created. The CHECK constraint specifies that thecolumn "P_Id" must only include integers greater than 0. MySQL: CREATE TABLEPersons ( P_Id int NOT NULL, LastName varchar(255) NOT NULL, FirstName varchar(255), Address varchar(255), City varchar(255), CHECK (P_Id>0) ) CREATE TABLEPersons ( P_Id int NOT NULL CHECK (P_Id>0), LastName varchar(255) NOT NULL, FirstName varchar(255), Address varchar(255), City varchar(255) ) To allow naming of a CHECK constraint, and for defining a CHECK constraint on multiple columns, use thefollowing SQL syntax: CREATE TABLEPersons ( P_Id int NOT NULL, LastName varchar(255) NOT NULL, FirstName varchar(255), Address varchar(255), City varchar(255), CONSTRAINT chk_Person CHECK (P_Id>0 AND City='Sandnes') ) SQLCHECK Constraint on ALTER TABLE To create a CHECK constraint on the "P_Id" column when thetable is already created, use the following SQL: MySQL/ SQLServer/ Oracle / MS Access: ALTER TABLEPersons ADD CHECK (P_Id>0) To allow naming of a CHECK constraint, and for defining a CHECK constraint on multiple columns, use thefollowing SQL syntax: MySQL/ SQLServer/ Oracle / MS Access: ALTER TABLEPersons ADD CONSTRAINT chk_Person CHECK (P_Id>0 AND City='Sandnes') To DROP a CHECK Constraint To drop a CHECK constraint, use thefollowing SQL: SQLServer/ Oracle / MS Access: ALTER TABLEPersons DROP CONSTRAINT chk_Person MySQL: ALTER TABLEPersons DROP CHECK chk_Person AUTO INCREMENT a Field Very often we would like thevalue of the primary key field to be created automatically every time a new record is inserted. We would like to create an auto-increment field in a table. Syntax for Oracle In Oracle the code is a little bit more tricky. You will have to create an auto-increment field with the sequence object (this object generates a number sequence). Use thefollowing CREATE SEQUENCE syntax: CREATE SEQUENCE seq_person MINVALUE1 START WITH 1 INCREMENT BY 1 CACHE 10
  • 5. Page 5 of 77 The code above creates a sequence object called seq_person, that starts with 1 and will increment by 1. It will also cache up to 10 values for performance. The cache option specifies how many sequence values will be stored in memory for faster access. To insert a new record into the"Persons" table, we will have to use thenextval function (this function retrieves thenext value from seq_person sequence): INSERT INTO Persons (P_Id,FirstName,LastName) VALUES (seq_person.nextval,'Lars','Monsen') The SQL statement above would insert a new record into the "Persons" table. The"P_Id" column would be assigned thenext number from the seq_person sequence. The "FirstName" column would be set to "Lars" and the "LastName" column would be set to "Monsen". The ALTER TABLE Statement The ALTER TABLE statement is used to add, delete, or modify columns in an existing table. SQLALTER TABLE Syntax To add a column in a table, use the following syntax: ALTER TABLEtable_name ADD column_name datatype To delete a column in a table, use the following syntax(notice that some database systems don't allow deleting a column): ALTER TABLEtable_name DROP COLUMN column_name To change the data typeof a column in a table, use the following syntax: ALTER TABLEtable_name MODIFYcolumn_name datatype SQLALTER TABLE Example Look at the "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger Now we want to add a column named "DateOfBirth" in the "Persons" table. We use thefollowing SQL statement: ALTER TABLEPersons ADD DateOfBirth date Notice that the new column, "DateOfBirth", is of typedateand is going to hold a date. The data typespecifies what typeof data thecolumn can hold. The "Persons" table will now like this: P_Id LastName FirstName Address City DateOfBirth 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger Change Data Type Example Now we want to change the data typeof thecolumn named "DateOfBirth" in the "Persons" table. We use thefollowing SQL statement: ALTER TABLEPersons ALTER COLUMN DateOfBirth year Notice that the "DateOfBirth" column is now of typeyear and is going to hold a year in a two-digit or four-digit format. DROP COLUMN Example Next, we want to delete thecolumn named "DateOfBirth" in the"Persons" table. We use thefollowing SQL statement: ALTER TABLEPersons DROP COLUMN DateOfBirth The "Persons" table will now like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger The SQLSELECT Statement The SELECT statement is used to select data from a database. The result is stored in a result table, called the result-set. SQLSELECT Syntax SELECT column_name(s) FROM table_name and SELECT * FROM table_name Note: SQL is not case sensitive. SELECT is thesame as select. An SQLSELECT Example The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger Now we want to select the content of the columns named "LastName" and "FirstName" from the table above. We use thefollowing SELECT statement: SELECT LastName,FirstName FROM Persons The result-set will look like this: LastName FirstName Hansen Ola Svendson Tove Pettersen Kari
  • 6. Page 6 of 77 SELECT * Example Now we want to select all the columns from the "Persons" table. We use thefollowing SELECT statement: SELECT * FROM Persons Tip: The asterisk (*) is a quick way of selecting all columns! The result-set will look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger The SQLSELECT DISTINCT Statement In a table, some of the columns may contain duplicate values. This is not a problem, however, sometimes you will want to list only the different (distinct) values in a table. The DISTINCT keyword can be used to return only distinct (different) values. SQLSELECT DISTINCT Syntax SELECT DISTINCT column_name(s) FROM table_name SELECT DISTINCT Example The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger Now we want to select only thedistinct values from thecolumn named "City" from the table above. We use thefollowing SELECT statement: SELECT DISTINCT City FROM Persons The result-set will look like this: City Sandnes Stavanger The WHERE Clause The WHERE clause is used to extract only thoserecords that fulfill a specified criterion. SQLWHERE Syntax SELECT column_name(s) FROM table_name WHERE column_name operator value WHERE Clause Example The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger Now we want to select only thepersons living in the city "Sandnes" from the table above. We use thefollowing SELECT statement: SELECT * FROM Persons WHERE City='Sandnes' The result-set will look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes Quotes Around Text Fields SQL uses single quotes around text values (most database systems will also accept double quotes). However, numeric values should not be enclosed in quotes. For text values: This is correct: SELECT * FROM Persons WHERE FirstName='Tove' This is wrong: SELECT * FROM Persons WHERE FirstName=Tove For numeric values: This is correct: SELECT * FROM Persons WHERE Year=1965 This is wrong: SELECT * FROM Persons WHERE Year='1965' Operators Allowed in the WHERE Clause With the WHERE clause, the following operators can be used: Operator Description = Equal <> Not equal > Greater than < Less than >= Greater than or equal <= Less than or equal BETWEEN Between an inclusive range LIKE Search for a pattern IN To specify multiple possiblevalues for a column Note: In some versions of SQL the <> operator may be written as != The AND & OR Operators The AND operator displays arecord if both the first condition and the second condition are true. The OR operator displays a record if either the first condition or thesecond condition is true.
  • 7. Page 7 of 77 AND Operator Example The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger Now we want to select only thepersons with the first name equal to "Tove" AND the last name equal to "Svendson": We use thefollowing SELECT statement: SELECT * FROM Persons WHERE FirstName='Tove' AND LastName='Svendson' The result-set will look like this: P_Id LastName FirstName Address City 2 Svendson Tove Borgvn 23 Sandnes OR Operator Example Now we want to select only thepersons with the first name equal to "Tove" OR the first name equal to "Ola": We use thefollowing SELECT statement: SELECT * FROM Persons WHERE FirstName='Tove' OR FirstName='Ola' The result-set will look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes Combining AND & OR You can also combine AND and OR (use parenthesis to form complex expressions). Now we want to select only thepersons with the last name equal to "Svendson" AND the first name equal to "Tove" OR to "Ola": We use thefollowing SELECT statement: SELECT * FROM Persons WHERE LastName='Svendson' AND (FirstName='Tove' OR FirstName='Ola') The result-set will look like this: P_Id LastName FirstName Address City 2 Svendson Tove Borgvn 23 Sandnes SQLORDER BY Keyword The ORDER BY keyword is used to sort the result-set. The ORDER BY Keyword The ORDER BY keyword is used to sort the result-set by a specified column. The ORDER BY keyword sorts therecords in ascending order by default. If you want to sort therecords in a descending order, you can use the DESC keyword. SQLORDER BY Syntax SELECT column_name(s) FROM table_name ORDER BY column_name(s) ASC|DESC ORDER BY Example The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger 4 Nilsen Tom Vingvn 23 Stavanger Now we want to select all the persons from the table above, however, we want to sort the persons by their last name. We use thefollowing SELECT statement: SELECT * FROM Persons ORDER BY LastName The result-set will look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 4 Nilsen Tom Vingvn 23 Stavanger 3 Pettersen Kari Storgt 20 Stavanger 2 Svendson Tove Borgvn 23 Sandnes ORDER BY DESC Example Now we want to select all the persons from the table above, however, we want to sort the persons descending by their last name. We use thefollowing SELECT statement: SELECT * FROM Persons ORDER BY LastName DESC The result-set will look like this: P_Id LastName FirstName Address City 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger 4 Nilsen Tom Vingvn 23 Stavanger 1 Hansen Ola Timoteivn 10 Sandnes The INSERT INTO Statement The INSERT INTO statement is used to insert a new row in a table. SQLINSERT INTO Syntax It is possible to write theINSERT INTO statement in two forms. The first form doesn't specify thecolumn names where the data will be inserted, only their values: INSERT INTO table_name VALUES (value1, value2, value3,...)
  • 8. Page 8 of 77 The second form specifies both thecolumn names and the values to be inserted: INSERT INTO table_name (column1, column2, column3,...) VALUES (value1, value2, value3,...) SQLINSERT INTO Example We have the following "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger Now we want to insert a new row in the "Persons" table. We use thefollowing SQL statement: INSERT INTO Persons VALUES (4,'Nilsen', 'Johan', 'Bakken 2', 'Stavanger') The "Persons" table will now look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger 4 Nilsen Johan Bakken 2 Stavanger Insert Data Only in SpecifiedColumns It is also possibleto only add data in specific columns. The following SQL statement will add a new row, but only add data in the "P_Id", "LastName" and the "FirstName" columns: INSERT INTO Persons (P_Id, LastName, FirstName) VALUES (5, 'Tjessem', 'Jakob') The "Persons" table will now look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger 4 Nilsen Johan Bakken 2 Stavanger 5 Tjessem Jakob The UPDATE Statement The UPDATEstatement is used to updateexisting records in a table. SQLUPDATE Syntax UPDATEtable_name SET column1=value, column2=value2,... WHERE some_column=some_value Note: Notice the WHERE clause in theUPDATEsyntax. The WHERE clause specifies which record or records that should be updated. If you omit theWHERE clause, all records will be updated! SQLUPDATE Example The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger 4 Nilsen Johan Bakken 2 Stavanger 5 Tjessem Jakob Now we want to updatethe person "Tjessem, Jakob" in the "Persons" table. We use thefollowing SQL statement: UPDATEPersons SET Address='Nissestien 67', City='Sandnes' WHERE LastName='Tjessem' AND FirstName='Jakob' The "Persons" table will now look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger 4 Nilsen Johan Bakken 2 Stavanger 5 Tjessem Jakob Nissestien 67 Sandnes SQLUPDATE Warning Be careful when updating records. If we had omitted the WHERE clause in theexample above, like this: UPDATEPersons SET Address='Nissestien 67', City='Sandnes' The "Persons" table would have looked like this: P_Id LastName FirstName Address City 1 Hansen Ola Nissestien 67 Sandnes 2 Svendson Tove Nissestien 67 Sandnes 3 Pettersen Kari Nissestien 67 Sandnes 4 Nilsen Johan Nissestien 67 Sandnes 5 Tjessem Jakob Nissestien 67 Sandnes The DELETE Statement The DELETE statement is used to delete rows in a table. SQLDELETE Syntax DELETE FROM table_name WHERE some_column=some_value Note: Notice the WHERE clause in theDELETE syntax. The WHERE clause specifies which record or records that should be deleted. If you omit the WHERE clause, all records will be deleted! SQLDELETE Example The "Persons" table:
  • 9. Page 9 of 77 P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger 4 Nilsen Johan Bakken 2 Stavanger 5 Tjessem Jakob Nissestien 67 Sandnes Now we want to delete theperson "Tjessem, Jakob" in the"Persons" table. We use thefollowing SQL statement: DELETE FROM Persons WHERE LastName='Tjessem' AND FirstName='Jakob' The "Persons" table will now look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger 4 Nilsen Johan Bakken 2 Stavanger Delete All Rows It is possible to delete all rows in a table without deleting the table. This means that the table structure, attributes, and indexes will be intact: DELETE FROM table_name or DELETE * FROM table_name Note: Be very careful when deleting records. You cannot undo this statement! The TOP Clause The TOP clause is used to specify thenumber of records to return. The TOP clause can be very useful on large tables with thousands of records. Returning a large number of records can impact on performance. Note: Not all database systems supporttheTOP clause. Oracle Syntax SELECT column_name(s) FROM table_name WHERE ROWNUM <= number Example SELECT * FROM Persons WHERE ROWNUM <=5 SQLTOP Example The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger 4 Nilsen Tom Vingvn 23 Stavanger Now we want to select only thetwo first records in the table above. We use thefollowing SELECT statement: SELECT TOP 2 * FROM Persons The result-set will look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes SQLTOP PERCENT Example The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger 4 Nilsen Tom Vingvn 23 Stavanger Now we want to select only 50% of therecords in the table above. We use thefollowing SELECT statement: SELECT TOP 50 PERCENT * FROM Persons The result-set will look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes The LIKE Operator The LIKE operator is used to search for a specified pattern in a column. SQLLIKE Syntax SELECT column_name(s) FROM table_name WHERE column_name LIKE pattern LIKE Operator Example The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger Now we want to select the persons living in a city that starts with "s" from the table above. We use thefollowing SELECT statement: SELECT * FROM Persons WHERE City LIKE 's%' The "%" sign can be used to define wildcards (missing letters in thepattern) both before and after the pattern. The result-set will look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes
  • 10. Page 10 of 77 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger Next, we want to select the persons living in a city that ends with an "s" from the "Persons" table. We use thefollowing SELECT statement: SELECT * FROM Persons WHERE City LIKE '%s' The result-set will look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes Next, we want to select the persons living in a city that contains the pattern "tav" from the "Persons" table. We use thefollowing SELECT statement: SELECT * FROM Persons WHERE City LIKE '%tav%' The result-set will look like this: P_Id LastName FirstName Address City 3 Pettersen Kari Storgt 20 Stavanger It is also possibleto select thepersons living in a city that does NOT contain thepattern "tav" from the"Persons" table, by using the NOT keyword. We use thefollowing SELECT statement: SELECT * FROM Persons WHERE City NOT LIKE'%tav%' The result-set will look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes SQLWildcards SQL wildcards can be used when searching for data in a database. SQLWildcards SQL wildcards can substitutefor one or more characters when searching for data in a database. SQL wildcards must be used with the SQL LIKE operator. With SQL, thefollowing wildcards can be used: Wildcard Description % A substitutefor zero or more characters _ A substitutefor exactly one character [charlist] Any single character in charlist [^charlist] or [!charlist] Any single character not in charlist SQLWildcard Examples We have the following "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger Using the % Wildcard Now we want to select the persons living in a city that starts with "sa" from the "Persons" table. We use thefollowing SELECT statement: SELECT * FROM Persons WHERE City LIKE 'sa%' The result-set will look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes Next, we want to select the persons living in a city that contains the pattern "nes" from the "Persons" table. We use thefollowing SELECT statement: SELECT * FROM Persons WHERE City LIKE '%nes%' The result-set will look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes Using the _ Wildcard Now we want to select the persons with a first name that starts with any character, followed by "la" from the "Persons" table. We use thefollowing SELECT statement: SELECT * FROM Persons WHERE FirstNameLIKE '_la' The result-set will look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes Next, we want to select the persons with a last name that starts with "S", followed by any character, followed by "end", followed by any character, followed by "on" from the "Persons" table. We use thefollowing SELECT statement: SELECT * FROM Persons WHERE LastName LIKE 'S_end_on' The result-set will look like this: P_Id LastName FirstName Address City 2 Svendson Tove Borgvn 23 Sandnes
  • 11. Page 11 of 77 Using the [charlist] Wildcard Now we want to select the persons with a last name that starts with "b" or "s" or "p" from the "Persons" table. We use thefollowing SELECT statement: SELECT * FROM Persons WHERE LastName LIKE '[bsp]%' The result-set will look like this: P_Id LastName FirstName Address City 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger Next, we want to select the persons with a last name that do not start with "b" or "s" or "p" from the "Persons" table. We use thefollowing SELECT statement: SELECT * FROM Persons WHERE LastName LIKE '[!bsp]%' The result-set will look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes The IN Operator The IN operator allows you to specify multiple values in a WHERE clause. SQLIN Syntax SELECT column_name(s) FROM table_name WHERE column_name IN (value1,value2,...) IN Operator Example The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger Now we want to select the persons with a last name equal to "Hansen" or "Pettersen" from the table above. We use thefollowing SELECT statement: SELECT * FROM Persons WHERE LastName IN ('Hansen','Pettersen') The result-set will look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 3 Pettersen Kari Storgt 20 Stavanger The BETWEEN Operator The BETWEEN operator selects a range of data between two values. Thevalues can be numbers, text, or dates. SQLBETWEEN Syntax SELECT column_name(s) FROM table_name WHERE column_name BETWEEN value1 AND value2 BETWEEN Operator Example The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger Now we want to select the persons with a last name alphabetically between "Hansen" and "Pettersen" from the table above. We use thefollowing SELECT statement: SELECT * FROM Persons WHERE LastName BETWEEN 'Hansen' AND 'Pettersen' The result-set will look like this: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes Note: The BETWEEN operator is treated differently in different databases! In some databases, persons with the LastName of "Hansen" or "Pettersen" will not be listed, because the BETWEEN operator only selects fields that are between and excluding the test values. In other databases, persons with theLastName of "Hansen" or "Pettersen" will be listed, because the BETWEEN operator selects fields that are between and including the test values. And in other databases, persons with theLastName of "Hansen" will be listed, but "Pettersen" will not be listed (like the example above), because the BETWEEN operator selects fields between thetest values, including the first test value and excluding the last test value. Therefore: Check how your database treats the BETWEEN operator. Example 2 To display the persons outsidethe range in theprevious example, use NOT BETWEEN: SELECT * FROM Persons WHERE LastName NOT BETWEEN 'Hansen' AND 'Pettersen' The result-set will look like this: P_Id LastName FirstName Address City 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger SQLAlias You can give a table or a column another name by using an alias. This can be a good thing to do if you have very long or complex table names or column names.
  • 12. Page 12 of 77 An alias name could be anything, but usually it is short. SQLAlias Syntax for Tables SELECT column_name(s) FROM table_name AS alias_name SQLAlias Syntax for Columns SELECT column_name AS alias_name FROM table_name Alias Example Assume we have a table called "Persons" and another table called "Product_Orders". We will give the table aliases of "p" and "po" respectively. Now we want to list all theorders that "Ola Hansen" is responsiblefor. We use thefollowing SELECT statement: SELECT po.OrderID, p.LastName, p.FirstName FROM Persons AS p, Product_Orders AS po WHERE p.LastName='Hansen' AND p.FirstName='Ola' The same SELECT statement without aliases: SELECT Product_Orders.OrderID, Persons.LastName, Persons.FirstName FROM Persons, Product_Orders WHERE Persons.LastName='Hansen' AND Persons.FirstName='Ola' As you'll see from the two SELECT statements above; aliases can make queries easier both to writeand to read. SQLJOIN The JOIN keyword is used in an SQL statement to query data from two or more tables, based on a relationship between certain columns in these tables. SQLINNER JOIN Syntax SELECT column_name(s) FROM table_name1 INNER JOIN table_name2 ON table_name1.column_name=table_name2.column_name PS: INNER JOIN is thesame as JOIN. SQLINNER JOIN Example The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger The "Orders" table: O_Id OrderNo P_Id 1 77895 3 2 44678 3 3 22456 1 4 24562 1 5 34764 15 Now we want to list all thepersons with any orders. We use thefollowing SELECT statement: SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo FROM Persons INNER JOIN Orders ON Persons.P_Id=Orders.P_Id ORDER BY Persons.LastName The result-set will look like this: LastName FirstName OrderNo Hansen Ola 22456 Hansen Ola 24562 Pettersen Kari 77895 Pettersen Kari 44678 The INNER JOIN keyword returns rows when there is at least one match in both tables. If there are rows in "Persons" that do not have matches in "Orders", thoserows will NOT be listed. SQLLEFT JOIN Keyword The LEFT JOIN keyword returns all rows from the left table (table_name1), even if there are no matches in the right table (table_name2). SQLLEFT JOIN Syntax SELECT column_name(s) FROM table_name1 LEFT JOIN table_name2 ON table_name1.column_name=table_name2.column_name PS: In some databases LEFT JOIN is called LEFT OUTER JOIN. SQLLEFT JOIN Example The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger The "Orders" table: O_Id OrderNo P_Id 1 77895 3 2 44678 3 3 22456 1 4 24562 1 5 34764 15 Now we want to list all thepersons and their orders - if any, from the tables above.
  • 13. Page 13 of 77 We use thefollowing SELECT statement: SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo FROM Persons LEFT JOIN Orders ON Persons.P_Id=Orders.P_Id ORDER BY Persons.LastName The result-set will look like this: LastName FirstName OrderNo Hansen Ola 22456 Hansen Ola 24562 Pettersen Kari 77895 Pettersen Kari 44678 Svendson Tove The LEFT JOIN keyword returns all therows from the left table (Persons), even if there are no matches in the right table (Orders). SQLRIGHT JOIN Keyword The RIGHT JOIN keyword returns all therows from the right table (table_name2), even if there are no matches in the left table (table_name1). SQLRIGHT JOIN Syntax SELECT column_name(s) FROM table_name1 RIGHT JOIN table_name2 ON table_name1.column_name=table_name2.column_name PS: In some databases RIGHT JOIN is called RIGHT OUTER JOIN. SQLRIGHT JOIN Example The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger The "Orders" table: O_Id OrderNo P_Id 1 77895 3 2 44678 3 3 22456 1 4 24562 1 5 34764 15 Now we want to list all theorders with containing persons - if any, from the tables above. We use thefollowing SELECT statement: SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo FROM Persons RIGHT JOIN Orders ON Persons.P_Id=Orders.P_Id ORDER BY Persons.LastName The result-set will look like this: LastName FirstName OrderNo Hansen Ola 22456 Hansen Ola 24562 Pettersen Kari 77895 Pettersen Kari 44678 34764 The RIGHT JOIN keyword returns all therows from the right table (Orders), even if there are no matches in the left table (Persons). SQLFULL JOIN Keyword The FULL JOIN keyword return rows when there is a match in one of the tables. SQLFULL JOIN Syntax SELECT column_name(s) FROM table_name1 FULL JOIN table_name2 ON table_name1.column_name=table_name2.column_name SQLFULL JOIN Example The "Persons" table: P_Id LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger The "Orders" table: O_Id OrderNo P_Id 1 77895 3 2 44678 3 3 22456 1 4 24562 1 5 34764 15 Now we want to list all thepersons and their orders, and all theorders with their persons. We use thefollowing SELECT statement: SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo FROM Persons FULL JOIN Orders ON Persons.P_Id=Orders.P_Id ORDER BY Persons.LastName The result-set will look like this: LastName FirstName OrderNo Hansen Ola 22456 Hansen Ola 24562 Pettersen Kari 77895 Pettersen Kari 44678 Svendson Tove 34764
  • 14. Page 14 of 77 The FULL JOIN keyword returns all the rows from the left table (Persons), and all the rows from theright table (Orders). If there are rows in "Persons" that do not have matches in "Orders", or if there are rows in "Orders" that do not have matches in "Persons", those rows will be listed as well. The SQLUNION Operator The UNION operator is used to combine the result-set of two or more SELECT statements. Notice that each SELECT statement within theUNION must have the same number of columns. Thecolumns must also have similar data types. Also, thecolumns in each SELECT statement must be in the same order. SQLUNION Syntax SELECT column_name(s) FROM table_name1 UNION SELECT column_name(s) FROM table_name2 Note: The UNION operator selects only distinct values by default. To allow duplicate values, use UNION ALL. SQLUNION ALL Syntax SELECT column_name(s) FROM table_name1 UNION ALL SELECT column_name(s) FROM table_name2 PS: The column names in theresult-set of a UNION are always equal to thecolumn names in the first SELECT statement in theUNION. SQLUNION Example Look at the following tables: "Employees_Norway": E_ID E_Name 01 Hansen, Ola 02 Svendson, Tove 03 Svendson, Stephen 04 Pettersen, Kari "Employees_USA": E_ID E_Name 01 Turner, Sally 02 Kent, Clark 03 Svendson, Stephen 04 Scott, Stephen Now we want to list all the different employees in Norway and USA. We use thefollowing SELECT statement: SELECT E_Name FROM Employees_Norway UNION SELECT E_Name FROM Employees_USA The result-set will look like this: E_Name Hansen, Ola Svendson, Tove Svendson, Stephen Pettersen, Kari Turner, Sally Kent, Clark Scott, Stephen Note: This command cannot be used to list all employees in Norway and USA. In the example above we have two employees with equal names, and only one of them will be listed. The UNION command selects only distinct values. SQLUNION ALL Example Now we want to list all employees in Norway and USA: SELECT E_Name FROM Employees_Norway UNION ALL SELECT E_Name FROM Employees_USA Result E_Name Hansen, Ola Svendson, Tove Svendson, Stephen Pettersen, Kari Turner, Sally Kent, Clark Svendson, Stephen Scott, Stephen SQLINTERSECT Operator The SQL INTERSECT query allows you to return theresults of 2 or more "select" queries. However, it only returns the rows selected by all queries. If a record exists in one query and not in the other, it will be omitted from the INTERSECT results. Each SQL statement within the SQL INTERSECT query must have thesame number of fields in the result sets with similar data types. The syntaxfor the SQL INTERSECT query is: select field1, field2, . field_n from tables INTERSECT select field1, field2, . field_n from tables; SQLINTERSECT Query - Singlefieldexample The following is an example of an SQL INTERSECT query that has one field with the same data type: select supplier_id from suppliers INTERSECT select supplier_id from orders;
  • 15. Page 15 of 77 In this SQL INTERSECT query example, if a supplier_id appeared in both the suppliers and orders table, it would appear in your result set. SQLINTERSECT Query - Using ORDER BY Clause example The following is an SQL INTERSECT query that uses an SQL ORDER BY clause: select supplier_id, supplier_name from suppliers where supplier_id > 2000 INTERSECT select company_id, company_name from companies where company_id > 1000 ORDER BY 2; Since thecolumn names are different between the two "select" statements, it is more advantageous to reference the columns in the SQL ORDER BY clause by their position in the result set. In this example, we've sorted theresults by supplier_name/ company_name in ascending order, as denoted by the"ORDER BY 2". The supplier_name/ company_name fields are in position #2 in the result set. SQLMINUS Operator The SQL MINUSquery returns all rows in the first SQL SELECT statement that are not returned in the second SQL SELECT statement. Each SQL SELECT statement within the SQL MINUSquery must have thesame number of fields in the result sets with similar data types. The syntaxfor the SQL MINUSquery is: select field1, field2, ... field_n from tables MINUS select field1, field2, ... field_n from tables; SQLMINUS Query - Singlefieldexample The following is an example of an SQL MINUSquery that has one field with the same data type: select supplier_id from suppliers MINUS select supplier_id from orders; This SQL Minus query example returns all supplier_id values that are in thesuppliers table and not in the orders table. What this means is that if a supplier_id value existed in the suppliers table and also existed in theorders table, thesupplier_id value would not appear in this result set. SQLMINUS Query - Using ORDER BY Clause example The following is an SQL MINUSquery that uses an ORDER BY clause: select supplier_id, supplier_name from suppliers where supplier_id > 2000 MINUS select company_id, company_name from companies where company_id > 1000 ORDER BY 2; In this SQL MINUSquery example, since the column names are different between thetwo "select" statements, it is more advantageous to reference the columns in the SQL ORDER BY clause by their position in theresult set. In this example, we've sorted the results by supplier_name / company_name in ascending order, as denoted by the"ORDER BY 2". The supplier_name/ company_name fields are in position #2 in the result set. The SQLSELECT INTO Statement The SELECT INTO statement selects data from one table and inserts it into a different table. The SELECT INTO statement is most often used to create backup copies of tables. SQLSELECT INTO Syntax We can select all columns into the new table: SELECT * INTO new_table_name [IN externaldatabase] FROM old_tablename Or we can select only the columns we want into the new table: SELECT column_name(s) INTO new_table_name [IN externaldatabase] FROM old_tablename SQLSELECT INTO Example Make a Backup Copy - Now we want to make an exact copy of the data in our "Persons" table. We use thefollowing SQL statement: SELECT * INTO Persons_Backup FROM Persons We can also use theIN clause to copy thetable into another database: SELECT * INTO Persons_Backup IN 'Backup.mdb' FROM Persons We can also copy only a few fields into the new table: SELECT LastName,FirstName INTO Persons_Backup FROM Persons SQLSELECT INTO - With a WHERE Clause We can also add a WHERE clause. The following SQL statement creates a "Persons_Backup" table with only thepersons who lives in the city "Sandnes": SELECT LastName,Firstname INTO Persons_Backup
  • 16. Page 16 of 77 FROM Persons WHERE City='Sandnes' SQLSELECT INTO - JoinedTables Selecting data from more than one table is also possible. The following example creates a "Persons_Order_Backup" table contains data from the two tables "Persons" and "Orders": SELECT Persons.LastName,Orders.OrderNo INTO Persons_Order_Backup FROM Persons INNER JOIN Orders ON Persons.P_Id=Orders.P_Id The GROUP BY Statement The GROUP BY statement is used in conjunction with the aggregate functions to group the result-set by one or more columns. SQLGROUP BY Syntax SELECT column_name, aggregate_function(column_name) FROM table_name WHERE column_name operator value GROUP BY column_name SQLGROUP BY Example We have the following "Orders" table: O_Id OrderDate OrderPrice Customer 1 2008/11/12 1000 Hansen 2 2008/10/23 1600 Nilsen 3 2008/09/02 700 Hansen 4 2008/09/03 300 Hansen 5 2008/08/30 2000 Jensen 6 2008/10/04 100 Nilsen Now we want to find the totalsum (total order) of each customer. We will have to use the GROUP BY statement to group thecustomers. We use thefollowing SQL statement: SELECT Customer,SUM(OrderPrice) FROM Orders GROUP BY Customer The result-set will look like this: Customer SUM(OrderPrice) Hansen 2000 Nilsen 1700 Jensen 2000 Nice! Isn't it? :) Let's see what happens if we omit the GROUP BY statement: SELECT Customer,SUM(OrderPrice) FROM Orders The result-set will look like this: Customer SUM(OrderPrice) Hansen 5700 Nilsen 5700 Hansen 5700 Hansen 5700 Jensen 5700 Nilsen 5700 The result-set above is not what we wanted. Explanation of why the above SELECT statement cannot be used: TheSELECT statement above has two columns specified (Customer and SUM(OrderPrice). The "SUM(OrderPrice)" returns a single value (that is the totalsum of the "OrderPrice" column), while "Customer" returns 6 values (one value for each row in the"Orders" table). This will therefore not give us the correct result. However, you have seen that theGROUP BY statement solves this problem. GROUP BY More Than One Column We can also use theGROUP BY statement on more than one column, like this: SELECT Customer,OrderDate,SUM(OrderPrice) FROM Orders GROUP BY Customer,OrderDate The HAVING Clause The HAVING clause was added to SQL because the WHERE keyword could not be used with aggregate functions. SQLHAVING Syntax SELECT column_name, aggregate_function(column_name) FROM table_name WHERE column_name operator value GROUP BY column_name HAVING aggregate_function(column_name) operator value SQLHAVING Example We have the following "Orders" table: O_Id OrderDate OrderPrice Customer 1 2008/11/12 1000 Hansen 2 2008/10/23 1600 Nilsen 3 2008/09/02 700 Hansen 4 2008/09/03 300 Hansen 5 2008/08/30 2000 Jensen 6 2008/10/04 100 Nilsen Now we want to find if any of the customers have a total order of less than 2000. We use thefollowing SQL statement: SELECT Customer,SUM(OrderPrice) FROM Orders GROUP BY Customer HAVING SUM(OrderPrice)<2000 The result-set will look like this: Customer SUM(OrderPrice) Nilsen 1700
  • 17. Page 17 of 77 Now we want to find if thecustomers "Hansen" or "Jensen" have a totalorder of more than 1500. We add an ordinary WHERE clause to the SQL statement: SELECT Customer,SUM(OrderPrice) FROM Orders WHERE Customer='Hansen' OR Customer='Jensen' GROUP BY Customer HAVING SUM(OrderPrice)>1500 The result-set will look like this: Customer SUM(OrderPrice) Hansen 2000 Jensen 2000 SQLViews A view is a virtual table. SQLCREATE VIEW Statement In SQL, a view is a virtual table based on theresult-set of an SQL statement. A view contains rows and columns, just like a real table. The fields in a view are fields from one or more real tables in thedatabase. You can add SQL functions, WHERE, and JOIN statements to a view and present thedata as if the data were coming from one single table. SQLCREATE VIEW Syntax CREATE VIEW view_name AS SELECT column_name(s) FROM table_name WHERE condition Note: A view always shows up-to-datedata! Thedatabase engine recreates the data, using the view's SQL statement, every time a user queries a view. SQLCREATE VIEW Examples If you have theNorthwind database you can see that it has several views installed by default. The view "Current Product List" lists all active products (products that arenot discontinued) from the"Products" table. Theview is created with thefollowing SQL: CREATE VIEW [Current Product List] AS SELECT ProductID,ProductName FROM Products WHERE Discontinued=No We can query theview above as follows: SELECT * FROM [Current Product List] Another view in the Northwind sample database selects every product in the"Products" table with a unit price higher than the average unit price: CREATE VIEW [Products Above Average Price] AS SELECT ProductName,UnitPrice FROM Products WHERE UnitPrice>(SELECT AVG(UnitPrice) FROM Products) We can query theview above as follows: SELECT * FROM [Products AboveAverage Price] Another view in the Northwind database calculates the totalsale for each category in 1997. Notethat this view selects its data from another view called "Product Sales for 1997": CREATE VIEW [Category Sales For 1997] AS SELECT DISTINCT CategoryName,Sum(ProductSales) AS CategorySales FROM [Product Sales for 1997] GROUP BY CategoryName We can query theview above as follows: SELECT * FROM [Category Sales For 1997] We can also add a condition to the query. Now we want to see the totalsale only for the category "Beverages": SELECT * FROM [Category Sales For 1997] WHERE CategoryName='Beverages' SQLUpdating a View You can updatea view by using the following syntax: SQLCREATE OR REPLACE VIEW Syntax CREATE OR REPLACE VIEW view_name AS SELECT column_name(s) FROM table_name WHERE condition Now we want to add the "Category" column to the "Current Product List" view. We will updatethe view with the following SQL: CREATE VIEW [Current Product List] AS SELECT ProductID,ProductName,Category FROM Products WHERE Discontinued=No SQLDropping a View You can delete a view with the DROP VIEW command. SQLDROP VIEW Syntax DROP VIEW view_name SQLCREATE INDEX Statement The CREATEINDEX statement is used to create indexes in tables. Indexes allow the database application to find data fast; without reading the whole table. Indexes An index can be created in a table to find data more quickly and efficiently. The users cannot see theindexes, they are just used to speed up searches/queries. Note: Updating a table with indexes takes more time than updating a table without (because the indexes also need an update). So you should only create indexes on columns (and tables) that will be frequently searched against. SQLCREATE INDEX Syntax Creates an index on a table. Duplicatevalues are allowed: CREATE INDEX index_name ON table_name (column_name)
  • 18. Page 18 of 77 SQLCREATE UNIQUE INDEX Syntax Creates a unique index on a table. Duplicate values are not allowed: CREATE UNIQUEINDEX index_name ON table_name (column_name) Note: The syntaxfor creating indexes varies amongst different databases. Therefore: Check thesyntax for creating indexes in your database. CREATE INDEX Example The SQL statement below creates an index named "PIndex" on the "LastName" column in the "Persons" table: CREATE INDEX PIndex ON Persons (LastName) If you want to create an index on a combination of columns, you can list thecolumn names within theparentheses, separated by commas: CREATE INDEX PIndex ON Persons (LastName, FirstName) Grant and Revoke privileges in Oracle Data ControlLanguage Statements are used to grant privileges on tables, views, sequences, synonyms, procedures to other users or roles. The DCL statements are GRANT :Use to grant privileges to other users or roles. REVOKE :Use to take back privileges granted to other users and roles. Privileges are of two types :  SYSTEM PRIVILEGES  OBJECT PRIVILEGES SystemPrivileges are normally granted by a DBA to users. Examples of systemprivileges are CREATESESSION, CREATE TABLE, CREATE USER etc. Object privileges means privileges on objects such as tables, views, synonyms, procedure. These are granted by owner of the object. Object Privileges are ALTER Change the table definition with the ALTER TABLE statement. DELETE Remove rows from thetable with the DELETE statement. Note: You must grant the SELECT privilege on the table along with the DELETE privilege. INDEX Create an index on the table with the CREATE INDEX statement. INSERT Add new rows to the table with the INSERT statement. REFERENCES Create a constraint that refers to thetable. You cannot grant this privilege to a role. SELECT Query the table with the SELECT statement. UPDATE Change data in the table with the UPDATEstatement. Note: You must grant the SELECT privilege on the table along with the UPDATEprivilege. Grant Grant is use to grant privileges on tables, view, procedure to other users or roles Examples: Supposeyou own emp table. Now you want to grant select,update,insert privilege on this table to other user “SAMI”. grant select, update, insert on emp to sami; Supposeyou want to grant all privileges on emp table to sami. Then grant all on emp to sami; Supposeyou want to grant select privilege on emp to all other users of the database. Then grant select on emp to public; Supposeyou want to grant updateand insert privilege on only certain columns not on all the columns then include the column names in grant statement. For example you want to grant updateprivilege on ename column only and insert privilege on empno and ename columns only. Then give the following statement grant update (ename),insert (empno, ename) on empto sami; To grant select statement on emp table to sami and to make sami be able further pass on this privilege you have to give WITH GRANT OPTION clausein GRANT statement like this. grant select on emp to sami with grant option; REVOKE Use to revoke privileges already granted to other users. For example to revoke select, update, insert privilege you have granted to Sami then give the following statement. revoke select, update, insert on emp from sami; To revoke select statement on emp granted to public give the following command. revoke select on empfrom public; To revoke updateprivilege on ename column and insert privilege on empno and ename columns give the following revoke statement. revoke update, insert on emp from sami; Note:You cannot take back column level privileges. Supposeyou just want to take back insert privilege on ename column then you have to first take back the whole insert privilege and then grant privilege on empno column. ROLES A role is a group of Privileges. A role is very handy in managing privileges, Particularly in such situation when number of users should have thesame set of privileges. For example you have four users :Sami, Scott, Ashi, Tanyain the database. To theseusers you want to grant select ,updateprivilege on emp table, select,delete privilege on dept table. To do this first create a role by giving thefollowing statement create role clerks
  • 19. Page 19 of 77 Then grant privileges to this role. grant select,update on emp to clerks; grant select,deleteon dept to clerks; Now grant this clerks role to users like this grant clerks to sami, scott, ashi, tanya ; Now Sami, Scott, Ashiand Tanyahave all the privileges granted on clerks role. Supposeafter one month you want grant delete on privilege on emp table all these users then just grant this privilege to clerks role and automatically all the users will have the privilege. grant delete on emp to clerks; If you want to take back updateprivilege on emp table from these users just take it back from clerks role. revoke update on emp from clerks; To Drop a role Drop role clerks; LISTING INFORMATION ABOUT PRIVILEGES To see which table privileges are granted by you to other users. SELECT * FROM USER_TAB_PRIVS_MADE To see which table privileges are granted to you by other users SELECT * FROM USER_TAB_PRIVS_RECD; To see which column level privileges are granted by you to other users. SELECT * FROM USER_COL_PRIVS_MADE To see which column level privileges are granted to you by other users SELECT * FROM USER_COL_PRIVS_RECD; To see which privileges are granted to roles SELECT * FROM USER_ROLE_PRIVS; Grant Privileges on Tables You can grant users various privileges to tables. Theseprivileges can be any combination of select, insert, update, delete, references, alter, and index. Below is an explanation of what each privilege means. Privilege Description Select Ability to query thetable with a select statement. Insert Ability to add new rows to the table with theinsert statement. Update Ability to updaterows in the table with the updatestatement. Delete Ability to delete rows from thetable with the delete statement. References Ability to create a constraint that refers to the table. Alter Ability to change the table definition with the alter table statement. Index Ability to create an index on the table with the create index statement. The syntaxfor granting privileges on a table is: grant privileges on object to user; For example, if you wanted to grant select, insert, update, and delete privileges on a table called suppliers to a user name smithj, you would execute the following statement: grant select, insert, update, delete on suppliers to smithj; You can also use theall keyword to indicate that you wish all permissions to be granted. For example: grant all on suppliers to smithj; If you wanted to grant select access on your table to all users, you could grant the privileges to thepublic keyword. For example: grant select on suppliers to public; Revoke Privileges on Tables Once you have granted privileges, you may need to revoke some or all of these privileges. To do this, you can execute a revoke command. You can revoke any combination of select, insert, update, delete, references, alter, and index. The syntaxfor revoking privileges on a table is: revoke privileges on object from user; For example, if you wanted to revoke delete privileges on a table called suppliers froma user named anderson, you would execute the following statement: revoke delete on suppliers from anderson; If you wanted to revoke all privileges on a table, you could use the all keyword. For example: revoke all on suppliers from anderson; If you had granted privileges to public (all users) and you wanted to revoke these privileges, you could execute thefollowing statement: revoke all on suppliers from public; Grant Privileges on Functions/Procedures When dealing with functions and procedures, you can grant users the ability to execute these functions and procedures. The Execute privilege is explained below: Privilege Description Execute Ability to compile the function/procedure. Ability to execute the function/procedure directly. The syntaxfor granting execute privileges on a function/procedure is: grant execute on object to user; For example, if you had a function called Find_Value and you wanted to grant execute access to the user named smithj, you would execute the following statement: grant execute on Find_Value to smithj; If you wanted to grant all users the ability to execute this function, you would execute the following: grant execute on Find_Value to public; Revoke Privileges on Functions/Procedures Once you have granted execute privileges on a function or procedure, you may need to revoke theseprivileges from a user. To do this, you can execute a revoke command. The syntaxfor the revoking privileges on a function or procedure is: revoke execute on object from user;
  • 20. Page 20 of 77 If you wanted to revoke execute privileges on a function called Find_Value from a user named anderson, you would execute the following statement: revoke execute on Find_Value from anderson; If you had granted privileges to public (all users) and you wanted to revoke these privileges, you could execute thefollowing statement: revoke execute on Find_Value from public; SEQUENCES CREATE SEQUENCE The syntax for a sequence is: CREATE SEQUENCE sequence_name MINVALUEvalue MAXVALUEvalue START WITH value INCREMENT BY value CACHE value; For example: CREATE SEQUENCE supplier_seq MINVALUE1 MAXVALUE999999999999999999999999999 START WITH 1 INCREMENT BY 1 CACHE 20; ALTER SEQUENCE Increment a sequence by a certain amount: ALTER SEQUENCE <sequence_name> INCREMENT BY <integer>; ALTER SEQUENCE seq_inc_by_ten INCREMENT BY10; Change the maximum value of a sequence: ALTER SEQUENCE <sequence_name> MAXVALUE<integer>; ALTER SEQUENCE seq_maxval MAXVALUE 10; Set the sequence to cycle or not cycle: ALTER SEQUENCE <sequence_name> <CYCLE | NOCYCLE>; ALTER SEQUENCE seq_cycle NOCYCLE; Configure the sequence to cache a value: ALTER SEQUENCE <sequence_name> CACHE <integer> | NOCACHE; ALTER SEQUENCE seq_cache NOCACHE; Set whetherornot the values are to be returnedin order ALTER SEQUENCE <sequence_name> <ORDER | NOORDER>; ALTER SEQUENCE seq_order NOORDER; 1 - Overview of PL/SQL This chapter introduces themain features of the PL/SQL language. It shows how PL/SQL deals with the challenges of database programming, and how you can reuse techniques that you know from other programming languages. Advantages of PL/SQL PL/SQL is a completely portable, high-performance transaction processing language that offers the following advantages:  Support for SQL  Support for object-oriented programming  Better performance  Higher productivity  Full portability  Tight integration with Oracle  Tight security Tight Integration with SQL The PL/SQL language is tightly integrated with SQL. You do not have to translate between SQL and PL/SQL datatypes:a NUMBER or VARCHAR2 column in thedatabase is stored in a NUMBER or VARCHAR2 variable in PL/SQL. This integration saves you both learning time and processing time. Special PL/SQL language features let you work with table columns and rows without specifyingthe datatypes, saving on maintenance work when the table definitions change. Running a SQL query and processing the result set is as easy in PL/SQL as opening a text file and processing each line in popular scripting languages. Using PL/SQL to access metadata about database objects and handle database error conditions, you can writeutility programs for database administration that are reliable and produce readable output about thesuccess of each operation. Many databasefeatures, such as triggers and object types, makeuse of PL/SQL. You can write thebodies of triggers and methods for object types in PL/SQL. Support for SQL SQL has become the standard database language because it is flexible, powerful, and easy to learn. A few English-like commands such as SELECT, INSERT, UPDATE, and DELETE make it easy to manipulate thedata stored in a relational database. PL/SQL lets you use all theSQL data manipulation, cursor control, and transaction control commands, as well as all theSQL functions, operators, and pseudocolumns. This extensive SQL support lets you manipulateOracle data flexibly and safely. Also, PL/SQL fully supports SQLdatatypes, reducing theneed to convert data passed between your applications and the database. PL/SQL also supports dynamicSQL, a programming technique that makes your applications more flexible and versatile. Your programs can build and process SQL data definition, data control, and session control statements at run time, without knowing details such as table names and WHERE clauses in advance. Better Performance Without PL/SQL, Oracle must process SQL statements one at a time. Programs that issue many SQL statements require multiple calls to the database, resulting in significant network and performance overhead. With PL/SQL, an entire block of statements can be sent to Oracle at one time. This can drastically reduce network traffic between thedatabase and an application. As Figure 1-1 shows, you can use PL/SQL blocks and subprograms to group SQL statements before sending them to thedatabase for execution. PL/SQL even has language features to further speed up SQL statements that are issued inside a loop.
  • 21. Page 21 of 77 PL/SQL stored procedures are compiled once and stored in executable form, so procedure calls are efficient. Because stored procedures execute in thedatabase server, a single call over the network can start a large job. This division of work reduces network traffic and improves responsetimes. Stored procedures are cached and shared among users, which lowers memory requirements and invocation overhead. Figure 1-1 PL/SQL Boosts Performance Description of the illustration lnpls005.gif Higher Productivity PL/SQL extends tools such as Oracle Forms and Oracle Reports. With PL/SQL in these tools, you can use familiar language constructs to build applications. For example, you can use an entire PL/SQL block in an Oracle Forms trigger, instead of multiple trigger steps, macros, or user exits. PL/SQL is the same in all environments. Once you learn PL/SQL with one Oracle tool, you can transfer your knowledge to other tools. Full Portability Applications written in PL/SQL can run on any operating systemand platformwhere the Oracle database runs. With PL/SQL, you can writeportable program libraries and reuse them in different environments. Tight Security PL/SQL stored procedures move application code from the client to theserver, where you can protect it from tampering, hide the internal details, and restrict who has access. For example, you can grant users access to a procedure that updates a table, but not grant them access to the table itself or to the text of the UPDATE statement. Triggers written in PL/SQL can control or record changes to data, making sure that all changes obey your business rules. Support for Object-Oriented Programming Object types arean ideal object-oriented modeling tool, which you can use to reduce the cost and time required to build complex applications. Besides allowing you to create softwarecomponents that are modular, maintainable, and reusable, object types allow different teams of programmers to develop softwarecomponents concurrently. By encapsulating operations with data, object types let you move data-maintenance code out of SQL scripts and PL/SQL blocks into methods. Also, object types hide implementation details, so that you can change the details without affecting client programs. In addition, object types allow for realistic data modeling. Complex real-world entities and relationships map directly into object types. This direct mapping helps your programs better reflect the world they are trying to simulate. Features of PL/SQL PL/SQL has the following features:  PL/SQL is tightly integrated with SQL.  It offers extensive error checking.  It offers numerous data types.  It offers a variety of programming structures.  It supports structured programming through functions and procedures.  It supports object oriented programming.  It supports developingweb applications and server pages. 2 - Fundamentals of the PL/SQL Language This chapter focuses on thedetailed aspects of the language. Like other programming languages, PL/SQL has a character set, reserved words, punctuation, datatypes, and fixed syntaxrules. Character Set You writea PL/SQL program as lines of text using a specific set of characters: Upper- and lower-case letters A .. Z and a .. z Numerals 0 .. 9 Symbols ( ) + - * / < > = ! ~ ^ ; : . ' @ % , " # $ & _ | { } ? [ ] Tabs, spaces, and carriage returns PL/SQL keywords are not case-sensitive, so lower-case letters are equivalent to corresponding upper-caseletters except within string and character literals. Lexical Units A line of PL/SQL text contains groups of characters known as lexical units: delimiters (simple and compound symbols) identifiers, which include reserved words literals comments To improve readability, you can separate lexical units by spaces. In fact, you must separate adjacent identifiers by a space or punctuation. Thefollowing line is not allowed because the reserved words END and IF are joined: IF x > y THEN high := x; ENDIF; -- not allowed, must be END IF
  • 22. Page 22 of 77 You cannot embed spaces inside lexical units except for string literals and comments. For example, thefollowing line is not allowed because the compound symbolfor assignment (:=) is split: count : = count + 1; -- not allowed, must be := To show structure, you can split lines using carriage returns, and indent lines using spaces or tabs. The formatting makes the IF statement on the right more readable: IF x>y THEN max:=x;ELSE max:=y;END IF; | IF x > y THEN | max := x; | ELSE | max := y; | END IF; Delimiters A delimiter is a simple or compound symbolthat has a special meaning to PL/SQL. For example, you use delimiters to represent arithmetic operations such as addition and subtraction. Identifiers You use identifiers to name PL/SQL program items and units, which include constants, variables, exceptions, cursors, cursor variables, subprograms, and packages. Some examples of identifiers follow: An identifier consists of a letter optionally followed by more letters, numerals, dollar signs, underscores, and number signs. Other characters such as hyphens, slashes, and spaces are not allowed, as the following examples show: mine&yours -- not allowed because ofampersand debit-amount -- not allowed because ofhyphen on/off -- not allowed because ofslash user id -- not allowed because ofspace Adjoining and trailing dollar signs, underscores, and number signs are allowed: money$$$tree SN## try_again_ You can use upper, lower, or mixed case to write identifiers. PL/SQL is not case sensitive except within string and character literals. If the only difference between identifiers is the case of corresponding letters, PL/SQL considers them the same: lastname LastName -- same as lastname LASTNAME -- same as lastname and LastName The sizeof an identifier cannot exceed 30 characters. Every character, including dollar signs, underscores, and number signs, is significant. For example, PL/SQL considers the following identifiers to be different: lastname last_name Identifiers should be descriptive. Avoid obscure names such as cpm. Instead, use meaningful names such as cost_per_thousand. Reserved Words Some identifiers, called reserved words, have a special syntacticmeaning to PL/SQL. For example, thewords BEGIN and END are reserved. Trying to redefine a reserved word causes a compilation error. Instead, you can embed reserved words as part of a longer identifier: DECLARE -- end BOOLEAN; -- not allowed; causes compilation error end_of_game BOOLEAN; -- allowed BEGIN NULL; END; / PredefinedIdentifiers Identifiers globally declared in package STANDARD, such as the exception INVALID_NUMBER, can be redeclared. However, redeclaring predefined identifiers is error pronebecause your local declaration overrides theglobal declaration. QuotedIdentifiers For flexibility, PL/SQL lets you enclose identifiers within double quotes. Quoted identifiers are seldom needed, but occasionally they can be useful. They can contain any sequence of printable characters including spaces but excluding double quotes. Thus, the following identifiers are valid: "X+Y" "last name" "on/off switch" "employee(s)" "*** header info ***" The maximum size of a quoted identifier is 30 characters not counting the double quotes. Though allowed, using PL/SQL reserved words as quoted identifiers is a poor programming practice. Literals A literal is an explicit numeric, character, string, or Boolean value not represented by an identifier. The numeric literal 147 and the Boolean literal FALSE are examples.  Numeric Literals  Character Literals  String Literals  Boolean Literals  Datetime Literals Comments The PL/SQL compiler ignores comments, but you should not. Adding comments to your program promotes readability and aids understanding. Generally, you use comments to describe the purposeand use of each code segment. PL/SQL supportstwo comment styles: single-line and multi-line. Single-Line Comments Single-line comments begin with a double hyphen (--) anywhereon a line and extend to the end of the line. A few examples follow: DECLARE howmany NUMBER; BEGIN -- begin processing SELECT count(*) INTO howmany FROM user_objects WHERE object_type = 'TABLE'; -- Check number oftables howmany := howmany * 2; -- Compute some other value END; / Notice that comments can appear within a statement at theend of a line. While testing or debugging a program, you might want to disable a line of code. The following example shows how you can "comment-out" theline: -- DELETE FROM employees WHERE comm_pct IS NULL; Multi-lineComments
  • 23. Page 23 of 77 Multi-line comments begin with a slash-asterisk (/*), end with an asterisk-slash (*/), and can span multiple lines. Some examples follow: DECLARE some_condition BOOLEAN; pi NUMBER := 3.1415926; radius NUMBER := 15; area NUMBER; BEGIN /* Perform some simple tests and assignments */ IF 2 + 2 = 4 THEN some_condition := TRUE; /* We expect this THEN to always be done */ END IF; /* The following line computes the area ofa circle using pi, which is the ratio between the circumference and diameter. */ area := pi * radius**2; END; / You can use multi-line comment delimiters to comment-out whole sections of code: /* LOOP FETCH c1 INTO emp_rec; EXIT WHEN c1%NOTFOUND; ... END LOOP; */ Restrictions on Comments You cannot nest comments. You cannot use single-line comments in a PL/SQL block that will be processed by an Oracle Precompiler program because end-of-line characters are ignored. As a result, single-line comments extend to the end of the block, not just to theend of a line. In this case, use the/* */ notation instead. Declarations Your program stores values in variables and constants. As the program executes, the values of variables can change, but the values of constants cannot. You can declare variables and constants in thedeclarative part of any PL/SQL block, subprogram, or package. Declarations allocate storage space for a value, specify its datatype, and name the storage location so that you can reference it. A couple of examples follow: DECLARE birthday DATE; emp_count SMALLINT := 0; The first declaration names a variable of type DATE. Thesecond declaration names a variable of typeSMALLINT and uses theassignment operator to assign an initial value of zero to the variable. The next examples show that the expression following theassignment operator can be arbitrarily complex and can refer to previously initialized variables: DECLARE pi REAL := 3.14159; radius REAL := 1; area REAL := pi * radius**2; BEGIN NULL; END; / By default, variables are initialized to NULL, so it is redundant to include ":= NULL" in a variable declaration. To declare a constant, put thekeyword CONSTANT before thetypespecifier: DECLARE credit_limit CONSTANT REAL := 5000.00; max_days_in_year CONSTANT INTEGER := 366; urban_legend CONSTANT BOOLEAN := FALSE; BEGIN NULL; END; / This declaration names a constant of type REAL and assigns an unchangeable value of 5000 to the constant. A constant must be initialized in its declaration. Otherwise, you get a compilation error. Using DEFAULT You can use the keyword DEFAULT instead of theassignment operator to initialize variables. For example, thedeclaration blood_type CHAR := 'O'; can be rewritten as follows: blood_type CHAR DEFAULT 'O'; Use DEFAULT for variables that have a typicalvalue. Use the assignment operator for variables (such as counters and accumulators) that have no typicalvalue. For example: hours_worked INTEGER DEFAULT 40; employee_count INTEGER := 0; You can also use DEFAULT to initialize subprogram parameters, cursor parameters, and fields in a user-defined record. Using NOT NULL Besides assigning an initial value, declarations can imposethe NOT NULL constraint: DECLARE acct_id INTEGER(4) NOT NULL := 9999; You cannot assign nulls to a variable defined as NOT NULL. If you try, PL/SQL raises the predefined exception VALUE_ERROR. The NOT NULL constraint must be followed by an initialization clause. PL/SQL provide subtypes NATURALN and POSITIVEN that are predefined as NOT NULL. You can omit theNOT NULL constraint when declaring variables of these types, and you must include an initialization clause. Using the %TYPE Attribute The %TYPE attributeprovides the datatypeof a variable or database column. In the following example, %TYPE provides thedatatypeof a variable: DECLARE credit NUMBER(7,2); debit credit%TYPE; name VARCHAR2(20) := 'JoHn SmItH'; -- If we increase the length ofNAME, the other variables -- become longer too. upper_name name%TYPE := UPPER(name); lower_name name%TYPE := LOWER(name);
  • 24. Page 24 of 77 init_name name%TYPE := INITCAP(name); BEGIN NULL; END; / Variables declared using %TYPE are treated like thosedeclared using a datatypespecifier. For example, given the previous declarations, PL/SQL treats debit like a REAL(7,2) variable. A %TYPE declaration can also include an initialization clause. The %TYPE attributeis particularly useful when declaring variables that refer to database columns. You can reference a table and column, or you can reference an owner, table, and column, as in DECLARE -- If the length ofthe column ever changes, this code -- will use the new length automatically. the_trigger user_triggers.trigger_name%TYPE; BEGIN NULL; END; / When you use table_name.column_name.TYPE to declare a variable, you do not need to know the actual datatype, and attributes such as precision, scale, and length. If the database definition of thecolumn changes, the datatypeof thevariable changes accordingly at run time. %TYPE variables do not inherit the NOT NULL column constraint. In the next example, even though the database column employee_id is defined as NOT NULL, you can assign a null to the variable my_empno: DECLARE my_empno employees.employee_id%TYPE; BEGIN my_empno := NULL; -- this works END; / Using the %ROWTYPE Attribute The %ROWTYPE attributeprovides a record typethat represents arow in a table (or view). The record can storean entire row of data selected from thetable, or fetched from a cursor or strongly typed cursor variable: DECLARE -- %ROWTYPE can include all the columns in a table... emp_rec employees%ROWTYPE; -- ...or a subset ofthe columns, based on a cursor. CURSOR c1 IS SELECT department_id, department_name FROM departments; dept_rec c1%ROWTYPE; -- Could even make a %ROWTYPE with columns from multiple tables. CURSOR c2 IS SELECT employee_id, email, employees.manager_id, location_id FROM employees, departments WHERE employees.department_id = departments.department_id; join_rec c2%ROWTYPE; BEGIN -- We know EMP_REC can hold a row fromthe EMPLOYEES table. SELECT * INTO emp_rec FROM employees WHERE ROWNUM < 2; -- We can refer to the fields of EMP_REC using column names -- from the EMPLOYEES table. IF emp_rec.department_id = 20 AND emp_rec.last_name = 'JOHNSON' THEN emp_rec.salary := emp_rec.salary * 1.15; END IF; END; / Columns in a row and corresponding fields in a record have the same names and datatypes. However, fields in a %ROWTYPE record do not inherit the NOT NULL column constraint. Aggregate Assignment Although a %ROWTYPE declaration cannot include an initialization clause, there are ways to assign values to all fields in a record at once. You can assign one record to another if their declarations refer to the same table or cursor. For example, the following assignment is allowed: DECLARE dept_rec1 departments%ROWTYPE; dept_rec2 departments%ROWTYPE; CURSOR c1 IS SELECT department_id, location_id FROM departments; dept_rec3 c1%ROWTYPE; BEGIN dept_rec1 := dept_rec2; -- allowed -- dept_rec2 refers to a table, dept_rec3 refers to a cursor -- dept_rec2 := dept_rec3; -- not allowed END; / You can assign a list of column values to a record by using the SELECT or FETCH statement, as the following example shows. The column names must appear in the order in which they were defined by the CREATE TABLE or CREATE VIEW statement. DECLARE dept_rec departments%ROWTYPE; BEGIN SELECT * INTO dept_rec FROM departments WHERE department_id = 30 and ROWNUM < 2; END; / However, there is no constructor for a record type, so you cannot assign a list of column values to a record by using an assignment statement. Using Aliases Select-list items fetched from a cursor associated with %ROWTYPE must have simple names or, if they are expressions, must have aliases. Thefollowing example uses an alias called complete_name to represent the concatenation of two columns: BEGIN -- We assign an alias (COMPLETE_NAME) to the expression value, because -- it has no column name. FOR itemIN ( SELECT first_name || ' ' || last_name complete_name FROM employees WHERE ROWNUM < 11 ) LOOP -- Now we can refer to the field in the record using this alias. dbms_output.put_line('Employee name: ' || item.complete_name); END LOOP; END; / Restrictions on Declarations PL/SQL does not allow forward references. You must declare a variable or constant before referencing it in other statements, including other declarative statements.
  • 25. Page 25 of 77 PL/SQL does allow theforward declaration of subprograms. Some languages allow you to declare a list of variables that have thesame datatype. PL/SQL does not allow this. You must declare each variable separately: DECLARE -- Multiple declarations not allowed. -- i, j, k, l SMALLINT; -- Instead, declare each separately. i SMALLINT; j SMALLINT; -- To save space, you can declare more than one on a line. k SMALLINT; l SMALLINT; BEGIN NULL; END; / Assigning Values to Variables You can use assignment statements to assign values to variables. For example, the following statement assigns a new value to the variable bonus, overwriting its old value: bonus := salary * 0.15; Unless you expressly initialize a variable, its value is undefined (NULL).Variables and constants are initialized every time a block or subprogram is entered. By default, variables are initialized to NULL: DECLARE counter INTEGER; BEGIN -- COUNTER is initially NULL, so 'COUNTER + 1' is also null. counter := counter + 1; IF counter IS NULL THEN dbms_output.put_line('Sure enough, COUNTER is NULL not 1.'); END IF; END; / To avoid unexpected results, never reference a variable before you assign it a value. The expression following theassignment operator can be arbitrarily complex, but it must yield a datatypethat is the same as or convertible to thedatatypeof the variable. Assigning Boolean Values Only the values TRUE, FALSE, and NULL can be assigned to a Boolean variable. You can assign these literal values, or expressions such as comparisons using relational operators. DECLARE done BOOLEAN; -- DONE is initially NULL counter NUMBER := 0; BEGIN done := FALSE; -- Assign a literal value WHILE done != TRUE -- Compare to a literal value LOOP counter := counter + 1; done := (counter > 500); -- If counter > 500, DONE = TRUE END LOOP; END; / Assigning a SQL Query Result to a PL/SQL Variable You can use the SELECT statement to have Oracle assign values to a variable. For each item in the select list, there must be a corresponding, type-compatiblevariable in the INTO list. For example: DECLARE emp_id employees.employee_id%TYPE := 100; emp_name employees.last_name%TYPE; wages NUMBER(7,2); BEGIN SELECT last_name, salary + (salary * nvl(commission_pct,0)) INTO emp_name, wages FROM employees WHERE employee_id = emp_id; dbms_output.put_line('Employee ' || emp_name || ' might make ' || wages); END; / Because SQL does not have a Boolean type, you cannot select column values into a Boolean variable. PL/SQL Expressions and Comparisons Expressions are constructed using operands and operators. An operand is a variable, constant, literal, or function call that contributes a value to an expression. An example of a simple arithmetic expression follows: -X / 2 + 3 Unary operators such as thenegation operator (-) operateon one operand; binary operators such as the division operator (/) operateon two operands. PL/SQL has no ternary operators. The simplest expressions consist of a single variable, which yields a value directly. PL/SQL evaluates an expression by combining thevalues of the operands in ways specified by the operators. An expression always returns a single value. PL/SQL determines thedatatypeof this value by examining the expression and the context in which it appears. Operator Precedence The operations within an expression are done in a particular order depending on their precedence (priority). Table2-1 shows the default order of operations from first to last (top to bottom). Table 2-1 Order of Operations Operator Operation ** exponentiation +, - identity, negation *, / multiplication, division +, -, || addition, subtraction, concatenation =, <, >, <=, >=, <>, !=, ~=, ^=, IS NULL, LIKE, BETWEEN, IN comparison NOT logical negation AND conjunction OR inclusion Logical Operators
  • 26. Page 26 of 77 The logical operators AND, OR, and NOT follow the tri-statelogic shown in Table 2-2. AND and OR are binary operators; NOT is a unary operator. Table 2-2 Logic Truth Table x y x AND y x OR y NOT x TRUE TRUE TRUE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE NULL NULL TRUE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE TRUE FALSE NULL FALSE NULL TRUE NULL TRUE NULL TRUE NULL NULL FALSE FALSE NULL NULL NULL NULL NULL NULL NULL Order of Evaluation When you do not use parentheses to specify the order of evaluation, operator precedence determines theorder. Compare thefollowing expressions: NOT (valid AND done) | NOT valid AND done If the Boolean variables valid and done have the value FALSE, the first expression yields TRUE. However, the second expression yields FALSE because NOT has a higher precedence than AND. Therefore, the second expression is equivalent to: (NOT valid) AND done In the following example, notice that when valid has the value FALSE, the whole expression yields FALSE regardless of the value of done: valid AND done Likewise, in the next example, when valid has the value TRUE, the whole expression yields TRUE regardless of the value of done: valid OR done Short-Circuit Evaluation When evaluating a logical expression, PL/SQL uses short-circuit evaluation. That is, PL/SQL stops evaluating theexpression as soon as the result can be determined. This lets you write expressions that might otherwisecause an error. Consider the following OR expression: DECLARE on_hand INTEGER := 0; on_order INTEGER := 100; BEGIN -- Does not cause divide-by-zero error; evaluation stops after 1st expr. IF (on_hand = 0) OR ((on_order / on_hand) < 5) THEN dbms_output.put_line('There are no more widgets left!'); END IF; END; / When thevalue of on_hand is zero, the left operand yields TRUE, so PL/SQL does not evaluate the right operand. If PL/SQL evaluated both operands before applyingthe OR operator, the right operand would cause a division by zero error. Comparison Operators Comparison operators compare one expression to another. Theresult is always true, false, or null. Typically, you usecomparison operators in conditional control statements and in the WHERE clause of SQL data manipulation statements. Here are some examples of comparisons for different types: DECLARE PROCEDURE assert(assertion VARCHAR2, truth BOOLEAN) IS BEGIN IF truth IS NULL THEN dbms_output.put_line('Assertion ' || assertion || ' is unknown (NULL)'); ELSIF truth = TRUE THEN dbms_output.put_line('Assertion ' || assertion || ' is TRUE'); ELSE dbms_output.put_line('Assertion ' || assertion || ' is FALSE'); END IF; END; BEGIN assert('2 + 2 = 4', 2 + 2 = 4); assert('10 > 1', 10 > 1); assert('10 <= 1', 10 <= 1); assert('5 BETWEEN 1 AND 10', 5 BETWEEN 1 AND 10); assert('NULL != 0', NULL != 0); assert('3 IN (1,3,5)', 3 IN (1,3,5)); assert('''A'' < ''Z''', 'A' < 'Z'); assert('''baseball'' LIKE ''%all%''', 'baseball' LIKE '%all%'); assert('''suit'' || ''case'' = ''suitcase''', 'suit' || 'case' = 'suitcase'); END; / Relational Operators Operator Meaning = equal to <>, !=, ~=, ^= not equal to < less than > greater than <= less than or equal to >= greater than or equal to IS NULL Operator The IS NULL operator returns the Boolean value TRUE if its operand is null or FALSE if it is not null. Comparisons involving nulls always yield NULL. Test whether a value is null as follows: IF variable IS NULL THEN ... LIKE Operator You use theLIKE operator to compare a character, string, or CLOB value to a pattern. Case is significant. LIKE returns the Boolean value TRUE if thepatterns match or FALSE if they do not match. The patterns matched by LIKE can include two special-purposecharacters called wildcards. An underscore (_) matches exactly one character; a percent sign (%) matches zero or more characters. For example, if thevalue of ename is 'JOHNSON', thefollowing expression is true:
  • 27. Page 27 of 77 ename LIKE 'J%S_N' To search for the percent sign and underscore characters, you define an escape character and put that character before the percent sign or underscore. The following example uses the backslash as the escape character, so that thepercent sign in the string does not act as a wildcard: IF sale_sign LIKE '50% off!' ESCAPE '' THEN... BETWEEN Operator The BETWEEN operator tests whether a value lies in a specified range. It means "greater than or equal to low value and less than or equal to high value." For example, the following expression is false: 45 BETWEEN 38 AND 44 IN Operator The IN operator tests set membership. It means "equal to any member of." The set can contain nulls, but they are ignored. For example, the following expression tests whether a value is part of a set of values: letter IN ('a','b','c') Be careful when inverting this condition. Expressions of theform: value NOT IN set yield FALSE if the set contains a null. Concatenation Operator Double vertical bars (||) serve as the concatenation operator, which appends one string (CHAR, VARCHAR2, CLOB, or the equivalent Unicode-enabled type) to another. For example, the expression 'suit' || 'case' returns the following value: 'suitcase' If both operands have datatype CHAR, the concatenation operator returns a CHAR value. If either operand is a CLOB value, the operator returns a temporary CLOB. Otherwise, it returns a VARCHAR2 value. CASE Expressions A CASE expression selects a result from one or more alternatives, and returns the result. Although it contains a block that might stretch over several lines, it really is an expression that forms part of a larger statement, such as an assignment or a procedure call. The CASE expression uses a selector, an expression whose value determines which alternative to return. A CASE expression has thefollowing form: CASE selector WHEN expression1 THEN result1 WHEN expression2 THEN result2 ... WHEN expressionN THEN resultN [ELSE resultN+1] END The selector is followed by one or more WHEN clauses, which are checked sequentially. The value of theselector determines which clause is evaluated. Thefirst WHEN clause that matches the value of the selector determines theresult value, and subsequent WHEN clauses are not evaluated. For example: DECLARE grade CHAR(1) := 'B'; appraisal VARCHAR2(20); BEGIN appraisal := CASE grade WHEN 'A' THEN 'Excellent' WHEN 'B' THEN 'Very Good' WHEN 'C' THEN 'Good' WHEN 'D' THEN 'Fair' WHEN 'F' THEN 'Poor' ELSE 'No such grade' END; dbms_output.put_line('Grade ' || grade || ' is ' || appraisal); END; / The optionalELSE clause works similarly to the ELSE clause in an IF statement. If thevalue of the selector is not one of thechoices covered by a WHEN clause, theELSE clause is executed. If no ELSE clause is provided and none of the WHEN clauses are matched, the expression returns NULL. An alternative to the CASE expression is the CASE statement, where each WHEN clause can be an entire PL/SQL block. SearchedCASEExpression PL/SQL also provides a searched CASE expression, which lets you test different conditions instead of comparing a single expression to various values. It has the form: CASE WHEN search_condition1 THEN result1 WHEN search_condition2 THEN result2 ... WHEN search_conditionN THEN resultN [ELSE resultN+1] END; A searched CASE expression has no selector. Each WHEN clause contains a search condition that yields a Boolean value, so you can test different variables or multiple conditions in a single WHEN clause. For example: DECLARE grade CHAR(1) := 'B'; appraisal VARCHAR2(120); id NUMBER := 8429862; attendance NUMBER := 150; min_days CONSTANT NUMBER := 200; FUNCTION attends_this_school(id NUMBER) RETURN BOOLEAN IS BEGIN RETURN TRUE; END; BEGIN appraisal := CASE WHEN attends_this_school(id) = FALSE THEN 'N/A - Student not enrolled' -- Have to put this condition early to detect -- good students with bad attendance WHEN grade = 'F' OR attendance < min_days THEN 'Poor (poor performance or bad attendance)' WHEN grade = 'A' THEN 'Excellent' WHEN grade = 'B' THEN 'Very Good' WHEN grade = 'C' THEN 'Good' WHEN grade = 'D' THEN 'Fair'
  • 28. Page 28 of 77 ELSE 'No such grade' END; dbms_output.put_line('Result for student ' || id || ' is ' || appraisal); END; / The search conditions are evaluated sequentially. The Boolean value of each search condition determines which WHEN clause is executed. If a search condition yields TRUE, its WHEN clause is executed. After any WHEN clause is executed, subsequent search conditions are not evaluated. If none of the search conditions yields TRUE, theoptional ELSE clause is executed. If no WHEN clause is executed and no ELSE clause is supplied, thevalue of the expression is NULL. 3 - PL/SQL Datatypes Every constant, variable, and parameter has a datatype (or type), which specifies a storage format, constraints, and valid range of values. PL/SQL provides many predefined datatypes. For instance, you can choose from integer, floating point, character, Boolean, date, collection, reference, and large object (LOB) types. PL/SQLalso lets you define your own subtypes. This chapter covers thebasic types used frequently in PL/SQL programs. Later chapters cover the more specialized types. Overview of Predefined PL/SQL Datatypes A scalar typehas no internal components. It holds a single value, such as a number or character string. A composite typehas internal components that can be manipulated individually, such as the elements of an array. A reference typeholds values, called pointers, that designate other program items. A LOB typeholds values, called lob locators, that specify thelocation of large objects, such as text blocks or graphic images, that are stored separately from other database data. Figure 3-1 shows the predefined PL/SQL datatypes. Thescalar types fall into four families, which storenumber, character, Boolean, and date/time data, respectively. Figure 3-1 Built-in Datatypes Description of the illustration lnpls006.gif PL/SQL Number Types Number types let you storenumeric data (integers, real numbers, and floating-point numbers), represent quantities, and do calculations. BINARY_INTEGER You use theBINARY_INTEGER datatypeto storesigned integers. Its magnitude range is - 2**31 .. 2**31. BINARY_INTEGER values require less storage than NUMBER values. Arithmetic operations on BINARY_INTEGER values are also faster than NUMBER arithmetic. BINARY_INTEGER and PLS_INTEGER both have theseadvantages. Because PLS_INTEGER was faster in earlier releases, you might use it instead of BINARY_INTEGER in code that will run on older databases. BINARY_INTEGER Subtypes A base type is thedatatypefromwhich a subtypeis derived. A subtype associates a base typewith aconstraint and so defines a subset of values. For your convenience, PL/SQL predefines the following BINARY_INTEGER subtypes: NATURAL NATURALN POSITIVE POSITIVEN SIGNTYPE The subtypes NATURAL and POSITIVE let you restrict an integer variable to non-negative or positivevalues, respectively. NATURALN and POSITIVEN prevent theassigning of nulls to an integer variable. SIGNTYPE lets you restrict an integer variable to thevalues -1, 0, and 1, which is useful in programming tri-statelogic. BINARY_FLOAT and BINARY_DOUBLE Single-precision and double-precision IEEE 754-format single-precision floating-point numbers. These types areused primarily for high-speed scientific computation. Literals of thesetypes end with f (for BINARY_FLOAT) or d (for BINARY_DOUBLE). For example, 2.07f or 3.000094d. Computations involving these types producespecialvalues that you need to check for, rather than raising exceptions. To help deal with overflow, underflow, and other conditions that can occur with thesenumbers, you can use several special predefined constants: BINARY_FLOAT_NAN, BINARY_FLOAT_INFINITY, BINARY_FLOAT_MAX_NORMAL, BINARY_FLOAT_MIN_NORMAL, BINARY_FLOAT_MAX_SUBNORMAL, BINARY_FLOAT_MIN_SUBNORMAL, and corresponding names starting with BINARY_DOUBLE. The constants for NaN ("not a number") and infinity are also defined by SQL; the others are PL/SQL-only. NUMBER You use theNUMBER datatypeto storefixed-point or floating-point numbers. Its magnitude range is 1E-130 .. 10E125. If the value of an expression falls outside this range, you get a numeric overflow or underflow error. You can specify precision, which is the totalnumber of digits, and scale, which is the number of digits to the right of thedecimal point. The syntaxfollows: NUMBER[(precision,scale)] To declare fixed-point numbers, for which you must specify scale, use thefollowing form: NUMBER(precision,scale)
  • 29. Page 29 of 77 To declare floating-point numbers, for which you cannot specify precision or scale because the decimal point can "float" to any position, use the following form: NUMBER To declare integers, which have no decimal point, use this form: NUMBER(precision) -- same as NUMBER(precision,0) You cannot use constants or variables to specify precision and scale; you must use integer literals. Themaximum precision of a NUMBER value is 38 decimal digits. If you do not specify precision, it defaults to 38 or the maximum supported by your system, whichever is less. Scale, which can range from -84 to 127, determines where rounding occurs. For instance, a scale of 2 rounds to the nearest hundredth (3.456 becomes 3.46). A negative scale rounds to the left of the decimal point. For example, a scale of -3 rounds to the nearest thousand (3456 becomes 3000). A scale of 0 rounds to thenearest whole number. If you do not specify scale, it defaults to 0. NUMBER Subtypes You can use the following NUMBER subtypes for compatibility with ANSI/ISO and IBM types or when you want a more descriptivename: DEC DECIMAL DOUBLE PRECISION FLOAT INTEGER INT NUMERIC REAL SMALLINT Use thesubtypes DEC, DECIMAL, and NUMERIC to declare fixed-point numbers with a maximum precision of 38 decimal digits. Use thesubtypes DOUBLE PRECISION and FLOAT to declare floating-point numbers with a maximum precision of 126 binary digits, which is roughly equivalent to 38 decimal digits. Or, use thesubtypeREALto declare floating-point numbers with a maximum precision of 63 binary digits, which is roughly equivalent to 18 decimal digits. Use thesubtypes INTEGER, INT, and SMALLINT to declare integers with a maximum precision of 38 decimal digits. PLS_INTEGER You use thePLS_INTEGER datatypeto storesigned integers. Its magnitude range is -2**31 .. 2**31. PLS_INTEGER values require less storage than NUMBER values. Also, PLS_INTEGER operations use machine arithmetic, so they are faster than NUMBER and BINARY_INTEGER operations, which use library arithmetic. For efficiency, use PLS_INTEGER for all calculations that fall within its magnitude range. Although PLS_INTEGER and BINARY_INTEGER have the same magnitude range, they are not fully compatible. When a PLS_INTEGER calculation overflows, an exception is raised. However, when a BINARY_INTEGER calculation overflows, no exception is raised if the result is assigned to a NUMBER variable. Because of this small semantic difference, you might want to continue using BINARY_INTEGER in old applications for compatibility. In new applications, always use PLS_INTEGER for better performance. PL/SQL Character and String Types Character types let you storealphanumeric data, represent words and text, and manipulate character strings. CHAR You use theCHAR datatypeto storefixed-length character data. How thedata is represented internally depends on thedatabase character set. The CHAR datatypetakes an optionalparameter that lets you specify a maximum sizeup to 32767 bytes. You can specify thesize in terms of bytes or characters, where each character contains one or more bytes, depending on the character set encoding. The syntaxfollows: CHAR[(maximum_size [CHAR | BYTE] )] You cannot use a symbolic constant or variable to specify themaximum size; you must use an integer literal in the range 1 .. 32767. If you do not specify a maximum size, it defaults to 1. If you specify themaximum sizein bytes rather than characters, a CHAR(n) variable might be too small to hold n multibyte characters. To avoid this possibility, usethe notation CHAR(n CHAR)so that the variable can hold n characters in the database character set, even if some of thosecharacters contain multiple bytes. When you specify thelength in characters, the upper limit is still 32767 bytes. So for double-byteand multibytecharacter sets, you can only specify 1/2 or 1/3 as many characters as with a single-byte character set. Although PL/SQL character variables can be relatively long, you cannot insert CHAR values longer than 2000 bytes into a CHAR database column. You can insert any CHAR(n) value into a LONG database column because the maximum width of a LONG column is 2**31 bytes or two gigabytes. However, you cannot retrieve a value longer than 32767 bytes from a LONG column into a CHAR(n) variable. When you do not use the CHAR or BYTE qualifiers, the default is determined by the setting of the NLS_LENGTH_SEMANTICS initialization parameter. When a PL/SQL procedure is compiled, thesetting of this parameter is recorded, so that the same setting is used when the procedure is recompiled after being invalidated. CHAR Subtype The CHAR subtypeCHARACTER has thesame range of values as its base type. That is, CHARACTER is just another name for CHAR. You can use this subtypefor compatibility with ANSI/ISO and IBM types or when you want an identifier more descriptive than CHAR. LONG and LONG RAW You use theLONG datatypeto storevariable-length character strings. The LONG datatypeis like the VARCHAR2 datatype, except that the maximum size of a LONG value is 32760 bytes. You use theLONG RAW datatypeto storebinary data or bytestrings. LONG RAW data is like LONG data, except that LONG RAW data is not interpreted by PL/SQL. The maximum sizeof a LONG RAW value is 32760 bytes. You can insert any LONG value into a LONG database column because themaximum width of a LONG column is 2**31 bytes. However, you cannot retrieve a value longer than 32760 bytes from a LONG column into a LONG variable. Likewise, you can insert any LONG RAW value into a LONG RAW database column because the maximum width of a LONG RAW column is 2**31 bytes. However, you cannot retrieve a value longer than 32760 bytes froma LONG RAW column into a LONG RAW variable. LONG columns can store text, arrays of characters, or even short documents. You can reference LONG columns in UPDATE, INSERT, and (most) SELECT statements, but not in expressions, SQL function calls, or certain SQL clauses such as WHERE, GROUP BY, and CONNECT BY.
  • 30. Page 30 of 77 Note: In SQL statements, PL/SQL binds LONG values as VARCHAR2, not as LONG. However, if the length of the bound VARCHAR2 exceeds the maximum width of a VARCHAR2 column (4000 bytes), Oracle converts the bind typeto LONG automatically, then issues an error message because you cannot pass LONG values to a SQL function. RAW You use theRAW datatypeto storebinary data or bytestrings. For example, a RAW variable might storea sequence of graphics characters or a digitized picture. Raw data is like VARCHAR2 data, except that PL/SQL does not interpret raw data. Likewise, Oracle Net does no character set conversions when you transmit raw data from one systemto another. The RAW datatypetakes a required parameter that lets you specify a maximum size up to 32767 bytes. Thesyntaxfollows: RAW(maximum_size) You cannot use a symbolic constant or variable to specify themaximum size; you must use an integer literal in the range 1 .. 32767. You cannot insert RAW values longer than 2000 bytes into a RAW column. You can insert any RAW value into a LONG RAW database column because the maximum width of a LONG RAW column is 2**31 bytes. However, you cannot retrieve a value longer than 32767 bytes from a LONG RAW column into a RAW variable. ROWID and UROWID Internally, every database table has a ROWID pseudocolumn, which stores binary values called rowids. Each rowid represents the storage address of a row. A physical rowid identifies a row in an ordinary table. A logical rowid identifies a row in an index- organized table. The ROWID datatypecan store only physicalrowids. However, the UROWID (universal rowid) datatypecan store physical, logical, or foreign (non-Oracle) rowids. Suggestion: UsetheROWID datatypeonly for backward compatibility with old applications. For new applications, use theUROWID datatype. When you select or fetch a rowid into a ROWID variable, you can use the built-in function ROWIDTOCHAR, which converts the binary value into an 18-bytecharacter string. Conversely, thefunction CHARTOROWID converts a ROWID character string into a rowid. If the conversion fails because the character string does not represent a valid rowid, PL/SQL raises the predefined exception SYS_INVALID_ROWID. This also applies to implicit conversions. To convert between UROWID variables and character strings, use regular assignment statements without any function call. The values are implicitly converted between UROWID and character types. Physical Rowids Physical rowids provide fast access to particular rows. As long as the row exists, its physicalrowid does not change. Efficient and stable, physicalrowids are useful for selecting a set of rows, operating on the whole set, and then updating a subset. For example, you can compare a UROWID variable with the ROWID pseudocolumn in the WHERE clause of an UPDATE or DELETE statement to identify the latest row fetched from a cursor. A physicalrowid can have either of two formats. The 10-byteextended rowid format supports tablespace-relativeblock addresses and can identify rows in partitioned and non- partitioned tables. The 6-byterestricted rowid format is provided for backward compatibility. Extended rowids use a base-64 encoding of thephysicaladdress for each row selected. For example, in SQL*Plus (which implicitly converts rowids into character strings), the query SQL> SELECT rowid, ename FROM emp WHERE empno = 7788; might return thefollowing row: ROWID ENAME ------------------ ---------- AAAAqcAABAAADFNAAH SCOTT The format, OOOOOOFFFBBBBBBRRR, has four parts:  OOOOOO:Thedata object number (AAAAqc in theexample above) identifies the database segment. Schema objects in thesame segment, such as a cluster of tables, have thesame data object number.  FFF: The file number (AAB in the example) identifies the data file that contains the row. File numbers are unique within a database.  BBBBBB: Theblock number (AAADFN in the example) identifies the data block that contains the row. Because block numbers are relative to their data file, not their tablespace, two rows in thesame tablespace but in different data files can have thesame block number.  RRR: The row number (AAH in theexample) identifies therow in the block. Logical Rowids Logical rowids provide the fastest access to particular rows. Oracle uses them to construct secondary indexes on index-organized tables. Having no permanent physicaladdress, a logical rowid can move across data blocks when new rows are inserted. However, if the physicallocation of a row changes, its logical rowid remains valid. A logical rowid can include a guess, which identifies the block location of a row at the time the guess is made. Instead of doing a full key search, Oracle uses the guess to search the block directly. However, as new rows are inserted, guesses can become stale and slow down access to rows. To obtain fresh guesses, you can rebuild thesecondary index. You can use the ROWID pseudocolumn to select logical rowids (which are opaquevalues) from an index-organized table. Also, you can insert logical rowids into a column of type UROWID, which has a maximum sizeof 4000 bytes. The ANALYZE statement helps you track the staleness of guesses. This is useful for applications that storerowids with guesses in a UROWID column, then use therowids to fetch rows. Note: To manipulate rowids, you can use the supplied package DBMS_ROWID. VARCHAR2 You use theVARCHAR2 datatypeto storevariable-length character data. How the data is represented internally depends on thedatabase character set. The VARCHAR2 datatypetakes a required parameter that specifies a maximum size up to 32767 bytes. Thesyntax follows: VARCHAR2(maximum_size [CHAR | BYTE]) You cannot use a symbolic constant or variable to specify themaximum size; you must use an integer literal in the range 1 .. 32767. Small VARCHAR2 variables are optimized for performance, and larger ones are optimized for efficient memory use. The cutoff point is 2000 bytes. For a VARCHAR2 that is 2000 bytes or longer, PL/SQL dynamically allocates only enough memory to hold the actual value. For a VARCHAR2 variable that is shorter than 2000 bytes, PL/SQL preallocates thefull declared length of thevariable. For example, if you assign thesame 500-bytevalue to a VARCHAR2(2000 BYTE) variable and to a VARCHAR2(1999 BYTE) variable, the former takes up 500 bytes and thelatter takes up 1999 bytes.
  • 31. Page 31 of 77 If you specify themaximum size in bytes rather than characters, a VARCHAR2(n) variable might be too small to hold n multibytecharacters. To avoid this possibility, usethe notation VARCHAR2(n CHAR)so that thevariable can hold n characters in thedatabase character set, even if some of thosecharacters contain multiple bytes. When you specify the length in characters, theupper limit is still 32767 bytes. So for double-byte and multibytecharacter sets, you can only specify 1/2 or 1/3 as many characters as with a single-byte character set. Although PL/SQL character variables can be relatively long, you cannot insert VARCHAR2 values longer than 4000 bytes into a VARCHAR2 database column. You can insert any VARCHAR2(n) value into a LONG database column because the maximum width of a LONG column is 2**31 bytes. However, you cannot retrieve a value longer than 32767 bytes from a LONG column into a VARCHAR2(n) variable. When you do not use the CHAR or BYTE qualifiers, the default is determined by the setting of the NLS_LENGTH_SEMANTICS initialization parameter. When a PL/SQL procedure is compiled, thesetting of this parameter is recorded, so that the same setting is used when the procedure is recompiled after being invalidated. VARCHAR2 Subtypes The VARCHAR2 subtypes below have the same range of values as their base type. For example, VARCHAR is just another name for VARCHAR2. STRING VARCHAR You can use these subtypes for compatibility with ANSI/ISO and IBM types. Note: Currently, VARCHAR is synonymous with VARCHAR2. However, in future releases of PL/SQL, to accommodate emerging SQL standards, VARCHAR might become a separate datatypewith different comparison semantics. It is a good idea to use VARCHAR2 rather than VARCHAR. PL/SQL LOB Types The LOB (large object) datatypes BFILE, BLOB, CLOB, and NCLOB let you storeblocks of unstructured data (such as text, graphic images, video clips, and sound waveforms) up to four gigabytes in size. And, they allow efficient, random, piece-wise access to the data. The LOB types differ from the LONG and LONG RAW types in several ways. For example, LOBs (except NCLOB) can be attributes of an object type, but LONGs cannot. Themaximum size of a LOB is four gigabytes, but the maximum size of a LONG is two gigabytes. Also, LOBs support randomaccess to data, but LONGs support only sequentialaccess. LOB types store lob locators, which point to large objects stored in an external file, in-line (inside therow) or out-of-line (outsidethe row). Database columns of type BLOB, CLOB, NCLOB, or BFILE storethe locators. BLOB, CLOB, and NCLOB data is stored in the database, in or outside therow. BFILE data is stored in operating systemfiles outside thedatabase. PL/SQL operates on LOBs through thelocators. For example, when you select a BLOB column value, only a locator is returned. If you got it during a transaction, the LOB locator includes a transaction ID, so you cannot use it to updatethat LOB in another transaction. Likewise, you cannot save a LOB locator during one session, then use it in another session. BFILE You use theBFILE datatypeto storelarge binary objects in operating systemfiles outside the database. Every BFILE variable stores a file locator, which points to a large binary file on the server. The locator includes a directory alias, which specifies a full path name (logical path names are not supported). BFILEs are read-only, so you cannot modify them. Thesize of a BFILE is systemdependent but cannot exceed four gigabytes (2**32 - 1 bytes). Your DBA makes sure that a given BFILE exists and that Oracle has read permissions on it. The underlying operating system maintains file integrity. BFILEs do not participatein transactions, are not recoverable, and cannot be replicated. The maximum number of open BFILEs is set by theOracle initialization parameter SESSION_MAX_OPEN_FILES, which is systemdependent. BLOB You use theBLOB datatypeto storelarge binary objects in thedatabase, in-line or out-of- line. Every BLOB variable stores a locator, which points to a large binary object. The size of a BLOB cannot exceed four gigabytes. BLOBs participatefully in transactions, are recoverable, and can be replicated. Changes made by package DBMS_LOB can be committed or rolled back. BLOB locators can span transactions (for reads only), but they cannot span sessions. CLOB You use theCLOB datatypeto storelarge blocks of character data in the database, in-line or out-of-line. Both fixed-width and variable-width character sets are supported. Every CLOB variable stores a locator, which points to a large block of character data. The size of a CLOB cannot exceed four gigabytes. CLOBs participatefully in transactions, are recoverable, and can be replicated. Changes made by package DBMS_LOB can be committed or rolled back. CLOB locators can span transactions (for reads only), but they cannot span sessions. NCLOB You use theNCLOB datatypeto storelarge blocks of NCHAR data in thedatabase, in-line or out-of-line. Both fixed-width and variable-width character sets are supported. Every NCLOB variable stores a locator, which points to a large block of NCHAR data. Thesize of an NCLOB cannot exceed four gigabytes. NCLOBs participatefully in transactions, are recoverable, and can be replicated. Changes made by package DBMS_LOB can be committed or rolled back. NCLOB locators can span transactions (for reads only), but they cannot span sessions. PL/SQL Boolean Types PL/SQL has a typefor representing Boolean values (true and false). Because SQL does not have an equivalent type, you can use BOOLEAN variables and parameters in PL/SQL contexts but not inside SQL statements or queries. BOOLEAN You use theBOOLEAN datatypeto storethelogical values TRUE, FALSE, and NULL (which stands for a missing, unknown, or inapplicable value). Only logic operations are allowed on BOOLEAN variables. The BOOLEAN datatypetakes no parameters. Only the values TRUE, FALSE, and NULL can be assigned to a BOOLEAN variable. You cannot insert the values TRUE and FALSE into a database column. You cannot select or fetch column values into a BOOLEAN variable. Functions called from a SQL query cannot take any BOOLEAN parameters. Neither can built-in SQL functions such as TO_CHAR; to represent BOOLEAN values in output, you must use IF-THEN or CASE constructs to translate BOOLEAN values into some other type, such as 0 or 1, 'Y' or 'N', 'true' or 'false', and so on. PL/SQL Date, Time, and Interval Types
  • 32. Page 32 of 77 The datatypes in this section let you storeand manipulate dates, times, and intervals (periods of time). A variable that has a date/time datatypeholds values called datetimes; a variable that has an interval datatypeholds values called intervals. A datetime or interval consists of fields, which determine its value. The following list shows the valid values for each field: FieldName Valid Datetime Values Valid Interval Values YEAR -4712 to 9999 (excluding year 0) Any nonzero integer MONTH 01 to 12 0 to 11 DAY 01 to 31 (limited by the values of MONTH and YEAR, according to therules of the calendar for the locale) Any nonzero integer HOUR 00 to 23 0 to 23 MINUTE 00 to 59 0 to 59 SECOND 00 to 59.9(n), where 9(n) is the precision of time fractional seconds 0 to 59.9(n), where 9(n) is the precision of interval fractional seconds TIMEZONE_HOUR -12 to 14 (range accommodates daylight savings time changes) Not applicable TIMEZONE_MINUTE 00 to 59 Not applicable TIMEZONE_REGION Found in the view V$TIMEZONE_NAMES Not applicable TIMEZONE_ABBR Found in the view V$TIMEZONE_NAMES Not applicable Except for TIMESTAMP WITH LOCAL TIMEZONE, thesetypes areall part of the SQL92 standard. DATE You use theDATE datatypeto storefixed-length datetimes, which include the time of day in seconds since midnight. The date portion defaults to the first day of the current month; the time portion defaults to midnight. The date function SYSDATE returns the current date and time. Tips:  To compare dates for equality, regardless of thetime portion of each date, use the function result TRUNC(date_variable) in comparisons, GROUP BY operations, and so on.  To find just the time portion of a DATEvariable, subtract thedate portion: date_variable - TRUNC(date_variable). Valid dates range from January 1, 4712 BC to December 31, 9999 AD. A Julian date is the number of days since January 1, 4712 BC. Julian dates allow continuous dating from a common reference. You can use the date format model 'J' with the date functions TO_DATE and TO_CHAR to convert between DATE values and their Julian equivalents. In date expressions, PL/SQL automatically converts character values in the default date format to DATE values. The default date format is set by the Oracle initialization parameter NLS_DATE_FORMAT. For example, the default might be 'DD-MON-YY', which includes a two- digit number for theday of themonth, an abbreviation of the month name, and the last two digits of theyear. You can add and subtract dates. In arithmetic expressions, PL/SQL interprets integer literals as days. For instance, SYSDATE + 1 signifies the same time tomorrow. TIMESTAMP The datatypeTIMESTAMP, which extends the datatype DATE, stores theyear, month, day, hour, minute, and second. The syntaxis: TIMESTAMP[(precision)] where the optionalparameter precision specifies thenumber of digits in the fractional part of the seconds field. You cannot use a symbolic constant or variable to specify theprecision; you must use an integer literal in the range 0 .. 9. Thedefault is 6. The default timestamp format is set by the Oracle initialization parameter NLS_TIMESTAMP_FORMAT. In the following example, you declare a variable of type TIMESTAMP, then assign a literal value to it: DECLARE checkout TIMESTAMP(3); BEGIN checkout := '1999-06-22 07:48:53.275'; ... END; In this example, the fractional part of the seconds field is 0.275. Datetime and Interval Arithmetic PL/SQL lets you construct datetime and interval expressions. The following list shows the operators that you can use in such expressions: Operand 1 Operator Operand 2 Result Type datetime + interval datetime datetime - interval datetime interval + datetime datetime datetime - datetime interval interval + interval interval interval - interval interval interval * numeric interval numeric * interval interval interval / numeric interval Overview of PL/SQL Subtypes Each PL/SQL base typespecifies a set of values and a set of operations applicable to items of that type. Subtypes specify thesame set of operations as their base type, but only a subset of its values. A subtypedoes notintroduce a new type;rather, it places an optional constraint on its base type. Subtypes can increase reliability, providecompatibility with ANSI/ISO types, and improve readability by indicating the intended use of constants and variables. PL/SQL predefines several subtypes in package STANDARD. For example, PL/SQL predefines the subtypes CHARACTER and INTEGER as follows: SUBTYPE CHARACTER IS CHAR; SUBTYPE INTEGER IS NUMBER(38,0); -- allows only whole numbers The subtypeCHARACTER specifies thesame set of values as its base type CHAR, so CHARACTER is an unconstrained subtype. But, thesubtypeINTEGER specifies only a subset of the values of its base type NUMBER, so INTEGER is a constrained subtype.
  • 33. Page 33 of 77 Defining Subtypes You can define your own subtypes in thedeclarative part of any PL/SQL block, subprogram, or package using thesyntax SUBTYPE subtype_name IS base_type[(constraint)] [NOT NULL]; where subtype_name is a typespecifier used in subsequent declarations, base_type is any scalar or user-defined PL/SQL datatype, and constraint applies only to base types that can specify precision and scale or a maximum size. Some examples follow: DECLARE SUBTYPE BirthDate IS DATE NOT NULL; -- based on DATE type SUBTYPE Counter IS NATURAL; -- based on NATURAL subtype TYPE NameList IS TABLE OF VARCHAR2(10); SUBTYPE DutyRoster IS NameList; -- based on TABLE type TYPE TimeRec IS RECORD (minutes INTEGER, hours INTEGER); SUBTYPE FinishTime IS TimeRec; -- based on RECORD type SUBTYPE ID_NumIS emp.empno%TYPE; -- based on column type You can use %TYPE or %ROWTYPE to specify thebase type. When %TYPE provides the datatypeof a database column, the subtypeinherits thesize constraint (if any) of the column. The subtypedoes notinherit other kinds of constraints such as NOT NULL. Using Subtypes Once you define a subtype, you can declare items of that type. In the example below, you declare a variable of type Counter. Noticehow the subtypenameindicates theintended use of the variable. DECLARE SUBTYPE Counter IS NATURAL; rows Counter; You can constrain a user-defined subtypewhen declaring variables of that type: DECLARE SUBTYPE Accumulator IS NUMBER; total Accumulator(7,2); Subtypes can increase reliability by detecting out-of-range values. In the example below, you restrict the subtype Numeral to storing integers in the range -9 .. 9. If your program tries to store a number outsidethat range in a Numeral variable, PL/SQL raises an exception. DECLARE SUBTYPE Numeral IS NUMBER(1,0); x_axis Numeral; -- magnitude range is -9 .. 9 y_axis Numeral; BEGIN x_axis := 10; -- raises VALUE_ERROR ... END; Type Compatibility An unconstrained subtypeis interchangeable with its base type. For example, given the following declarations, thevalue of amount can be assigned to total without conversion: DECLARE SUBTYPE Accumulator IS NUMBER; amount NUMBER(7,2); total Accumulator; BEGIN ... total := amount; ... END; Different subtypes areinterchangeable if they have the same base type: DECLARE SUBTYPE b1 IS BOOLEAN; SUBTYPE b2 IS BOOLEAN; finished b1; -- Different subtypes, debugging b2; -- both based on BOOLEAN. BEGIN debugging := finished; -- They can be assigned to each other. END; Different subtypes arealso interchangeable if their base types arein the same datatype family. For example, given the following declarations, thevalue of verb can be assigned to sentence: DECLARE SUBTYPE Word IS CHAR(15); SUBTYPE Text IS VARCHAR2(1500); verb Word; -- Different subtypes sentence Text(150); -- of types from the same family BEGIN sentence := verb; -- can be assigned, ifnot too long. END; Converting PL/SQL Datatypes Sometimes it is necessary to convert a value from one datatypeto another. For example, to use a DATE value in a report, you must convert it to a character string. PL/SQL supports both explicit and implicit (automatic) datatypeconversion. To ensure your program does exactly what you expect, use explicit conversions wherever possible. Explicit Conversion To convert values from one datatypeto another, you use built-in functions. For example, to convert a CHAR value to a DATE or NUMBER value, you use the function TO_DATE or TO_NUMBER, respectively. Conversely, to convert a DATE or NUMBER value to a CHAR value, you use the function TO_CHAR. Using explicit conversions, particularly when passing parameters to subprograms, can avoid unexpected errors or wrong results. For example, the TO_CHAR function lets you specify theformat for a DATE value, rather than relying on language settings in the database. Including an arithmetic expression among strings being concatenated with the || operator can producean error unless you put parentheses or a call to TO_CHAR around the entire arithmetic expression. Implicit Conversion When it makes sense, PL/SQL can convert thedatatypeof a value implicitly. This lets you use literals, variables, and parameters of one typewhereanother typeis expected. For example, you can pass a numeric literal to a subprogram that expects a string value, and the subprogram receives the string representation of the number. In the following example, the CHAR variables start_time and finish_time hold string values representing the number of seconds past midnight. The difference between thosevalues must be assigned to the NUMBER variable elapsed_time. PL/SQL converts the CHAR values to NUMBER values automatically. DECLARE start_time CHAR(5); finish_time CHAR(5); elapsed_time NUMBER(5); BEGIN
  • 34. Page 34 of 77 /* Get systemtime as seconds past midnight. */ SELECT TO_CHAR(SYSDATE,'SSSSS') INTO start_time FROM sys.dual; -- do something /* Get systemtime again. */ SELECT TO_CHAR(SYSDATE,'SSSSS') INTO finish_time FROM sys.dual; /* Compute elapsed time in seconds. */ elapsed_time := finish_time - start_time; INSERT INTO results VALUES (elapsed_time, ...); END; Before assigning a selected column value to a variable, PL/SQL will, if necessary, convert the value from the datatypeof thesource column to thedatatypeof the variable. This happens, for example, when you select a DATE column value into a VARCHAR2 variable. Likewise, before assigning the value of a variable to a database column, PL/SQL will, if necessary, convert thevalue from thedatatypeof the variable to thedatatypeof the target column. If PL/SQL cannot determine which implicit conversion is needed, you get a compilation error. In such cases, you must use a datatypeconversion function. Table 3-1 shows which implicit conversions PL/SQL can do. Notes:  The labels PLS_INT and BIN_INT represent thetypes PLS_INTEGER and BINARY_INTEGER in thetable. You cannot use them as abbreviations in code.  The table lists only types that havedifferent representations. Types that havethe same representation, such as CLOB and NCLOB, CHAR and NCHAR, and VARCHAR and NVARCHAR2, can be substituted for each other.  You can implicitly convert between CLOB and NCLOB, but be careful because this can be an expensive operation. To make clear that this conversion is intended, you can use theconversion functions TO_CLOB and TO_NCLOB.  TIMESTAMP, TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE, INTERVAL DAY TO SECOND, and INTERVAL YEAR TO MONTH can all be converted using the same rules as the DATE type. However, because of their different internal representations, thesetypes cannot always be converted to each other. Table 3-1 ImplicitConversions BIN_ INT BL OB CH AR CL OB DA TE LO NG NUM BER PLS_ INT R A W URO WID VARC HAR2 BIN_I NT X X X X X BLOB X CHAR X X X X X X X X X CLOB X X DATE X X X LONG X X X NUMB ER X X X X X PLS_I NT X X X X X RAW X X X X UROW ID X X VARC HAR2 X X X X X X X X X It is your responsibility to ensure that values are convertible. For instance, PL/SQL can convert the CHAR value '02-JUN-92' to a DATE value but cannot convert the CHAR value 'YESTERDAY' to a DATE value. Similarly, PL/SQL cannot convert a VARCHAR2 value containing alphabetic characters to a NUMBER value. Choosing Between Implicit and Explicit Conversion Relying on implicit datatypeconversions is a poor programming practice because they can be slower and theconversion rules might change in later softwarereleases. Implicit conversions are context-sensitive and not always predictable. For best reliability and maintainability, use datatypeconversion functions. DATE Values When you select a DATE column value into a CHAR or VARCHAR2 variable, PL/SQL must convert the internal binary value to a character value. PL/SQL calls thefunction TO_CHAR, which returns a character string in thedefault date format. To get other information, such as the time or Julian date, call TO_CHAR with a format mask. A conversion is also necessary when you insert a CHAR or VARCHAR2 value into a DATE column. PL/SQL calls the function TO_DATE, which expects the default date format. To insert dates in other formats, call TO_DATE with a format mask. RAW and LONG RAW Values When you select a RAW or LONG RAW column value into a CHAR or VARCHAR2 variable, PL/SQL must convert the internal binary value to a character value. In this case, PL/SQL returns each binary byteof RAW or LONG RAW data as a pair of characters. Each character represents thehexadecimal equivalent of a nibble (half a byte). For example, PL/SQL returns the binary byte11111111 as the pair of characters 'FF'. The function RAWTOHEX does the same conversion. A conversion is also necessary when you insert a CHAR or VARCHAR2 value into a RAW or LONG RAW column. Each pair of characters in the variable must represent the hexadecimal equivalent of a binary byte. Otherwise, PL/SQL raises an exception. 4 - Using PL/SQL Control Structures This chapter shows you how to structuretheflow of control through a PL/SQL program. PL/SQL provides conditional tests, loops, and branches that let you producewell- structured programs. Overview of PL/SQL Control Structures Procedural computer programs use thebasic control structures shown in Figure 4-1. Figure 4-1 Control Structures
  • 35. Page 35 of 77 Description of the illustration lnpls008.gif The selection structuretests a condition, then executes one sequence of statements instead of another, depending on whether thecondition is trueor false. A condition is any variable or expression that returns a Boolean value (TRUE or FALSE). The iteration structure executes a sequence of statements repeatedly as long as a condition holds true. The sequence structuresimply executes a sequence of statements in the order in which they occur. Testing Conditions: IF and CASE Statements The IF statement executes a sequence of statements depending on the value of a condition. There are three forms of IF statements:IF-THEN, IF-THEN-ELSE, and IF-THEN-ELSIF. The CASE statement is a compact way to evaluate a single condition and choose between many alternative actions. It makes sense to use CASE when there are three or more alternatives to choose from. Using the IF-THEN Statement The simplest form of IF statement associates a condition with a sequence of statements enclosed by the keywords THEN and END IF (not ENDIF): IF condition THEN sequence_of_statements END IF; The sequence of statements is executed only if thecondition is true. If the condition is false or null, the IF statement does nothing. In either case, control passes to the next statement. IF sales > quota THEN compute_bonus(empid); UPDATE payroll SET pay = pay + bonus WHERE empno = emp_id; END IF; You can place brief IF statements on a single line: IF x > y THEN high := x; END IF; Using the IF-THEN-ELSE Statement The second form of IF statement adds thekeyword ELSE followed by an alternative sequence of statements: IF condition THEN sequence_of_statements1 ELSE sequence_of_statements2 END IF; The statements in the ELSE clause are executed only if thecondition is false or null. The IF- THEN-ELSE statement ensures that one or theother sequence of statements is executed. In the following example, the first UPDATE statement is executed when thecondition is true, and the second UPDATE statement is executed when the condition is false or null: IF trans_type = 'CR' THEN UPDATE accounts SET balance = balance + credit WHERE ... ELSE UPDATE accounts SET balance = balance - debit WHERE ... END IF; IF statements can be nested: IF trans_type = 'CR' THEN UPDATE accounts SET balance = balance + credit WHERE ... ELSE IF new_balance >= minimum_balance THEN UPDATE accounts SET balance = balance - debit WHERE ... ELSE RAISE insufficient_funds; END IF; END IF; Using the IF-THEN-ELSIF Statement Sometimes you want to choose between several alternatives. You can use the keyword ELSIF (not ELSEIF or ELSE IF) to introduce additional conditions: IF condition1 THEN sequence_of_statements1 ELSIF condition2 THEN sequence_of_statements2 ELSE sequence_of_statements3 END IF; If the first condition is false or null, the ELSIF clause tests another condition. An IF statement can have any number of ELSIF clauses; the final ELSE clause is optional. Conditions are evaluated one by one from top to bottom. If any condition is true, its associated sequence of statements is executed and control passes to the next statement. If all conditions are false or null, the sequence in the ELSE clause is executed. Consider the following example: BEGIN IF sales > 50000 THEN bonus := 1500; ELSIF sales > 35000 THEN bonus := 500; ELSE bonus := 100; END IF; INSERT INTO payroll VALUES (emp_id, bonus, ...); END; If the value of sales is larger than 50000, thefirst and second conditions are true. Nevertheless, bonus is assigned theproper value of 1500 because thesecond condition is never tested. When the first condition is true, its associated statement is executed and control passes to the INSERT statement. Using the CASE Statement Like the IF statement, the CASE statement selects one sequence of statements to execute. However, to select thesequence, the CASE statement uses a selector rather than multiple Boolean expressions. (Recall from Chapter 2 that a selector is an expression whose value is used to select one of several alternatives.) To compare the IF and CASE statements, consider thefollowing code that outputs descriptions of schoolgrades: IF grade = 'A' THEN
  • 36. Page 36 of 77 dbms_output.put_line('Excellent'); ELSIF grade = 'B' THEN dbms_output.put_line('Very Good'); ELSIF grade = 'C' THEN dbms_output.put_line('Good'); ELSIF grade = 'D' THEN dbms_output. put_line('Fair'); ELSIF grade = 'F' THEN dbms_output.put_line('Poor'); ELSE dbms_output.put_line('No such grade'); END IF; Notice the five Boolean expressions. In each instance, we test whether the same variable, grade, is equal to one of five values: 'A', 'B', 'C', 'D', or 'F'. Let us rewrite thepreceding code using the CASE statement, as follows: CASE grade WHEN 'A' THEN dbms_output.put_line('Excellent'); WHEN 'B' THEN dbms_output.put_line('Very Good'); WHEN 'C' THEN dbms_output.put_line('Good'); WHEN 'D' THEN dbms_output.put_line('Fair'); WHEN 'F' THEN dbms_output.put_line('Poor'); ELSE dbms_output.put_line('No such grade'); END CASE; The CASE statement is more readable and more efficient. When possible, rewrite lengthy IF-THEN-ELSIF statements as CASE statements. The CASE statement begins with the keyword CASE. Thekeyword is followed by a selector, which is the variable grade in thelast example. Theselector expression can be arbitrarily complex. For example, it can contain function calls. Usually, however, it consists of a single variable. The selector expression is evaluated only once. Thevalue it yields can have any PL/SQL datatypeother than BLOB, BFILE, an object type, aPL/SQL record, an index-by-table, a varray, or a nested table. The selector is followed by one or more WHEN clauses, which are checked sequentially. The value of theselector determines which clause is executed. If the value of the selector equals thevalue of a WHEN-clause expression, that WHEN clause is executed. For instance, in the last example, if grade equals 'C', theprogram outputs 'Good'. Execution never falls through; if any WHEN clause is executed, control passes to the next statement. The ELSE clause works similarly to the ELSE clause in an IF statement. In the last example, if the grade is not one of the choices covered by a WHEN clause, the ELSE clause is selected, and the phrase'No such grade' is output. TheELSE clause is optional. However, if you omit the ELSE clause, PL/SQL adds the following implicit ELSE clause: ELSE RAISE CASE_NOT_FOUND; There is always a default action, even when you omit the ELSE clause. If the CASE statement does not match any of the WHEN clauses and you omit the ELSE clause, PL/SQL raises the predefined exception CASE_NOT_FOUND. The keywords END CASE terminate theCASE statement. These two keywords must be separated by a space. The CASE statement has the following form: [<<label_name>>] CASE selector WHEN expression1 THEN sequence_of_statements1; WHEN expression2 THEN sequence_of_statements2; ... WHEN expressionN THEN sequence_of_statementsN; [ELSE sequence_of_statementsN+1;] END CASE [label_name]; Like PL/SQL blocks, CASE statements can be labeled. Thelabel, an undeclared identifier enclosed by double angle brackets, must appear at thebeginning of the CASE statement. Optionally, thelabel name can also appear at the end of the CASE statement. Exceptions raised during the execution of a CASE statement are handled in the usual way. That is, normal execution stops and control transfers to the exception-handling part of your PL/SQL block or subprogram. An alternative to the CASEstatement is the CASE expression, where each WHEN clause is an expression. SearchedCASEStatement PL/SQL also provides a searched CASE statement, which has theform: [<<label_name>>] CASE WHEN search_condition1 THEN sequence_of_statements1; WHEN search_condition2 THEN sequence_of_statements2; ... WHEN search_conditionN THEN sequence_of_statementsN; [ELSE sequence_of_statementsN+1;] END CASE [label_name]; The searched CASE statement has no selector. Also, its WHEN clauses contain search conditions that yield a Boolean value, not expressions that can yield a value of any type. An example follows: CASE WHEN grade = 'A' THEN dbms_output.put_line('Excellent'); WHEN grade = 'B' THEN dbms_output.put_line('Very Good'); WHEN grade = 'C' THEN dbms_output.put_line('Good'); WHEN grade = 'D' THEN dbms_output.put_line('Fair'); WHEN grade = 'F' THEN dbms_output.put_line('Poor'); ELSE dbms_output.put_line('No such grade'); END CASE; The search conditions are evaluated sequentially. The Boolean value of each search condition determines which WHEN clause is executed. If a search condition yields TRUE, its WHEN clause is executed. If any WHEN clause is executed, control passes to thenext statement, so subsequent search conditions are not evaluated. If none of the search conditions yields TRUE, theELSE clause is executed. The ELSE clause is optional. However, if you omit the ELSE clause, PL/SQL adds thefollowing implicit ELSE clause: ELSE RAISE CASE_NOT_FOUND; Exceptions raised during the execution of a searched CASE statement are handled in the usual way. That is, normal execution stops and control transfers to theexception-handling part of your PL/SQL block or subprogram. Controlling Loop Iterations: LOOP and EXIT Statements LOOP statements execute a sequence of statements multiple times. There are three forms of LOOP statements:LOOP, WHILE-LOOP, and FOR-LOOP. Using the LOOP Statement The simplest form of LOOP statement is the basic loop, which encloses a sequence of statements between thekeywords LOOP and END LOOP, as follows: LOOP sequence_of_statements END LOOP;
  • 37. Page 37 of 77 With each iteration of the loop, the sequence of statements is executed, then control resumes at the top of the loop. You use an EXIT statement to stop loopingand prevent an infinite loop. You can place one or more EXIT statements anywhereinside a loop, but not outside a loop. There are two forms of EXIT statements:EXIT and EXIT-WHEN. Using the EXIT Statement The EXIT statement forces a loop to complete unconditionally. When an EXIT statement is encountered, theloop completes immediately and control passes to thenext statement: LOOP IF credit_rating < 3 THEN EXIT; -- exit loop immediately END IF; END LOOP; -- control resumes here Remember, theEXIT statement must be placed inside a loop. To complete a PL/SQL block before its normal end is reached, you can use the RETURN statement. Using the EXIT-WHEN Statement The EXIT-WHEN statement lets a loop complete conditionally. When the EXIT statement is encountered, thecondition in the WHEN clause is evaluated. If thecondition is true, the loop completes and control passes to the next statement after theloop: LOOP FETCH c1 INTO ... EXIT WHEN c1%NOTFOUND; -- exit loop ifcondition is true ... END LOOP; CLOSE c1; Until thecondition is true, the loop cannot complete. A statement inside theloop must change the value of thecondition. In theprevious example, if the FETCH statement returns a row, the condition is false. When the FETCH statement fails to return a row, the condition is true, theloop completes, and control passes to the CLOSE statement. The EXIT-WHEN statement replaces a simple IF statement. For example, compare the following statements: IF count > 100 THEN | EXIT WHEN count > 100; EXIT; | END IF; | These statements are logically equivalent, but the EXIT-WHEN statement is easier to read and understand. Labeling a PL/SQL Loop Like PL/SQL blocks, loops can be labeled. The label, an undeclared identifier enclosed by double angle brackets, must appear at thebeginning of the LOOP statement, as follows: <<label_name>> LOOP sequence_of_statements END LOOP; Optionally, thelabel name can also appear at the end of the LOOP statement, as the following example shows: <<my_loop>> LOOP ... END LOOP my_loop; When you nest labeled loops, use ending label names to improve readability. With either form of EXIT statement, you can complete not only the current loop, but any enclosing loop. Simply label the enclosing loop that you want to complete. Then, use the label in an EXIT statement, as follows: <<outer>> LOOP ... LOOP ... EXIT outer WHEN ... -- exit both loops END LOOP; ... END LOOP outer; Every enclosing loop up to and including the labeled loop is exited. Using the WHILE-LOOP Statement The WHILE-LOOP statement executes thestatements in the loop body as long as a condition is true: WHILE condition LOOP sequence_of_statements END LOOP; Before each iteration of theloop, thecondition is evaluated. If it is true, the sequence of statements is executed, then control resumes at the top of the loop. If it is false or null, the loop is skipped and control passes to the next statement: WHILE total <= 25000 LOOP SELECT sal INTO salary FROM emp WHERE ... total := total + salary; END LOOP; The number of iterations depends on the condition and is unknown until the loop completes. Thecondition is tested at the top of the loop, so the sequence might execute zero times. In thelast example, if the initial value of total is larger than 25000, the condition is false and the loop is skipped. Some languages have a LOOP UNTIL or REPEAT UNTIL structure, which tests thecondition at the bottomof theloop instead of at the top, so that the sequence of statements is executed at least once. The equivalent in PL/SQL would be: LOOP sequence_of_statements EXIT WHEN boolean_expression; END LOOP; To ensure that a WHILE loop executes at least once, use an initialized Boolean variable in the condition, as follows: done := FALSE; WHILE NOT done LOOP sequence_of_statements done := boolean_expression; END LOOP;
  • 38. Page 38 of 77 A statement inside the loop must assign a new value to the Boolean variable to avoid an infinite loop. Using the FOR-LOOP Statement Simple FOR loops iterate over a specified range of integers. The number of iterations is known before the loop is entered. A double dot (..) serves as the range operator: FOR counter IN [REVERSE] lower_bound..higher_bound LOOP sequence_of_statements END LOOP; The range is evaluated when the FOR loop is first entered and is never re-evaluated. As the next example shows, the sequence of statements is executed once for each integer in the range. After each iteration, the loop counter is incremented. FOR i IN 1..3 LOOP -- assign the values 1,2,3 to i sequence_of_statements -- executes three times END LOOP; If the lower bound equals thehigher bound, the loop body is executed once: FOR i IN 3..3 LOOP -- assign the value 3 to i sequence_of_statements -- executes one time END LOOP; By default, iteration proceeds upward from the lower bound to thehigher bound. If you use the keyword REVERSE, iteration proceeds downward from the higher bound to the lower bound. After each iteration, theloop counter is decremented. You still writethe range bounds in ascending (not descending) order. FOR i IN REVERSE 1..3 LOOP -- assign the values 3,2,1 to i sequence_of_statements -- executes three times END LOOP; Inside a FOR loop, the counter can be read but cannot be changed: FOR ctr IN 1..10 LOOP IF NOT finished THEN INSERT INTO ... VALUES (ctr, ...); -- OK factor := ctr * 2; -- OK ELSE ctr := 10; -- not allowed END IF; END LOOP; Tip: A useful variation of the FOR loop uses a SQL query instead of a range of integers. This technique lets you run a query and process all therows of theresult set with straightforward syntax How PL/SQL Loops Iterate The bounds of a loop range can be literals, variables, or expressions but must evaluate to numbers. Otherwise, PL/SQL raises the predefined exception VALUE_ERROR. The lower bound need not be 1, but theloop counter increment or decrement must be 1. j IN -5..5 k IN REVERSE first..last step IN 0..TRUNC(high/low) * 2 Internally, PL/SQL assigns the values of thebounds to temporary PLS_INTEGER variables, and, if necessary, rounds the values to thenearest integer. Themagnitude range of a PLS_INTEGER is -2**31 .. 2**31. If a bound evaluates to a number outside that range, you get a numeric overflow error when PL/SQL attempts theassignment. Some languages provide a STEP clause, which lets you specify a different increment (5 instead of 1 for example). PL/SQL has no such structure, but you can easily build one. Inside the FOR loop, simply multiply each reference to the loop counter by the new increment. In the following example, you assign today's date to elements 5, 10, and 15 of an index-by table: DECLARE TYPE DateList IS TABLE OF DATE INDEX BY BINARY_INTEGER; dates DateList; k CONSTANT INTEGER := 5; -- set new increment BEGIN FOR j IN 1..3 LOOP dates(j*k) := SYSDATE; -- multiply loop counter by increment END LOOP; ... END; DynamicRanges for Loop Bounds PL/SQL lets you specify theloop range at run time by using variables for bounds: SELECT COUNT(empno) INTO emp_count FROM emp; FOR i IN 1..emp_count LOOP ... END LOOP; If the lower bound of a loop range evaluates to a larger integer than theupper bound, the loop body is not executed and control passes to thenext statement: -- limit becomes 1 FOR i IN 2..limit LOOP sequence_of_statements -- executes zero times END LOOP; -- control passes here Scope of the Loop CounterVariable The loop counter is defined only within the loop. You cannot reference that variable name outside theloop. After the loop exits, theloop counter is undefined: FOR ctr IN 1..10 LOOP ... END LOOP; sum := ctr - 1; -- not allowed You do not need to declare theloop counter because it is implicitly declared as a local variable of typeINTEGER. It is safest not to use thename of an existing variable, because the local declaration hides any global declaration: DECLARE ctr INTEGER := 3; BEGIN ... FOR ctr IN 1..25 LOOP ... IF ctr > 10 THEN ... -- Refers to loop counter END LOOP; -- After the loop, ctr refers to the original variable with value 3. END;
  • 39. Page 39 of 77 To reference the global variable in this example, you must use a label and dot notation, as follows: <<main>> DECLARE ctr INTEGER; ... BEGIN ... FOR ctr IN 1..25 LOOP ... IF main.ctr > 10 THEN -- refers to global variable ... END IF; END LOOP; END main; The same scope rules apply to nested FOR loops. Consider the example below. Both loop counters have the same name. To reference theouter loop counter from theinner loop, you use a label and dot notation: <<outer>> FOR step IN 1..25 LOOP FOR step IN 1..10 LOOP ... IF outer.step > 15 THEN ... END LOOP; END LOOP outer; Using the EXIT Statement in a FORLoop The EXIT statement lets a FOR loop complete early. For example, thefollowing loop normally executes ten times, but as soon as the FETCH statement fails to return a row, the loop completes no matter how many times it has executed: FOR j IN 1..10 LOOP FETCH c1 INTO emp_rec; EXIT WHEN c1%NOTFOUND; ... END LOOP; Supposeyou must exit early from a nested FOR loop. To complete not only the current loop, but also any enclosing loop, label the enclosing loop and use the label in an EXIT statement: <<outer>> FOR i IN 1..5 LOOP ... FOR j IN 1..10 LOOP FETCH c1 INTO emp_rec; EXIT outer WHEN c1%NOTFOUND; -- exit both FOR loops ... END LOOP; END LOOP outer; -- control passes here Sequential Control: GOTO and NULL Statements Unlike theIF and LOOP statements, the GOTO and NULL statements are not crucial to PL/SQL programming. The GOTO statement is seldom needed. Occasionally, it can simplify logic enough to warrant its use. The NULL statement can improve readability by making the meaning and action of conditional statements clear. Overuse of GOTO statements can result in code that is hard to understand and maintain. Use GOTO statements sparingly. For example, to branch from a deeply nested structureto an error-handling routine, raise an exception rather than use a GOTO statement. Using the GOTO Statement The GOTO statement branches to a label unconditionally. Thelabel must be unique within its scope and must precede an executable statement or a PL/SQL block. When executed, the GOTO statement transfers control to the labeled statement or block. In the following example, you go to an executable statement farther down in a sequence of statements: BEGIN ... GOTO insert_row; ... <<insert_row>> INSERT INTO emp VALUES ... END; In the next example, you go to a PL/SQL block farther up in a sequence of statements: DECLARE x NUMBER := 0; BEGIN <<increment_x>> BEGIN x := x + 1; END; IF x < 10 THEN GOTO increment_x; END IF; END; The label end_loop in the following example is not allowed because it does not precede an executable statement: DECLARE done BOOLEAN; BEGIN FOR i IN 1..50 LOOP IF done THEN GOTO end_loop; END IF; <<end_loop>> -- not allowed END LOOP; -- not an executable statement END; To correct theprevious example, add the NULL statement:: FOR i IN 1..50 LOOP IF done THEN GOTO end_loop; END IF; ... <<end_loop>> NULL; -- an executable statement END LOOP; As the following example shows, a GOTO statement can branch to an enclosing block from the current block:
  • 40. Page 40 of 77 DECLARE my_ename CHAR(10); BEGIN <<get_name>> SELECT ename INTO my_ename FROM emp WHERE ... BEGIN GOTO get_name; -- branch to enclosing block END; END; The GOTO statement branches to thefirst enclosing block in which the referenced label appears. Restrictions on the GOTO Statement Some possible destinations of a GOTO statement are not allowed. Specifically, a GOTO statement cannot branch into an IF statement, CASE statement, LOOP statement, or sub- block. For example, the following GOTO statement is not allowed: BEGIN GOTO update_row; -- can't branch into IF statement IF valid THEN <<update_row>> UPDATE emp SET ... END IF; END; A GOTO statement cannot branch from one IF statement clause to another, or from one CASE statement WHEN clause to another. A GOTO statement cannot branch from an outer block into a sub-block (that is, an inner BEGIN-END block). A GOTO statement cannot branch out of a subprogram. To end a subprogram early, you can use the RETURN statement or use GOTO to branch to a place right before the end of the subprogram. A GOTO statement cannot branch from an exception handler back into the current BEGIN- END block. However, a GOTO statement can branch from an exception handler into an enclosing block. Using the NULL Statement The NULL statement does nothing, and passes controlto thenext statement. (Some languages refer to such an instruction as a no-op.) You can use the NULL statement to indicate that you are aware of a possibility, but no action is necessary. In the following example, the NULL statement shows that you have chosen not to take any action for unnamed exceptions: EXCEPTION WHEN ZERO_DIVIDE THEN ROLLBACK; WHEN VALUE_ERROR THEN INSERT INTO errors VALUES ... COMMIT; WHEN OTHERS THEN NULL; END; The NULL statement is a handy way to create placeholders and stub procedures. In the following example, the NULL statement lets you compile this procedure, then fill in thereal body later: PROCEDURE debit_account (acct_id INTEGER, amount REAL) IS BEGIN NULL; END debit_account; 5 – Data Concurrency and Consistency This chapter explains how Oracle maintains consistent data in a multiuser database environment. Introduction to Data Concurrency and Consistency in a Multiuser Environment In a single-user database, the user can modify data in thedatabase without concern for other users modifying the same data at the same time. However, in a multiuser database, the statements within multiple simultaneous transactions can updatethe same data. Transactions executing at thesame time need to produce meaningful and consistent results. Therefore, control of data concurrency and data consistency is vital in a multiuser database.  Data concurrency means that many users can access data at thesame time.  Data consistency means that each user sees a consistent view of the data, including visible changes made by the user's own transactions and transactions of other users. To describe consistent transaction behavior when transactions run at the same time, database researchers have defined a transaction isolation model called serializability. The serializable mode of transaction behavior tries to ensure that transactions run in such a way that they appear to be executed one at a time, or serially, rather than concurrently. While this degree of isolation between transactions is generally desirable, running many applications in this mode can seriously compromise application throughput. Complete isolation of concurrently running transactions could mean that one transaction cannot perform an insert into a table being queried by another transaction. In short, real-world considerations usually require a compromise between perfect transaction isolation and performance. Oracle offers two isolation levels, providing application developers with operational modes that preserve consistency and provide high performance. Overview of Locking Mechanisms In general, multiuser databases use some form of data locking to solve the problems associated with data concurrency, consistency, and integrity. Locks are mechanisms that prevent destructive interaction between transactions accessing the same resource. Resources include two general types of objects:  User objects, such as tables and rows (structures and data)  Systemobjects not visible to users, such as shared data structures in the memory and data dictionary rows Row-Level Locking Both read committed and serializable transactions use row-level locking, and both will wait if they try to change a row updated by an uncommitted concurrent transaction. The second transaction that tries to updatea given row waits for the other transaction to commit or undo and release its lock. If that other transaction rolls back, thewaiting transaction, regardless of its isolation mode, can proceed to change thepreviously locked row as if the other transaction had not existed.
  • 41. Page 41 of 77 However, if the other blocking transaction commits and releases its locks, a read committed transaction proceeds with its intended update. A serializable transaction, however, fails with the error "Cannot serialize access", because the other transaction has committed a change that was made since the serializable transaction began. How Oracle Locks Data Locks are mechanisms that prevent destructive interaction between transactions accessing the same resource—either user objects such as tables and rows or systemobjects not visible to users, such as shared data structures in memory and data dictionary rows. In all cases, Oracle automatically obtains necessary locks when executing SQL statements, so users need not be concerned with such details. Oracle automatically uses thelowest applicable level of restrictiveness to providethe highest degree of data concurrency yet also provide fail-safe data integrity. Oracle also allows theuser to lock data manually. Transactions and Data Concurrency Oracle provides data concurrency and integrity between transactions using its locking mechanisms. Because the locking mechanisms of Oracle are tied closely to transaction control, application designers need only define transactions properly, and Oracle automatically manages locking. Keep in mind that Oracle locking is fully automatic and requires no user action. Implicit locking occurs for all SQL statements so that database users never need to lock any resource explicitly. Oracle's default locking mechanisms lock data at the lowest level of restrictiveness to guarantee data integrity while allowing the highest degree of data concurrency. Modes of Locking Oracle uses two modes of locking in a multiuser database:  Exclusive lock mode prevents theassociates resource from being shared. This lock mode is obtained to modify data. Thefirst transaction to lock a resource exclusively is theonly transaction that can alter the resource until theexclusive lock is released.  Share lock mode allows the associated resource to be shared, depending on the operations involved. Multipleusers reading data can share the data, holding share locks to prevent concurrent access by a writer (who needs an exclusive lock). Several transactions can acquire share locks on thesame resource. Lock Duration All locks acquired by statements within a transaction are held for theduration of the transaction, preventing destructive interference including dirty reads, lost updates, and destructive DDLoperations from concurrent transactions. The changes made by theSQL statements of one transaction become visible only to other transactions that start after the first transaction is committed. Oracle releases all locks acquired by the statements within a transaction when you either commit or undo thetransaction. Oracle also releases locks acquired after a savepoint when rolling back to the savepoint. However, only transactions not waiting for thepreviously locked resources can acquire locks on thenow available resources. Waiting transactions will continue to wait until after the original transaction commits or rolls back completely. Deadlocks A deadlock can occur when two or more users are waiting for data locked by each other. Deadlocks prevent some transactions from continuing to work. Figure 13-3 is a hypotheticalillustration of two transactions in a deadlock. In Figure 13-3, no problem exists at time point A, as each transaction has a row lock on the row it attempts to update. Each transaction proceeds without being terminated. However, each tries next to updatetherow currently held by the other transaction. Therefore, a deadlock results at time point B, because neither transaction can obtain the resource it needs to proceed or terminate. It is a deadlock because no matter how long each transaction waits, the conflicting locks are held. Figure 13-3 Two Transactions in a Deadlock Description of the illustration cncpt068.gif Deadlock Detection Oracle automatically detects deadlock situations and resolves them by rolling back one of the statements involved in thedeadlock, thereby releasing one set of theconflicting row locks. A corresponding message also is returned to the transaction that undergoes statement-level rollback. Thestatement rolled back is the one belonging to the transaction that detects the deadlock. Usually, thesignalled transaction should be rolled back explicitly, but it can retry therolled-back statement after waiting. Note: In distributed transactions, local deadlocks are detected by analyzing a "waits for" graph, and global deadlocks are detected by a time out. Once detected, nondistributed and distributed deadlocks are handled by the database and application in the same way. Deadlocks most often occur when transactions explicitly override the default locking of Oracle. Because Oracle itself does no lock escalation and does not use read locks for queries, but does use row-level locking (rather than page-level locking), deadlocks occur infrequently in Oracle. Avoid Deadlocks Multitabledeadlocks can usually be avoided if transactions accessing the same tables lock thosetables in thesame order, either through implicit or explicit locks. For example, all application developers might follow therule that when both a master and detail table are updated, the master table is locked first and then the detail table. If such rules are properly designed and then followed in all applications, deadlocks are very unlikely to occur. When you know you will require a sequence of locks for one transaction, consider acquiring the most exclusive (least compatible) lock first.
  • 42. Page 42 of 77 Types of Locks Oracle automatically uses different types of locks to control concurrent access to data and to prevent destructive interaction between users. Oracle automatically locks a resource on behalf of a transaction to prevent other transactions from doing something also requiring exclusive access to the same resource. The lock is released automatically when some event occurs so that the transaction no longer requires theresource. Throughout its operation, Oracle automatically acquires different types of locks at different levels of restrictiveness depending on the resource being locked and the operation being performed. Oracle locks fall into one of three general categories. Lock Description DML locks (data locks) DMLlocks protect data. For example, table locks lock entire tables, row locks lock selected rows. DDL locks (dictionary locks) DDL locks protect thestructureof schema objects—for example, the definitions of tables and views. Internal locks and latches Internal locks and latches protect internal database structures such as datafiles. Internal locks and latches are entirely automatic. The following sections discuss DMLlocks, DDLlocks, and internal locks. DML Locks The purposeof a DML(data) lock is to guarantee the integrity of data being accessed concurrently by multiple users. DMLlocks prevent destructive interference of simultaneous conflicting DMLor DDL operations. For example, Oracle DMLlocks guarantee that a specific row in a table can be updated by only one transaction at a time and that a table cannot be dropped if an uncommitted transaction contains an insert into the table. DMLoperations can acquire data locks at two different levels: for specific rows and for entire tables. Note: The acronym in parentheses after each typeof lock or lock mode is the abbreviation used in the Locks Monitor of EnterpriseManager. EnterpriseManager might display TM for any table lock, rather than indicate the mode of table lock (such as RS or SRX). Row Locks (TX) The only DMLlocks Oracle acquires automatically are row-level locks. There is no limit to the number of row locks held by a statement or transaction, and Oracle does not escalate locks from therow level to a coarser granularity. Row locking provides the finest grain locking possibleand so provides thebest possibleconcurrency and throughput. The combination of multiversion concurrency control and row-level locking means that users contend for data only when accessing the same rows, specifically:  Readers of data do not wait for writers of the same data rows.  Writers of data do not wait for readers of thesame data rows unless SELECT ... FOR UPDATE is used, which specifically requests a lock for the reader.  Writers only wait for other writers if they attempt to updatethesame rows at the same time. Note: Readers of data may have to wait for writers of the same data blocks in some very special cases of pending distributed transactions. A transaction acquires an exclusive DMLlock for each individual row modified by one of the following statements: INSERT, UPDATE, DELETE, and SELECT with the FOR UPDATE clause. A modified row is always locked exclusively so that other users cannot modify therow until the transaction holding the lock is committed or rolled back. However, if the transaction dies due to instance failure, block-level recovery makes a row available before the entire transaction is recovered. Row locks are always acquired automatically by Oracle as a result of the statements listed previously. If a transaction obtains a row lock for a row, the transaction also acquires a table lock for the corresponding table. Thetable lock prevents conflicting DDL operations that would override data changes in a current transaction. Table Locks (TM) A transaction acquires a table lock when a table is modified in the following DML statements:INSERT, UPDATE, DELETE, SELECT with the FOR UPDATE clause, and LOCK TABLE. These DMLoperations require table locks for two purposes:to reserve DMLaccess to the table on behalf of a transaction and to prevent DDL operations that would conflict with the transaction. Any table lock prevents the acquisition of an exclusive DDL lock on the same table and thereby prevents DDLoperations that require such locks. For example, a table cannot be altered or dropped if an uncommitted transaction holds a table lock for it. A table lock can be held in any of several modes: row share (RS), row exclusive (RX), share (S), share row exclusive (SRX), and exclusive (X). The restrictiveness of a table lock's mode determines the modes in which other table locks on the same table can be obtained and held. Table 13-3 shows the table lock modes that statements acquire and operations that those locks permit and prohibit. Table 13-3 Summary of Table Locks SQLStatement Mode of Table Lock Lock Modes Permitted? RS RX S SRX X SELECT...FROM table... none Y Y Y Y Y INSERT INTO table ... RX Y Y N N N UPDATE table ... RX Y* Y* N N N DELETE FROM table ... RX Y* Y* N N N SELECT ... FROM table FOR UPDATE OF ... RS Y* Y* Y* Y* N LOCK TABLE table IN ROW SHARE MODE RS Y Y Y Y N LOCK TABLE table IN ROW EXCLUSIVE MODE RX Y Y N N N LOCK TABLE table IN SHARE MODE S Y N Y N N LOCK TABLE table IN SHARE ROW EXCLUSIVE MODE SRX Y N N N N LOCK TABLE table IN EXCLUSIVE MODE X N N N N N
  • 43. Page 43 of 77 RS: row share RX: row exclusive S: share SRX: share row exclusive X: exclusive *Yes, if no conflicting row locks are held by another transaction. Otherwise, waits occur. The following sections explain each mode of table lock, from least restrictive to most restrictive. They also describe the actions that cause the transaction to acquire a table lock in that mode and which actions are permitted and prohibited in other transactions by a lock in that mode. Row Share Table Locks (RS) A row share table lock (also sometimes called a subshare table lock, SS) indicates that the transaction holding the lock on the table has locked rows in the table and intends to updatethem. A row share table lock is automatically acquired for a table when one of the following SQL statements is run: SELECT ... FROM table ... FOR UPDATE OF ... ; LOCK TABLE table IN ROW SHARE MODE; A row share table lock is theleast restrictive mode of table lock, offering thehighest degree of concurrency for a table. Permitted Operations: A row share table lock held by a transaction allows other transactions to query, insert, update, delete, or lock rows concurrently in the same table. Therefore, other transactions can obtain simultaneous row share, row exclusive, share, and share row exclusive table locks for the same table. Prohibited Operations: A row share table lock held by a transaction prevents other transactions from exclusive write access to thesame table using only thefollowing statement: LOCK TABLE table IN EXCLUSIVE MODE; Row Exclusive Table Locks (RX) A row exclusive table lock (also called a subexclusive table lock, SX) generally indicates that the transaction holding the lock has made one or more updates to rows in the table. A row exclusive table lock is acquired automatically for a table modified by the following types of statements: INSERT INTO table ... ; UPDATE table ... ; DELETE FROM table ... ; LOCK TABLE table IN ROW EXCLUSIVE MODE; A row exclusive table lock is slightly more restrictive than a row share table lock. Permitted Operations: A row exclusive table lock held by a transaction allows other transactions to query, insert, update, delete, or lock rows concurrently in the same table. Therefore, row exclusive table locks allow multiple transactions to obtain simultaneous row exclusive and row share table locks for the same table. Prohibited Operations: A row exclusive table lock held by a transaction prevents other transactions from manually locking the table for exclusive reading or writing. Therefore, other transactions cannot concurrently lock the table using the following statements: LOCK TABLE table IN SHARE MODE; LOCK TABLE table IN SHARE EXCLUSIVE MODE; LOCK TABLE table IN EXCLUSIVE MODE; Share Table Locks (S) A share table lock is acquired automatically for the table specified in the following statement: LOCK TABLE table IN SHARE MODE; Permitted Operations: A share table lock held by a transaction allows other transactions only to query the table, to lock specific rows with SELECT ... FOR UPDATE, or to run LOCK TABLE ... IN SHARE MODE statements successfully. No updates are allowed by other transactions. Multipletransactions can hold share table locks for the same table concurrently. In this case, no transaction can updatethe table (even if a transaction holds row locks as the result of a SELECT statement with the FOR UPDATE clause). Therefore, a transaction that has a share table lock can updatethe table only if no other transactions also have a share table lock on the same table. Prohibited Operations: A share table lock held by a transaction prevents other transactions from modifying the same table and from executing the following statements: LOCK TABLE table IN SHARE ROW EXCLUSIVE MODE; LOCK TABLE table IN EXCLUSIVE MODE; LOCK TABLE table IN ROW EXCLUSIVE MODE; Share Row Exclusive Table Locks (SRX) A share row exclusive table lock (also sometimes called a share-subexclusive table lock, SSX) is more restrictive than a share table lock. A share row exclusive table lock is acquired for a table as follows: LOCK TABLE table IN SHARE ROW EXCLUSIVE MODE; Permitted Operations: Only one transaction at a time can acquire a share row exclusive table lock on a given table. A share row exclusive table lock held by a transaction allows other transactions to query or lock specific rows using SELECT with theFOR UPDATE clause, but not to updatethe table. Prohibited Operations: A share row exclusive table lock held by a transaction prevents other transactions from obtaining row exclusive table locks and modifying thesame table. A share row exclusive table lock also prohibits other transactions from obtaining share, share row exclusive, and exclusive table locks, which prevents other transactions from executing thefollowing statements: LOCK TABLE table IN SHARE MODE; LOCK TABLE table IN SHARE ROW EXCLUSIVE MODE; LOCK TABLE table IN ROW EXCLUSIVE MODE; LOCK TABLE table IN EXCLUSIVE MODE; Exclusive Table Locks (X) An exclusive table lock is the most restrictive mode of table lock, allowing thetransaction that holds the lock exclusive writeaccess to the table. An exclusive table lock is acquired for a table as follows: LOCK TABLE table IN EXCLUSIVE MODE; Permitted Operations: Only one transaction can obtain an exclusive table lock for a table. An exclusive table lock permits other transactions only to query the table. Prohibited Operations: An exclusive table lock held by a transaction prohibits other transactions from performing any typeof DMLstatement or placing any typeof lock on the table. DML Locks Automatically Acquired for DML Statements The previous sections explained the different types of data locks, the modes in which they can be held, when they can be obtained, when they are obtained, and what they prohibit. The following sections summarize how Oracle automatically locks data on behalf of different DMLoperations. Table 13-4 summarizes theinformation in the following sections. Table 13-4 Locks Obtained By DML Statements DML Statement Row Locks? Mode of Table Lock
  • 44. Page 44 of 77 SELECT ... FROM table INSERT INTO table ... X RX UPDATE table ... X RX DELETE FROM table ... X RX SELECT ... FROM table ... FOR UPDATE OF ... X RS LOCK TABLE table IN ... ROW SHARE MODE RS ROW EXCLUSIVE MODE RX SHARE MODE S SHARE EXCLUSIVE MODE SRX EXCLUSIVE MODE X X: exclusive RX: row exclusive RS: row share S: share SRX: share row exclusive Default Locking for Queries Queries are the SQL statements least likely to interfere with other SQL statements because they only read data. INSERT, UPDATE, and DELETE statements can have implicit queries as part of thestatement. Queries include the following kinds of statements: SELECT INSERT ... SELECT ... ; UPDATE ... ; DELETE ... ; They do not include thefollowing statement: SELECT ... FOR UPDATE OF ... ; The following characteristics are trueof all queries that do not use the FOR UPDATE clause:  A query acquires no data locks. Therefore, other transactions can query and updatea table being queried, including thespecific rows being queried. Because queries lacking FOR UPDATE clauses do not acquire any data locks to block other operations, such queries are often referred to in Oracle as nonblocking queries.  A query does not have to wait for any data locks to be released; it can always proceed. (Queries may have to wait for data locks in some very specific cases of pending distributed transactions.) Default Locking for INSERT, UPDATE, DELETE, and SELECT ... FOR UPDATE The locking characteristics of INSERT, UPDATE, DELETE, and SELECT ... FOR UPDATE statements are as follows:  The transaction that contains a DMLstatement acquires exclusive row locks on the rows modified by the statement. Other transactions cannot updateor delete the locked rows until the locking transaction either commits or rolls back.  The transaction that contains a DMLstatement does not need to acquire row locks on any rows selected by a subquery or an implicit query, such as a query in a WHERE clause. A subquery or implicit query in a DMLstatement is guaranteed to be consistent as of thestart of the query and does not see the effects of the DMLstatement it is part of.  A query in a transaction can see the changes made by previous DMLstatements in the same transaction, but cannot see the changes of other transactions begun after its own transaction.  In addition to thenecessary exclusive row locks, a transaction that contains a DMLstatement acquires at least a row exclusive table lock on thetable that contains the affected rows. If thecontaining transaction already holds a share, share row exclusive, or exclusive table lock for that table, the row exclusive table lock is not acquired. If thecontaining transaction already holds a row share table lock, Oracle automatically converts this lock to a row exclusive table lock. Explicit (Manual) Data Locking Oracle always performs locking automatically to ensure data concurrency, data integrity, and statement-level read consistency. However, you can override the Oracle default locking mechanisms. Overriding the default locking is useful in situations such as these:  Applications require transaction-level read consistency or repeatable reads. In other words, queries in them must produce consistent data for the duration of the transaction, not reflecting changes by other transactions. You can achieve transaction-level read consistency by using explicit locking, read-only transactions, serializable transactions, or by overriding default locking.  Applications require that a transaction have exclusive access to a resource so that the transaction does not have to wait for other transactions to complete. Oracle's automatic locking can be overridden at the transaction level or thesession level. At the transaction level, transactions that include the following SQL statements override Oracle's default locking:  The SET TRANSACTION ISOLATION LEVEL statement  The LOCK TABLE statement (which locks either a table or, when used with views, the underlying base tables)  The SELECT ... FOR UPDATE statement Locks acquired by these statements are released after the transaction commits or rolls back. At the session level, a session can set therequired transaction isolation level with the ALTER SESSION statement. Note: If Oracle's default locking is overridden at any level, thedatabase administrator or application developer should ensure that the overriding locking procedures operatecorrectly. Thelocking procedures must satisfy thefollowing criteria: data integrity is guaranteed, data concurrency is acceptable, and deadlocks are not possible or are appropriately handled. 6 - Performing SQL Operations from PL/SQL
  • 45. Page 45 of 77 Overview of Implicit Cursor Attributes Implicit cursor attributes return information about theexecution of an INSERT, UPDATE, DELETE, or SELECT INTO statement. Thevalues of the cursor attributes always refer to the most recently executed SQL statement. Before Oracle opens the SQL cursor, the implicit cursor attributes yield NULL. Note: The SQL cursor has another attribute, %BULK_ROWCOUNT, designed for use with the FORALL statement. %FOUND Attribute: Has a DML Statement ChangedRows? Until a SQL data manipulation statement is executed, %FOUND yields NULL. Thereafter, %FOUND yields TRUE if an INSERT, UPDATE, or DELETE statement affected one or more rows, or a SELECT INTO statement returned one or more rows. Otherwise, %FOUND yields FALSE. In the following example, you use %FOUND to insert a row if a delete succeeds: DELETE FROM emp WHERE empno = my_empno; IF SQL%FOUND THEN -- delete succeeded INSERT INTO new_emp VALUES (my_empno, my_ename, ...); %ISOPEN Attribute: Always FALSE for Implicit Cursors Oracle closes the SQL cursor automatically after executing its associated SQL statement. As a result, %ISOPEN always yields FALSE. %NOTFOUND Attribute: Has a DML Statement Failedto Change Rows? %NOTFOUND is the logical oppositeof %FOUND. %NOTFOUND yields TRUE if an INSERT, UPDATE, or DELETE statement affected no rows, or a SELECT INTO statement returned no rows. Otherwise, %NOTFOUND yields FALSE. %ROWCOUNT Attribute: How Many Rows Affected So Far? %ROWCOUNT yields the number of rows affected by an INSERT, UPDATE, or DELETE statement, or returned by a SELECT INTO statement. %ROWCOUNT yields 0 if an INSERT, UPDATE, or DELETE statement affected no rows, or a SELECT INTO statement returned no rows. In the following example, you use %ROWCOUNT to take action if more than ten rows have been deleted: DELETE FROM emp WHERE ... IF SQL%ROWCOUNT > 10 THEN -- more than 10 rows were deleted ... END IF; If a SELECT INTO statement returns more than one row, PL/SQL raises the predefined exception TOO_MANY_ROWS and %ROWCOUNT yields 1, not the actual number of rows that satisfy thequery. Guidelines forUsing Implicit Cursor Attributes The values of the cursor attributes always refer to the most recently executed SQL statement, wherever that statement is. It might be in a different scope (for example, in a sub-block). To save an attributevalue for later use, assign it to a Boolean variable immediately. Doing other operations, such as procedure calls, might change thevalue of %NOTFOUND before you can test it. The %NOTFOUND attributeis not useful in combination with the SELECT INTO statement: If a SELECT INTO statement fails to return a row, PL/SQL raises the predefined exception NO_DATA_FOUND immediately, interrupting theflow of control before you can check %NOTFOUND. A SELECT INTO statement that calls a SQL aggregate function always returns a value or a null. After such a statement, the %NOTFOUND attributeis always FALSE, so checking it is unnecessary. Using PL/SQL Records in SQL INSERT and UPDATE Statements Instead of listing each field of a PL/SQL record in INSERT and UPDATE statements, you can use PL/SQL records directly. The most convenient technique is to declare the record using a %ROWTYPE attribute, so that it has exactly the same fields as the SQL table: DECLARE emp_rec emp%ROWTYPE; BEGIN emp_rec.eno := 1500; emp_rec.ename := 'Steven Hill'; emp_rec.sal := '40000'; -- A %ROWTYPE value can fill in all the row fields. INSERT INTO emp VALUES emp_rec; -- The fields of a %ROWTYPE can completely replace the table columns. UPDATE emp SET ROW = emp_rec WHERE eno = 100; END; / Although this technique integrates PL/SQL variables and types with SQLDML statements, you cannot use PL/SQL records as bind variables in dynamic SQL statements. Issuing Queries from PL/SQL PL/SQL lets you performqueries (SELECT statements in SQL) and access individual fields or entire rows from the result set. Depending on the complexity of the processing that you want to do on thequery results, you can use various notations. Selecting At Most One Row: SELECT INTO Statement If you expect a query to only return one row, you can write a regular SQL SELECT statement with an additional INTO clause specifying the PL/SQL variable to hold the result: If the query might return more than one row, but you do not care about values after the first, you can restrict any result set to a single row by comparing the ROWNUM value: If the query might return no rows at all, use an exception handler to specify any actions to take when no data is found: If you just want to check whether a condition exists in your data, you might be able to code thequery with the COUNT(*) operator, which always returns a number and never raises the NO_DATA_FOUND exception: Selecting Multiple Rows: BULK COLLECT Clause If you need to bring a large quantity of data into local PL/SQL variables, rather than looping through a result set one row at a time, you can use the BULK COLLECT clause. When you query only certain columns, you can store all theresults for each column in a separatecollection variable: SELECT employee_id, last_name, salary FROM employees BULK COLLECT INTO all_employee_ids, all_last_names, all_salaries; When you query all the columns of a table, you can storethe entire result set in a collection of records, which makes it convenient to loop through the results and refer to different columns:
  • 46. Page 46 of 77 SELECT * FROM employees BULK COLLECT INTO all_employees; FOR i IN all_employees.FIRST .. all_employees.LAST LOOP ... END LOOP; This technique can be very fast, but also very memory-intensive. If you use it often, you might be able to improve your code by doing more of the work in SQL:  If you only need to loop once through theresult set, use a FOR loop as described in the following sections. This technique avoids thememory overhead of storing a copy of theresult set.  If you are looping through theresult set to scan for certain values or filter the results into a smaller set, do this scanning or filtering in the original query instead. You can add more WHERE clauses in simple cases, or use set operators such as INTERSECT and MINUS if you are comparing two or more sets of results.  If you are looping through theresult set and running another query or a DML statement for each result row, you can probably find a more efficient technique. For queries, look at including subqueries or EXISTS or NOT EXISTS clauses in the original query. For DMLstatements, look at the FORALL statement, which is much faster than coding thesestatements inside a regular loop. Looping Through Multiple Rows: Cursor FOR Loop Perhaps the most common case of a query is one where you issue the SELECT statement, then immediately loop once through the rows of the result set. PL/SQL lets you use a simple FOR loop for this kind of query: The iterator variable for the FOR loop does not need to be declared in advance. It is a %ROWTYPE record whosefield names match the column names from the query, and that exists only during the loop. When you use expressions rather than explicit column names, use column aliases so that you can refer to the corresponding values inside the loop: Performing Complicated Query Processing:Explicit Cursors For full control over query processing, you can use explicit cursors in combination with the OPEN, FETCH, and CLOSE statements. You might want to specify a query in one place but retrieve therows somewhere else, even in another subprogram. Or you might want to choose very different query parameters, such as ORDER BY or GROUP BY clauses, depending on thesituation. Or you might want to process some rows differently than others, and so need more than a simple loop. Because explicit cursors are so flexible, you can choose from different notations depending on your needs. The following sections describe all the query-processing features that explicit cursors provide. Querying Data with PL/SQL In traditional database programming, you process query results using an internal data structurecalled a cursor. In most situations, PL/SQL can manage thecursor for you, so that code to process query results is straightforward and compact. This section discusses how to process both simple queries where PL/SQL manages everything, and complex queries where you interact with the cursor. Querying Data with PL/SQL: Implicit Cursor FOR Loop With PL/SQL, it is very simple to issue a query, retrieve each row of the result into a %ROWTYPE record, and process each row in a loop:  You include the text of thequery directly in the FOR loop.  PL/SQL creates a record variable with fields corresponding to the columns of the result set.  You refer to thefields of this record variable inside the loop. You can perform tests and calculations, display output, or storetheresults somewhere else. Here is an example that you can run in SQL*Plus. It does a query to get thename and status of every index that you can access. BEGIN FOR item IN ( SELECT object_name, status FROM user_objects WHERE object_type = 'INDEX' AND object_name NOT LIKE '%$%' ) LOOP dbms_output.put_line('Index = ' || item.object_name || ', Status = ' || item.status); END LOOP; END; / Before each iteration of the FOR loop, PL/SQL fetches into theimplicitly declared record. The sequence of statements inside the loop is executed once for each row that satisfies the query. When you leave the loop, the cursor is closed automatically. The cursor is closed even if you use an EXIT or GOTO statement to leave the loop before all rows are fetched, or an exception is raised inside the loop. Querying Data with PL/SQL: Explicit Cursor FOR Loops IIf you need to reference thesame query from different parts of the same procedure, you can declare a cursor that specifies thequery, and process the results using a FOR loop. The following PL/SQ block runs two variations of thesame query, first finding all the tables you can access, then all the indexes you can access: DECLARE CURSOR c1 IS SELECT object_name, status FROM user_objects WHERE object_type = 'TABLE' AND object_name NOT LIKE '%$%'; BEGIN FOR item IN c1 LOOP dbms_output.put_line('Table = ' || item.object_name || ', Status = ' || item.status); END LOOP; END; / Overview of Explicit Cursors When you need precise control over query processing, you can explicitly declare a cursor in the declarative part of any PL/SQL block, subprogram, or package. You use three commands to control a cursor: OPEN, FETCH, and CLOSE. First, you initialize the cursor with the OPEN statement, which identifies the result set. Then, you can execute FETCH repeatedly until all rows have been retrieved, or you can use the BULK COLLECT clause to fetch all rows at once. When thelast row has been processed, you release the cursor with the CLOSE statement. This technique requires more code than other techniques such as theimplicit cursor FOR loop. Its advantage is flexibility. You can:  Process several queries in parallel by declaring and opening multiple cursors.
  • 47. Page 47 of 77  Process multiple rows in a single loop iteration, skip rows, or split theprocessing into more than one loop. Declaring a Cursor You must declare a cursor before referencing it in other statements. You give the cursor a name and associate it with a specific query. You can optionally declare a return typefor the cursor (such as table_name%ROWTYPE). You can optionally specify parameters that you use in the WHERE clause instead of referring to local variables. These parameters can have default values. For example, you might declare cursors like these: DECLARE CURSOR c1 IS SELECT empno, ename, job, sal FROM emp WHERE sal > 2000; CURSOR c2 RETURN dept%ROWTYPE IS SELECT * FROM dept WHERE deptno = 10; The cursor is not a PL/SQL variable: you cannot assign values to a cursor or use it in an expression. Cursors and variables follow thesame scoping rules. Naming cursors after database tables is possible but not recommended. A cursor can take parameters, which can appear in the associated query wherever constants can appear. Theformal parameters of a cursor must be IN parameters; they supply values in the query, but do not return any values from the query. You cannot imposethe constraint NOT NULL on a cursor parameter. As the example below shows, you can initialize cursor parameters to default values. You can pass different numbers of actual parameters to a cursor, accepting or overriding the default values as you please. Also, you can add new formal parameters without having to change existing references to thecursor. DECLARE CURSOR c1 (low INTEGER DEFAULT 0, high INTEGER DEFAULT 99) IS SELECT ... Cursor parameters can be referenced only within the query specified in thecursor declaration. The parameter values are used by the associated query when thecursor is opened. Opening a Cursor Opening the cursor executes the query and identifies the result set, which consists of all rows that meet thequery search criteria. For cursors declared using the FOR UPDATE clause, the OPEN statement also locks thoserows. An example of the OPEN statement follows: DECLARE CURSOR c1 IS SELECT ename, job FROM emp WHERE sal < 3000; ... BEGIN OPEN c1; ... END; Rows in theresult set are retrieved by the FETCH statement, not when the OPEN statement is executed. Fetching with a Cursor Unless you use theBULK COLLECT clause (discussed in the next section), the FETCH statement retrieves the rows in theresult set one at a time. Each fetch retrieves the current row and advances the cursor to the next row in the result set. You can store each column in a separate variable, or store theentire row in a record that has the appropriatefields (usually declared using %ROWTYPE): -- This cursor queries 3 columns. -- Each column is fetched into a separate variable. FETCH c1 INTO my_empno, my_ename, my_deptno; -- This cursor was declared as SELECT * FROM employees. -- An entire row is fetched into the my_employees record, which -- is declared with the type employees%ROWTYPE. FETCH c2 INTO my_employees; For each column value returned by thequery associated with the cursor, there must be a corresponding, type-compatiblevariable in the INTO list. Typically, you usethe FETCH statement in the following way: LOOP FETCH c1 INTO my_record; EXIT WHEN c1%NOTFOUND; -- process data record END LOOP; The query can reference PL/SQL variables within its scope. Any variables in the query are evaluated only when the cursor is opened. In thefollowing example, each retrieved salary is multiplied by 2, even though factor is incremented after every fetch: DECLARE my_sal employees.salary%TYPE; my_job employees.job_id%TYPE; factor INTEGER := 2; CURSOR c1 IS SELECT factor*salary FROM employees WHERE job_id = my_job; BEGIN OPEN c1; -- here factor equals 2 LOOP FETCH c1 INTO my_sal; EXIT WHEN c1%NOTFOUND; factor := factor + 1; -- does not affect FETCH END LOOP; END; / To change the result set or the values of variables in the query, you must close and reopen the cursor with the input variables set to their new values. However, you can use a different INTO list on separate fetches with the same cursor. Each fetch retrieves another row and assigns values to the target variables, as the following example shows: DECLARE CURSOR c1 IS SELECT last_name FROM employees ORDER BY last_name; name1 employees.last_name%TYPE; name2 employees.last_name%TYPE; name3 employees.last_name%TYPE; BEGIN OPEN c1; FETCH c1 INTO name1; -- this fetches first row FETCH c1 INTO name2; -- this fetches second row FETCH c1 INTO name3; -- this fetches third row CLOSE c1;
  • 48. Page 48 of 77 END; / If you fetch past thelast row in theresult set, the values of thetarget variables are undefined. Note: Eventually, the FETCH statement fails to return a row. When that happens, no exception is raised. To detect the failure, use the cursor attribute %FOUND or %NOTFOUND. Fetching Bulk Data with a Cursor The BULK COLLECT clause lets you fetch all rows from the result set at once. In the following example, you bulk-fetch from a cursor into two collections: DECLARE TYPE NumTab IS TABLE OF employees.employee_id%TYPE; TYPE NameTab IS TABLE OF employees.last_name%TYPE; nums NumTab; names NameTab; CURSOR c1 IS SELECT employee_id, last_name FROM employees WHERE job_id = 'ST_CLERK'; BEGIN OPEN c1; FETCH c1 BULK COLLECT INTO nums, names; -- Here is where you iterate through the elements in the NUMS and -- NAMES collections. NULL; CLOSE c1; END; / Closing a Cursor The CLOSE statement disables the cursor, and the result set becomes undefined. Once a cursor is closed, you can reopen it, which runs thequery again with the latest values of any cursor parameters and variables referenced in the WHERE clause. Any other operation on a closed cursor raises the predefined exception INVALID_CURSOR. Writing Maintainable PL/SQL Queries Instead of referring to local variables, you can declare a cursor that accepts parameters, and pass values for thoseparameters when you open the cursor. If thequery is usually issued with certain values, you can make those values the defaults. You can use either positionalnotation or named notation to pass the parameter values. Example 6-1 Passing Parameters to a Cursor FOR Loop The following example computes the totalwages paid to employees in a specified department. DECLARE CURSOR c1 (name VARCHAR2, max_wage NUMBER) IS SELECT * FROM employees WHERE last_name = name and salary < max_wage; BEGIN FOR person IN c1('Austin', 30000) LOOP -- process data record dbms_output.put_line('Name = ' || person.last_name || ', salary = ' || person.salary); END LOOP; END; / Example 6-2 Passing Parameters to ExplicitCursors For example, here are several ways to open a cursor: DECLARE emp_name employees.last_name%TYPE := 'Austin'; emp_salary employees.salary%TYPE := 30000; my_record employees%ROWTYPE; CURSOR c1 (name VARCHAR2, max_wage NUMBER) IS SELECT * FROM employees WHERE last_name = name and salary < max_wage; BEGIN -- Any ofthe following statements opens the cursor: -- OPEN c1('Austin', 3000); -- OPEN c1('Austin', emp_salary); -- OPEN c1(emp_name, 3000); -- OPEN c1(emp_name, emp_salary); OPEN c1(emp_name, emp_salary); LOOP FETCH c1 INTO my_record; EXIT WHEN c1%NOTFOUND; -- process data record dbms_output.put_line('Name = ' || my_record.last_name || ', salary = ' || my_record.salary); END LOOP; END; / To avoid confusion, use different names for cursor parameters and the PL/SQL variables that you pass into those parameters. Formal parameters declared with a default value do not need a corresponding actual parameter. If you omit them, they assume their default values when the OPEN statement is executed. Using Cursor Attributes Every explicit cursor and cursor variable has four attributes: %FOUND, %ISOPEN %NOTFOUND, and %ROWCOUNT. When appended to the cursor or cursor variable, these attributes return useful information about the execution of a data manipulation statement. You can use cursor attributes in procedural statements but not in SQL statements. Overview of Explicit Cursor Attributes Explicit cursor attributes return information about theexecution of a multi-row query. When an explicit cursor or a cursor variable is opened, therows that satisfy theassociated query are identified and form theresult set. Rows are fetched from the result set. %FOUND Attribute: Has a Row Been Fetched? After a cursor or cursor variable is opened but before the first fetch, %FOUND returns NULL. After any fetches, it returns TRUE if the last fetch returned a row, or FALSE if thelast fetch did not return a row. The following example uses %FOUND to select an action: DECLARE CURSOR c1 IS SELECT last_name, salary FROM employees WHERE ROWNUM < 11; my_ename employees.last_name%TYPE; my_salary employees.salary%TYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO my_ename, my_salary; IF c1%FOUND THEN -- fetch succeeded dbms_output.put_line('Name = ' || my_ename || ', salary = ' || my_salary); ELSE -- fetch failed, so exit loop
  • 49. Page 49 of 77 EXIT; END IF; END LOOP; END; / If a cursor or cursor variable is not open, referencing it with %FOUND raises thepredefined exception INVALID_CURSOR. %ISOPEN Attribute: Is the Cursor Open? %ISOPEN returns TRUE if its cursor or cursor variable is open;otherwise, %ISOPEN returns FALSE. Thefollowing example uses %ISOPEN to select an action: DECLARE CURSOR c1 IS SELECT last_name, salary FROM employees WHERE ROWNUM < 11; the_name employees.last_name%TYPE; the_salary employees.salary%TYPE; BEGIN IF c1%ISOPEN = FALSE THEN -- cursor was not already open OPEN c1; END IF; FETCH c1 INTO the_name, the_salary; CLOSE c1; END; / %NOTFOUND Attribute: Has a Fetch Failed? %NOTFOUND is the logical oppositeof %FOUND. %NOTFOUND yields FALSE if thelast fetch returned a row, or TRUE if thelast fetch failed to return a row. In the following example, you use %NOTFOUND to exit a loop when FETCH fails to return a row: DECLARE CURSOR c1 IS SELECT last_name, salary FROM employees WHERE ROWNUM < 11; my_ename employees.last_name%TYPE; my_salary employees.salary%TYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO my_ename, my_salary; IF c1%NOTFOUND THEN -- fetch failed, so exit loop -- A shorter form of this test is "EXIT WHEN c1%NOTFOUND;" EXIT; ELSE -- fetch succeeded dbms_output.put_line('Name = ' || my_ename || ', salary = ' || my_salary); END IF; END LOOP; END; / Before thefirst fetch, %NOTFOUND returns NULL. If FETCH never executes successfully, the loop is never exited, because the EXIT WHEN statement executes only if its WHEN condition is true. To be safe, you might want to use the following EXIT statement instead: EXIT WHEN c1%NOTFOUND OR c1%NOTFOUND IS NULL; If a cursor or cursor variable is not open, referencing it with %NOTFOUND raises an INVALID_CURSOR exception. %ROWCOUNT Attribute: How Many Rows Fetched So Far? When its cursor or cursor variable is opened, %ROWCOUNT is zeroed. Before thefirst fetch, %ROWCOUNT yields 0. Thereafter, it yields the number of rows fetched so far. The number is incremented if thelast fetch returned a row. The following example uses %ROWCOUNT to test if more than ten rows have been fetched: DECLARE CURSOR c1 IS SELECT last_name FROM employees WHERE ROWNUM < 11; name employees.last_name%TYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO name; EXIT WHEN c1%NOTFOUND; dbms_output.put_line(c1%ROWCOUNT || '. ' || name); IF c1%ROWCOUNT = 5 THEN dbms_output.put_line('--- Fetched 5th record ---'); END IF; END LOOP; CLOSE c1; END; / If a cursor or cursor variable is not open, referencing it with %ROWCOUNT raises INVALID_CURSOR. Table 6-1 shows what each cursor attributereturns before and after you execute an OPEN, FETCH, or CLOSE statement. Table 6-1 Cursor Attribute Values %FOUN D %ISOPE N %NOTFOUN D %ROWCOUN T OPEN befor e exception FALSE exception exception after NULL TRUE NULL 0 First FETCH befor e NULL TRUE NULL 0 after TRUE TRUE FALSE 1 Next FETCH(es ) befor e TRUE TRUE FALSE 1 after TRUE TRUE FALSE data dependent Last FETCH befor e TRUE TRUE FALSE data dependent after FALSE TRUE TRUE data dependent CLOSE befor e FALSE TRUE TRUE data dependent after exception FALSE exception exception Notes: 1. Referencing %FOUND, %NOTFOUND, or %ROWCOUNT before a cursor is opened or after it is closedraises INVALID_CURSOR. 2. After the first FETCH, if the result set was empty, %FOUND yields FALSE, %NOTFOUND yields TRUE, and %ROWCOUNT yields 0. Using Cursor Variables (REF CURSORs)
  • 50. Page 50 of 77 Like a cursor, a cursor variable points to the current row in theresult set of a multi-row query. A cursor variable is more flexible because it is not tied to a specific query. You can open a cursor variable for any query that returns the right set of columns. You pass a cursor variable as a parameter to local and stored subprograms. Opening the cursor variable in one subprogram, and processing it in a different subprogram, helps to centralize data retrieval. This technique is also useful for multi-language applications, where a PL/SQL subprogram might return a result set to a subprogram written in a different language. Cursor variables are available to every PL/SQL client. For example, you can declare a cursor variable in a PL/SQL host environment such as an OCI or Pro*C program, then pass it as an input host variable (bind variable) to PL/SQL. Application development tools such as Oracle Forms and Oracle Reports, which have a PL/SQL engine, can use cursor variables entirely on the client side. Or, you can pass cursor variables back and forth between a client and the database server through remote procedure calls. What Are Cursor Variables (REF CURSORs)? Cursor variables are like pointers to result sets. You use them when you want to performa query in one subprogram, and process theresults in a different subprogram (possibly one written in a different language). A cursor variable has datatype REF CURSOR, and you might see them referred to informally as REF CURSORs. Unlike an explicit cursor, which always refers to the same query work area, a cursor variable can refer to different work areas. You cannot use a cursor variable where a cursor is expected, or vice versa. Why Use Cursor Variables? You use cursor variables to pass query result sets between PL/SQL stored subprograms and various clients. PL/SQL and its clients share a pointer to the query work area in which the result set is stored. For example, an OCI client, Oracle Forms application, and Oracle database server can all refer to the same work area. A query work area remains accessible as long as any cursor variable points to it, as you pass thevalue of a cursor variable from one scopeto another. For example, if you pass a host cursor variable to a PL/SQL block embedded in a Pro*C program, the work area to which the cursor variable points remains accessible after theblock completes. If you have a PL/SQL engine on the client side, calls from client to server impose no restrictions. For example, you can declare a cursor variable on the client side, open and fetch from it on the server side, then continue to fetch from it back on the client side. You can also reduce network traffic by having a PL/SQL block open or close several host cursor variables in a single round trip. Declaring REF CURSOR Types and Cursor Variables To create cursor variables, you define a REF CURSOR type, then declare cursor variables of that type. You can define REF CURSOR types in any PL/SQL block, subprogram, or package. In the following example, you declare a REF CURSOR typethat represents aresult set from theDEPARTMENTS table: DECLARE TYPE DeptCurTyp IS REF CURSOR RETURN departments%ROWTYPE; REF CURSOR types can be strong (with a return type) or weak (with no return type). Strong REF CURSOR types areless error pronebecause the PL/SQL compiler lets you associate a strongly typed cursor variable only with queries that return the right set of columns. Weak REF CURSOR types aremore flexible because thecompiler lets you associate a weakly typed cursor variable with any query. Because there is no typechecking with a weak REF CURSOR, all such types are interchangeable. Instead of creating a new type, you can use the predefined type SYS_REFCURSOR. Once you define a REF CURSOR type, you can declare cursor variables of that typein any PL/SQL block or subprogram. DECLARE TYPE EmpCurTyp IS REF CURSOR RETURN emp%ROWTYPE; -- strong TYPE GenericCurTyp IS REF CURSOR; -- weak cursor1 EmpCurTyp; cursor2 GenericCurTyp; my_cursor SYS_REFCURSOR; -- didn't need to declare a new type above The following example declares the cursor variable dept_cv: DECLARE TYPE DeptCurTyp IS REF CURSOR RETURN dept%ROWTYPE; dept_cv DeptCurTyp; -- declare cursor variable To avoid declaring thesame REF CURSOR typein each subprogram that uses it, you can put the REF CURSOR declaration in a package spec. You can declare cursor variables of that typein the corresponding package body, or within your own procedure or function. Example 6-3 Cursor Variable Returning %ROWTYPE In the RETURN clause of a REF CURSOR typedefinition, you can use %ROWTYPE to refer to a strongly typed cursor variable: DECLARE TYPE TmpCurTyp IS REF CURSOR RETURN employees%ROWTYPE; tmp_cv TmpCurTyp; -- declare cursor variable TYPE EmpCurTyp IS REF CURSOR RETURN tmp_cv%ROWTYPE; emp_cv EmpCurTyp; -- declare cursor variable BEGIN NULL; END; / Example 6-4 Cursor Variable Returning %TYPE You can also use %TYPE to provide thedatatypeof a record variable: DECLARE dept_rec departments%ROWTYPE; -- declare record variable TYPE DeptCurTyp IS REF CURSOR RETURN dept_rec%TYPE; dept_cv DeptCurTyp; -- declare cursor variable BEGIN NULL; END; / Example 6-5 Cursor Variable Returning Record Type This example specifies a user-defined RECORD typein the RETURN clause: DECLARE TYPE EmpRecTyp IS RECORD ( employee_id NUMBER, last_name VARCHAR2(30), salary NUMBER(7,2)); TYPE EmpCurTyp IS REF CURSOR RETURN EmpRecTyp; emp_cv EmpCurTyp; -- declare cursor variable BEGIN NULL; END;
  • 51. Page 51 of 77 / Passing CursorVariables As Parameters You can declare cursor variables as the formal parameters of functions and procedures. The following example defines a REF CURSOR type, then declares a cursor variable of that typeas a formal parameter: DECLARE TYPE EmpCurTyp IS REF CURSOR RETURN employees%ROWTYPE; emp EmpCurTyp; -- Once we have a result set, we can process all the rows -- inside a single procedure rather than calling a procedure -- for each row. PROCEDURE process_emp_cv (emp_cv IN EmpCurTyp) IS person employees%ROWTYPE; BEGIN dbms_output.put_line('-----'); dbms_output.put_line('Here are the names from the result set:'); LOOP FETCH emp_cv INTO person; EXIT WHEN emp_cv%NOTFOUND; dbms_output.put_line('Name = ' || person.first_name || ' ' || person.last_name); END LOOP; END; BEGIN -- First find 10 arbitrary employees. OPEN emp FOR SELECT * FROM employees WHERE ROWNUM < 11; process_emp_cv(emp); CLOSE emp; -- Then find employees matching a condition. OPEN emp FOR SELECT * FROM employees WHERE last_name LIKE 'R%'; process_emp_cv(emp); CLOSE emp; END; / Note: Like all pointers, cursor variables increase the possibility of parameter aliasing. Controlling Cursor Variables: OPEN-FOR, FETCH, and CLOSE You use three statements to control a cursor variable: OPEN-FOR, FETCH, and CLOSE. First, you OPEN a cursor variable FOR a multi-row query. Then, you FETCH rows from the result set. When all the rows are processed, you CLOSE thecursor variable. Opening a Cursor Variable The OPEN-FOR statement associates a cursor variable with a multi-row query, executes the query, and identifies the result set. OPEN {cursor_variable | :host_cursor_variable} FOR { select_statement | dynamic_string [USING bind_argument[, bind_argument]...] }; The cursor variable can be declared directly in PL/SQL, or in a PL/SQL host environment such as an OCI program. The SELECT statement for the query can be coded directly in thestatement, or can be a string variable or string literal. When you use a string as thequery, it can include placeholders for bind variables, and you specify thecorresponding values with a USING clause. Note: This section discusses the staticSQL case, in which select_statement is used. Unlike cursors, cursor variables take no parameters. Instead, you can pass whole queries (not just parameters) to a cursor variable. Thequery can reference host variables and PL/SQL variables, parameters, and functions. The example below opens a cursor variable. Notice that you can apply cursor attributes (%FOUND, %NOTFOUND, %ISOPEN, and %ROWCOUNT) to a cursor variable. DECLARE TYPE EmpCurTyp IS REF CURSOR RETURN employees%ROWTYPE; emp_cv EmpCurTyp; BEGIN IF NOT emp_cv%ISOPEN THEN /* Open cursor variable. */ OPEN emp_cv FOR SELECT * FROM employees; END IF; CLOSE emp_cv; END; / Other OPEN-FOR statements can open thesame cursor variable for different queries. You need not close a cursor variable before reopening it. (Recall that consecutive OPENs of a staticcursor raise the predefined exception CURSOR_ALREADY_OPEN.) When you reopen a cursor variable for a different query, theprevious query is lost. Fetching from a Cursor Variable The FETCH statement retrieves rows from the result set of a multi-row query. It works the same with cursor variables as with explicit cursors. Example 6-9 Fetching from a Cursor Variable into a Record The following example fetches rows one at a time from a cursor variable into a record: DECLARE TYPE EmpCurTyp IS REF CURSOR RETURN employees%ROWTYPE; emp_cv EmpCurTyp; emp_rec employees%ROWTYPE; BEGIN OPEN emp_cv FOR SELECT * FROM employees WHERE salary < 3000; LOOP /* Fetch from cursor variable. */ FETCH emp_cv INTO emp_rec; EXIT WHEN emp_cv%NOTFOUND; -- exit when last row is fetched -- process data record dbms_output.put_line('Name = ' || emp_rec.first_name || ' ' || emp_rec.last_name); END LOOP; CLOSE emp_cv; END; / Example 6-10 Fetching from a Cursor Variable into Collections Using theBULK COLLECT clause, you can bulk fetch rows from a cursor variable into one or more collections: DECLARE TYPE EmpCurTyp IS REF CURSOR; TYPE NameList IS TABLE OF employees.last_name%TYPE; TYPE SalList IS TABLE OF employees.salary%TYPE; emp_cv EmpCurTyp; names NameList; sals SalList; BEGIN OPEN emp_cv FOR SELECT last_name, salary FROM employees WHERE salary < 3000; FETCH emp_cv BULK COLLECT INTO names, sals; CLOSE emp_cv;
  • 52. Page 52 of 77 -- Now loop through the NAMES and SALS collections. FOR i IN names.FIRST .. names.LAST LOOP dbms_output.put_line('Name = ' || names(i) || ', salary = ' || sals(i)); END LOOP; END; / Any variables in the associated query are evaluated only when thecursor variable is opened. To change the result set or thevalues of variables in thequery, reopen the cursor variable with the variables set to new values. You can use a different INTO clause on separatefetches with thesame cursor variable. Each fetch retrieves another row from the same result set. PL/SQL makes sure thereturn typeof thecursor variable is compatible with the INTO clause of the FETCH statement. If there is a mismatch, an error occurs at compile time if the cursor variable is strongly typed, or at run time if it is weakly typed. At run time, PL/SQL raises the predefined exception ROWTYPE_MISMATCH before thefirst fetch. If you trap the error and execute the FETCH statement using a different (compatible) INTO clause, no rows are lost. When you declare a cursor variable as the formal parameter of a subprogram that fetches from the cursor variable, you must specify the IN or IN OUT mode. If the subprogram also opens the cursor variable, you must specify the IN OUT mode. If you try to fetch from a closed or never-opened cursor variable, PL/SQL raises the predefined exception INVALID_CURSOR. Closing a CursorVariable The CLOSE statement disables a cursor variable and makes the associated result set undefined. Close the cursor variable after thelast row is processed. When declaring a cursor variable as the formal parameter of a subprogram that closes the cursor variable, you must specify the IN or IN OUT mode. If you try to close an already-closed or never-opened cursor variable, PL/SQL raises the predefined exception INVALID_CURSOR. 7 - Overview of Transaction Processing in PL/SQL This section explains how to do transaction processing with PL/SQL. You should already be familiar with theidea of transactions, and how to ensure the consistency of a database, such as the COMMIT, SAVEPOINT, and ROLLBACK statements. These are Oracle features, available through all programming languages, that let multiple users work on the database concurrently, and ensure that each user sees a consistent version of data and that all changes are applied in theright order. You usually do not need to writeextra code to prevent problems with multiple users accessing data concurrently. Oracle uses locks to control concurrent access to data, and locks only theminimum amount of data necessary, for as little time as possible. You can request locks on tables or rows if you really do need this level of control. You can choose from several modes of locking such as row share and exclusive. Using COMMIT, SAVEPOINT, and ROLLBACK in PL/SQL You can include COMMIT, SAVEPOINT, and ROLLBACK statements directly in your PL/SQL programs. The COMMIT statement ends the current transaction, making any changes made during that transaction permanent, and visible to other users. The ROLLBACK statement ends the current transaction and undoes any changes made during that transaction. If you make a mistake, such as deleting the wrong row from a table, a rollback restores the original data. If you cannot finish a transaction because an exception is raised or a SQL statement fails, a rollback lets you take corrective action and perhaps start over. SAVEPOINT names and marks the current point in the processing of a transaction. Savepoints let you roll back part of a transaction instead of thewhole transaction. Consider a transaction that transfers money from one bank account to another. It is important that themoney come out of one account, and into the other, at exactly thesame moment. Otherwise, a problem partway through might make the money be lost from both accounts or be duplicated in both accounts. BEGIN UPDATE accts SET bal = my_bal - debit WHERE acctno = 7715; UPDATE accts SET bal = my_bal + credit WHERE acctno = 7720; COMMIT WORK; END; Transactions are not tied to PL/SQL BEGIN-END blocks. A block can contain multiple transactions, and a transaction can span multiple blocks. The optionalCOMMENT clause lets you specify a comment to be associated with a distributed transaction. If a network or machine fails during the commit, thestateof the distributed transaction might be unknown or in doubt. In that case, Oracle stores the text specified by COMMENT in the data dictionary along with thetransaction ID. Thetext must be a quoted literal up to 50 characters long: COMMIT COMMENT 'In-doubt order transaction; notify Order Entry'; PL/SQL does not support the FORCE clause of SQL, which manually commits an in-doubt distributed transaction. The following example inserts information about an employee into three different database tables. If an INSERT statement tries to storea duplicate employeenumber, the predefined exception DUP_VAL_ON_INDEX is raised. To make sure that changes to all three tables are undone, the exception handler executes a ROLLBACK. DECLARE emp_id INTEGER; BEGIN SELECT empno, ... INTO emp_id, ... FROM new_emp WHERE ... INSERT INTO emp VALUES (emp_id, ...); INSERT INTO tax VALUES (emp_id, ...); INSERT INTO pay VALUES (emp_id, ...); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN ROLLBACK; END; Statement-Level Rollbacks Before executing a SQL statement, Oracle marks an implicit savepoint. Then, if the statement fails, Oracle rolls it back automatically. For example, if an INSERT statement raises an exception by trying to insert a duplicate value in a unique index, the statement is rolled back. Only work started by the failed SQL statement is lost. Work done before that statement in the current transaction is kept.
  • 53. Page 53 of 77 Oracle can also roll back single SQL statements to break deadlocks. Oracle signals an error to one of the participatingtransactions and rolls back the current statement in that transaction. Before executing a SQL statement, Oracle must parseit, that is, examine it to make sure it follows syntaxrules and refers to valid schema objects. Errors detected while executing a SQL statement cause a rollback, but errors detected while parsing the statement do not. The following example marks a savepoint before doing an insert. If theINSERT statement tries to storea duplicate value in the empno column, the predefined exception DUP_VAL_ON_INDEX is raised. In that case, you roll back to the savepoint, undoing just the insert. DECLARE emp_id emp.empno%TYPE; BEGIN UPDATE emp SET ... WHERE empno = emp_id; DELETE FROM emp WHERE ... SAVEPOINT do_insert; INSERT INTO emp VALUES (emp_id, ...); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN ROLLBACK TO do_insert; END; When you roll back to a savepoint, any savepoints marked after that savepoint are erased. The savepoint to which you roll back is not erased. A simple rollback or commit erases all savepoints. If you mark a savepoint within a recursive subprogram, new instances of the SAVEPOINT statement are executed at each level in therecursive descent, but you can only roll back to the most recently marked savepoint. Savepoint names are undeclared identifiers. Reusing a savepoint name within a transaction moves the savepoint from its old position to the current point in thetransaction. Thus, a rollback to thesavepoint affects only thecurrent part of your transaction: BEGIN SAVEPOINT my_point; UPDATE emp SET ... WHERE empno = emp_id; SAVEPOINT my_point; -- move my_point to current point INSERT INTO emp VALUES (emp_id, ...); EXCEPTION WHEN OTHERS THEN ROLLBACK TO my_point; END; The number of active savepoints for each session is unlimited. Committing a Transaction A transaction is made permanent by issuing the SQL command COMMIT. Thegeneral syntaxfor the COMMITcommand is: COMMIT; For example, INSERT INTO CUSTOMERS(ID,NAME,AGE,ADDRESS,SALARY) VALUES (1, 'Ramesh', 32, 'Ahmedabad', 2000.00 ); INSERT INTO CUSTOMERS(ID,NAME,AGE,ADDRESS,SALARY) VALUES (6, 'Komal', 22, 'MP', 4500.00 ); COMMIT; Rolling Back Transactions Changes made to thedatabase without COMMIT could be undone using the ROLLBACK command. The general syntaxfor the ROLLBACK command is: ROLLBACK [TO SAVEPOINT < savepoint_name>]; When a transaction is aborted due to some unprecedented situation, like systemfailure, the entire transaction since a commit is automatically rolled back. if you are not using savepoint then simply use the following statement to rollback all the changes: ROLLBACK; Savepoints Savepoints are sort of markers that help in splittinga long transaction into smaller units by setting some checkpoints. By setting savepoints within a long transaction, you can roll back to a checkpoint if required. This is done by issuing the SAVEPOINT command. The general syntaxfor the SAVEPOINT command is: SAVEPOINT < savepoint_name >; For example: INSERT INTO CUSTOMERS(ID,NAME,AGE,ADDRESS,SALARY) VALUES (7, 'Rajnish', 27, 'HP', 9500.00 ); SAVEPOINT sav1; UPDATECUSTOMERS SET SALARY = SALARY + 1000; ROLLBACK TO sav1; UPDATECUSTOMERS SET SALARY = SALARY + 1000 WHERE ID = 8; COMMIT; Here ROLLBACK TO sav1; statement rollback the changes upto thepoint where you had marked savepoint sav1 and after that new changes will start. How Oracle Does Implicit Rollbacks Before executing an INSERT, UPDATE, or DELETE statement, Oracle marks an implicit savepoint (unavailable to you). If thestatement fails, Oracle rolls back to the savepoint. Normally, just the failed SQL statement is rolled back, not the whole transaction. If the statement raises an unhandled exception, the host environment determines what is rolled back. If you exit a stored subprogram with an unhandled exception, PL/SQL does not assign values to OUT parameters, and does not do any rollback. Ending Transactions You should explicitly commit or roll back every transaction. Whether you issue the commit or rollback in your PL/SQL program or from a client program depends on the application logic. If you do not commit or roll back a transaction explicitly, the client environment determines its final state. For example, in theSQL*Plus environment, if your PL/SQL block does not include a COMMIT or ROLLBACK statement, thefinal stateof your transaction depends on what you do after running the block. If you execute a data definition, data control, or COMMIT statement
  • 54. Page 54 of 77 or if you issue the EXIT, DISCONNECT, or QUIT command, Oracle commits the transaction. If you execute a ROLLBACK statement or abort the SQL*Plus session, Oracle rolls back the transaction. Oracle precompiler programs roll back thetransaction unless the program explicitly commits or rolls back work, and disconnects using the RELEASE parameter: EXEC SQL COMMIT WORK RELEASE; Setting Transaction Properties with SET TRANSACTION You use theSET TRANSACTION statement to begin a read-only or read-write transaction, establish an isolation level, or assign your current transaction to a specified rollback segment. Read-only transactions are useful for running multiple queries while other users updatethe same tables. During a read-only transaction, all queries refer to thesame snapshot of the database, providing a multi-table, multi-query, read-consistent view. Other users can continue to query or updatedata as usual. A commit or rollback ends the transaction. In theexample below a storemanager uses a read-only transaction to gather sales figures for the day, the past week, and the past month. Thefigures are unaffected by other users updating the database during thetransaction. DECLARE daily_sales REAL; weekly_sales REAL; monthly_sales REAL; BEGIN COMMIT; -- ends previous transaction SET TRANSACTION READ ONLY NAME 'Calculate sales figures'; SELECT SUM(amt) INTO daily_sales FROM sales WHERE dte = SYSDATE; SELECT SUM(amt) INTO weekly_sales FROM sales WHERE dte > SYSDATE - 7; SELECT SUM(amt) INTO monthly_sales FROM sales WHERE dte > SYSDATE - 30; COMMIT; -- ends read-only transaction END; The SET TRANSACTION statement must be the first SQL statement in a read-only transaction and can only appear once in a transaction. If you set a transaction to READ ONLY, subsequent queries see only changes committed before the transaction began. The use of READ ONLY does not affect other users or transactions. Restrictions on SET TRANSACTION Only the SELECT INTO, OPEN, FETCH, CLOSE, LOCK TABLE, COMMIT, and ROLLBACK statements are allowed in a read-only transaction. Queries cannot be FOR UPDATE. Overriding Default Locking By default, Oracle locks data structures for you automatically, which is a major strength of the Oracle database: different applications can read and write to the same data without harming each other's data or coordinating with each other. You can request data locks on specific rows or entire tables if you need to override default locking. Explicit locking lets you deny access to data for the duration of a transaction.:  With the LOCK TABLE statement, you can explicitly lock entire tables.  With the SELECT FOR UPDATE statement, you can explicitly lock specific rows of a table to make sure they do not change after you have read them. That way, you can check which or how many rows will be affected by an UPDATEor DELETE statement before issuing the statement, and no other application can change the rows in the meantime. Using FOR UPDATE When you declare a cursor that will be referenced in the CURRENT OF clause of an UPDATE or DELETE statement, you must use the FOR UPDATE clause to acquire exclusive row locks. An example follows: DECLARE CURSOR c1 IS SELECT empno, sal FROM emp WHERE job = 'SALESMAN' AND comm > sal FOR UPDATE NOWAIT; The SELECT ... FOR UPDATE statement identifies therows that will be updated or deleted, then locks each row in the result set. This is useful when you want to base an updateon the existing values in a row. In that case, you must make sure the row is not changed by another user before theupdate. The optionalkeyword NOWAIT tells Oracle not to wait if requested rows have been locked by another user. Control is immediately returned to your program so that it can do other work before trying again to acquire thelock. If you omit the keyword NOWAIT, Oracle waits until the rows are available. All rows are locked when you open thecursor, not as they are fetched. The rows are unlocked when you commit or roll back the transaction. Since the rows are no longer locked, you cannot fetch from a FOR UPDATE cursor after a commit. When querying multiple tables, you can use the FOR UPDATE clause to confine row locking to particular tables. Rows in a table are locked only if the FOR UPDATE OF clause refers to a column in that table. For example, the following query locks rows in the emp table but not in the dept table: DECLARE CURSOR c1 IS SELECT ename, dname FROM emp, dept WHERE emp.deptno = dept.deptno AND job = 'MANAGER' FOR UPDATE OF sal; As the next example shows, you use the CURRENT OF clause in an UPDATE or DELETE statement to refer to the latest row fetched from a cursor: DECLARE CURSOR c1 IS SELECT empno, job, sal FROM emp FOR UPDATE; BEGIN OPEN c1; LOOP FETCH c1 INTO ... UPDATE emp SET sal = new_sal WHERE CURRENT OF c1; END LOOP; Using LOCK TABLE You use theLOCK TABLE statement to lock entire database tables in a specified lock mode so that you can share or deny access to them.. Row share locks allow concurrent access to a table; they prevent other users from locking the entire table for exclusive use. Table locks are released when your transaction issues a commit or rollback. LOCK TABLE emp IN ROW SHARE MODE NOWAIT; The lock mode determines what other locks can be placed on the table. For example, many users can acquire row share locks on a table at the same time, but only one user at a time can acquire an exclusive lock. While one user has an exclusive lock on a table, no other
  • 55. Page 55 of 77 users can insert, delete, or updaterows in that table. A table lock never keeps other users from querying a table, and a query never acquires a table lock. Only if two different transactions try to modify thesame row will one transaction wait for the other to complete. Fetching Across Commits PL/SQL raises an exception if you try to fetch from a FOR UPDATE cursor after doing a commit. TheFOR UPDATE clause locks therows when you open the cursor, and unlocks them when you commit. DECLARE CURSOR c1 IS SELECT ename FROM emp FOR UPDATE OF sal; BEGIN FOR emp_rec IN c1 LOOP -- FETCH fails on the second iteration INSERT INTO temp VALUES ('still going'); COMMIT; -- releases locks END LOOP; END; If you want to fetch across commits, use the ROWID pseudocolumn to mimic the CURRENT OF clause. Select the rowid of each row into a UROWID variable, then use the rowid to identify the current row during subsequent updates and deletes: DECLARE CURSOR c1 IS SELECT ename, job, rowid FROM emp; my_ename emp.ename%TYPE; my_job emp.job%TYPE; my_rowid UROWID; BEGIN OPEN c1; LOOP FETCH c1 INTO my_ename, my_job, my_rowid; EXIT WHEN c1%NOTFOUND; UPDATE emp SET sal = sal * 1.05 WHERE rowid = my_rowid; -- this mimics WHERE CURRENT OF c1 COMMIT; END LOOP; CLOSE c1; END; Because the fetched rows are not locked by a FOR UPDATE clause, other users might unintentionally overwrite your changes. The extra space needed for read consistency is not released until the cursor is closed, which can slow down processing for large updates. The next example shows that you can use the %ROWTYPE attributewith cursors that reference theROWID pseudocolumn: DECLARE CURSOR c1 IS SELECT ename, sal, rowid FROM emp; emp_rec c1%ROWTYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO emp_rec; EXIT WHEN c1%NOTFOUND; IF ... THEN DELETE FROM emp WHERE rowid = emp_rec.rowid; END IF; END LOOP; CLOSE c1; END; 8 - Using PL/SQL Subprograms This chapter shows you how to turn sets of statements into reusable subprograms. Subprograms are like building blocks for modular, maintainable applications. What Are Subprograms? Subprograms are named PL/SQL blocks that can be called with a set of parameters. PL/SQL has two types of subprograms, procedures and functions. Generally, you use a procedure to perform an action and a function to computea value. Like anonymous blocks, subprograms have:  A declarative part, with declarations of types, cursors, constants, variables, exceptions, and nested subprograms. These items are local and cease to exist when the subprogram ends.  An executable part, with statements that assign values, control execution, and manipulate Oracle data.  An optionalexception-handling part, which deals with runtime error conditions. Example 8-1 SimplePL/SQL Procedure The following example shows a string-manipulation procedure that accepts both input and output parameters, and handles potentialerrors: CREATE OR REPLACE PROCEDURE double ( original IN VARCHAR2, new_string OUT VARCHAR2 ) AS BEGIN new_string := original || original; EXCEPTION WHEN VALUE_ERROR THEN dbms_output.put_line('Output buffer not long enough.'); END; / Example 8-2 SimplePL/SQL Function The following example shows a numeric function that declares a local variable to hold temporary results, and returns a value when finished: CREATE OR REPLACE FUNCTION square(original NUMBER) RETURN NUMBER AS original_squared NUMBER; BEGIN original_squared := original * original; RETURN original_squared; END; / Advantages of PL/SQL Subprograms Subprograms let you extend the PL/SQL language. Procedures act like new statements. Functions act like new expressions and operators. Subprograms let you break a program down into manageable, well-defined modules. You can use top-down design and the stepwiserefinement approach to problem solving. Subprograms promotereusability. Once tested, a subprogram can be reused in any number of applications. You can call PL/SQL subprograms from many different environments, so that you do not have to reinvent thewheel each time you use a new language or API to access thedatabase.
  • 56. Page 56 of 77 Subprograms promotemaintainability. You can change the internals of a subprogram without changing other subprograms that call it. Subprograms play a big part in other maintainability features, such as packages and object types. Dummy subprograms (stubs) let you defer thedefinition of procedures and functions until after testing the main program. You can design applications from the top down, thinking abstractly, without worryingabout implementation details. When you use PL/SQL subprograms to define an API, you can make your code even more reusable and maintainable by grouping thesubprograms into a PL/SQL package. Understanding PL/SQL Procedures A procedure is a subprogram that performs a specific action. You write procedures using the SQL CREATE PROCEDURE statement. You specify the name of theprocedure, its parameters, its local variables, and the BEGIN-END block that contains its code and handles any exceptions. For each parameter, you specify:  Its name.  Its parameter mode (IN, OUT, or IN OUT). If you omit the mode, the default is IN. The optionalNOCOPY keyword speeds up processing of large OUT or IN OUT parameters.  Its datatype. You specify only the type, not any length or precision constraints.  Optionally, its default value. You can specify whether theprocedure executes using theschema and permissions of the user who defined it, or the user who calls it. You can specify whether it should be part of the current transaction, or execute in its own transaction where it can COMMIT or ROLLBACK without ending thetransaction of the caller. Procedures created this way are stored in thedatabase. You can execute the CREATE PROCEDURE statement interactively from SQL*Plus, or from a program using native dynamic . A procedure has two parts:the specification (spec for short) and the body. The procedure specbegins with the keyword PROCEDURE and ends with the procedure name or a parameter list. Parameter declarations are optional. Procedures that take no parameters are written without parentheses. The procedure body begins with the keyword IS (or AS) and ends with the keyword END followed by an optionalprocedure name. The procedure body has three parts:a declarative part, an executable part, and an optionalexception-handling part. The declarative part contains local declarations. The keyword DECLARE is used for anonymous PL/SQL blocks, but not procedures. The executable part contains statements, which are placed between the keywords BEGIN and EXCEPTION (or END). At least one statement must appear in theexecutable part of a procedure. You can use the NULL statement to define a placeholder procedure or specify that theprocedure does nothing. The exception-handling part contains exception handlers, which are placed between the keywords EXCEPTION and END. A procedure is called as a PL/SQL statement. For example, you might call the procedure raise_salary as follows: raise_salary(emp_id, amount); Understanding PL/SQL Functions A function is a subprogram that computes a value. Functions and procedures are structured alike, except that functions have a RETURN clause. Functions have a number of optionalkeywords, used to declare a special class of functions known as table functions. They are typically used for transforming large amounts of data in data warehousing applications. The CREATE clause lets you create standalone functions, which are stored in an Oracle database. You can execute the CREATE FUNCTION statement interactively from SQL*Plus or from a program using native dynamic SQL. The AUTHID clause determines whether a stored function executes with the privileges of its owner (the default) or current user and whether its unqualified references to schema objects are resolved in theschema of the owner or current user. You can override the default behavior by specifying CURRENT_USER. Like a procedure, a function has two parts:thespecand thebody. The function spec begins with thekeyword FUNCTION and ends with the RETURN clause, which specifies the datatypeof thereturn value. Parameter declarations are optional. Functions that take no parameters are written without parentheses. The function body begins with the keyword IS (or AS) and ends with the keyword END followed by an optionalfunction name. The function body has three parts:a declarative part, an executable part, and an optionalexception-handling part. The declarative part contains local declarations, which are placed between thekeywords IS and BEGIN. The keyword DECLARE is not used. The executable part contains statements, which are placed between the keywords BEGIN and EXCEPTION (or END). One or more RETURN statements must appear in theexecutable part of a function. Theexception- handling part contains exception handlers, which are placed between the keywords EXCEPTION and END. A function is called as part of an expression: IF sal_ok(new_sal, new_title) THEN ... Using the RETURN Statement The RETURN statement immediately ends the execution of a subprogram and returns control to the caller. Execution continues with thestatement following the subprogram call. (Do not confuse the RETURN statement with the RETURN clause in a function spec, which specifies the datatypeof thereturn value.) A subprogram can contain several RETURN statements. The subprogram does not have to conclude with a RETURN statement. Executing any RETURN statement completes the subprogram immediately. In procedures, a RETURN statement does not return a value and so cannot contain an expression. The statement returns control to thecaller before theend of theprocedure. In functions, a RETURN statement must contain an expression, which is evaluated when the RETURN statement is executed. The resulting value is assigned to the function identifier, which acts like a variable of the typespecified in the RETURN clause. Observe how the function balance returns the balance of a specified bank account: FUNCTION balance (acct_id INTEGER) RETURN REAL IS acct_bal REAL; BEGIN SELECT bal INTO acct_bal FROM accts WHERE acct_no = acct_id; RETURN acct_bal; END balance; /
  • 57. Page 57 of 77 The following example shows that the expression in a function RETURN statement can be arbitrarily complex: FUNCTION compound ( years NUMBER, amount NUMBER, rate NUMBER) RETURN NUMBER IS BEGIN RETURN amount * POWER((rate / 100) + 1, years); END compound; / In a function, there must be at least one execution path that leads to a RETURN statement. Otherwise, you get a function returned without value error at run time. Declaring NestedPL/SQL Subprograms You can declare subprograms in any PL/SQL block, subprogram, or package. The subprograms must go at the end of the declarative section, after all other items. You must declare a subprogram before calling it. This requirement can make it difficult to declare several nested subprograms that call each other. You can declare interrelated nested subprograms using a forward declaration: a subprogram spec terminated by a semicolon, with no body. Although the formal parameter list appears in theforward declaration, it must also appear in the subprogram body. You can place the subprogram body anywhere after theforward declaration, but they must appear in the same program unit. Example 8-3 Forward Declaration for a Nested Subprogram DECLARE PROCEDURE proc1(arg_list); -- forward declaration PROCEDURE proc2(arg_list); -- calls proc1 PROCEDURE proc1(arg_list) IS BEGIN proc2; END; -- calls proc2 BEGIN NULL; END; / Passing Parameters to PL/SQL Subprograms Actual Versus Formal Subprogram Parameters Subprograms pass information using parameters:  The variables declared in a subprogram specand referenced in the subprogram body are formal parameters.  The variables or expressions passed from the calling subprogram are actual parameters. A good programming practice is to use different names for actual and formal parameters. When you call a procedure, the actual parameters are evaluated and the results are assigned to thecorresponding formal parameters. If necessary, before assigning the value of an actual parameter to a formal parameter, PL/SQL converts the datatypeof thevalue. For example, if you pass a number when the procedure expects a string, PL/SQL converts the parameter so that the procedure receives a string. The actual parameter and its corresponding formal parameter must have compatible datatypes. For instance, PL/SQL cannot convert between the DATE and REAL datatypes, or convert a string to a number if the string contains extra characters such as dollar signs. Example 8-4 Formal Parameters and Actual Parameters The following procedure declares two formal parameters named emp_id and amount: PROCEDURE raise_salary (emp_id INTEGER, amount REAL) IS BEGIN UPDATE emp SET sal = sal + amount WHERE empno = emp_id; END raise_salary; / This procedure call specifies theactual parameters emp_num and amount: raise_salary(emp_num, amount); Expressions can be used as actual parameters: raise_salary(emp_num, merit + cola); Using Positional, Named, or Mixed Notation for Subprogram Parameters When calling a subprogram, you can writethe actual parameters using either:  Positional notation. You specify thesame parameters in the same order as they are declared in theprocedure. This notation is compact, but if you specify theparameters (especially literals) in the wrong order, thebug can be hard to detect. You must change your code if the procedure's parameter list changes.  Named notation. You specify thename of each parameter along with its value. An arrow (=>) serves as the association operator. The order of the parameters is not significant. This notation is more verbose, but makes your code easier to read and maintain. You can sometimes avoid changing your code if the procedure's parameter list changes, for example if theparameters are reordered or a new optional parameter is added. Named notation is a good practice to use for any code that calls someone else's API, or defines an API for someone else to use.  Mixed notation. You specify the first parameters with positionalnotation, then switch to named notation for the last parameters. You can use this notation to call procedures that have some required parameters, followed by some optionalparameters. Example 8-5 Subprogram CallsUsing Positional, Named, and Mixed Notation DECLARE acct INTEGER := 12345; amt REAL := 500.00; PROCEDURE credit_acct (acct_no INTEGER, amount REAL) IS BEGIN NULL; END; BEGIN -- The following calls are all equivalent. credit_acct(acct, amt); -- positional credit_acct(amount => amt, acct_no => acct); -- named credit_acct(acct_no => acct, amount => amt); -- named credit_acct(acct, amount => amt); -- mixed END; / Specifying Subprogram Parameter Modes You use parameter modes to define the behavior of formal parameters. Thethree parameter modes are IN (the default), OUT, and IN OUT. Any parameter mode can be used with any subprogram. Avoid using the OUT and IN OUT modes with functions. To have a function return multiple values is a poor programming practice. Also, functions should be free from side effects, which change the values of variables not local to the subprogram. Using the IN Mode
  • 58. Page 58 of 77 An IN parameter lets you pass values to the subprogram being called. Inside the subprogram, an IN parameter acts like a constant. It cannot be assigned a value. You can pass a constant, literal, initialized variable, or expression as an IN parameter. IN parameters can be initialized to default values, which are used if those parameters are omitted from the subprogram call. Using the OUT Mode An OUT parameter returns a value to the caller of a subprogram. Inside the subprogram, an OUT parameter acts like a variable. You can change its value, and reference thevalue after assigning it: PROCEDURE split_name ( phrase IN VARCHAR2, first OUT VARCHAR2, last OUT VARCHAR2 ) IS first := SUBSTR(phrase, 1, INSTR(phrase, ' ')-1); last := SUBSTR(phrase, INSTR(phrase, ' ')+1); IF first = 'John' THEN DBMS_OUTPUT.PUT_LINE('That is a common first name.'); END IF; END; / You must pass a variable, not a constant or an expression, to an OUT parameter. Its previous value is lost unless you specify the NOCOPY or the subprogram exits with an unhandled exception. Like variables, OUT formal parameters are initialized to NULL. The datatypeof an OUT formal parameter cannot be a subtypedefined as NOT NULL, such as the built-in subtypes NATURALN and POSITIVEN. Otherwise, when you call thesubprogram, PL/SQL raises VALUE_ERROR. Before exiting a subprogram, assign values to all OUT formal parameters. Otherwise, the corresponding actual parameters will be null. If you exit successfully, PL/SQL assigns values to the actual parameters. If you exit with an unhandled exception, PL/SQL does not assign values to the actual parameters. Using the IN OUT Mode An IN OUT parameter passes initial values to a subprogram and returns updated values to the caller. It can be assigned a value and its value can be read. Typically, an IN OUT parameter is a string buffer or numeric accumulator, that is read inside the subprogram and then updated. The actual parameter that corresponds to an IN OUT formal parameter must be a variable; it cannot be a constant or an expression. If you exit a subprogram successfully, PL/SQL assigns values to the actual parameters. If you exit with an unhandled exception, PL/SQL does not assign values to theactual parameters. Summary of Subprogram Parameter Modes Table 8-1 summarizes all you need to know about the parameter modes. Table 8-1 Parameter Modes IN OUT IN OUT The default Must bespecified Must bespecified Passes values to a subprogram Returns values to the caller Passes initial values to a subprogram and returns updated values to the caller Formal parameter acts like a constant Formal parameter acts like an uninitialized variable Formal parameter acts like an initialized variable Formal parameter cannot be assigneda value Formal parameter must be assigned a value Formal parameter should be assigned a value Actual parameter can be a constant, initialized variable, literal, or expression Actual parameter must be a variable Actual parameter must be a variable Actual parameter is passed by reference (a pointerto the value is passed in) Actual parameter is passed by value (a copy of the value is passed out) unless NOCOPY is specified Actual parameter is passed by value (a copy of thevalue is passed in and out) unless NOCOPY is specified Using Default Values for Subprogram Parameters By initializing IN parameters to default values, you can pass different numbers of actual parameters to a subprogram, accepting thedefault values for any parameters you omit. You can also add new formal parameters without having to change every call to the subprogram. Example 8-6 Procedure with DefaultParameter Values PROCEDURE create_dept ( new_dname VARCHAR2 DEFAULT 'TEMP', new_loc VARCHAR2 DEFAULT 'TEMP') IS BEGIN NULL; END; / If a parameter is omitted, the default value of its corresponding formal parameter is used. Consider the following calls to create_dept: create_dept; -- Same as create_dept('TEMP','TEMP'); create_dept('SALES'); -- Same as create_dept('SALES','TEMP'); create_dept('SALES', 'NY'); You cannot skip a formal parameter by leaving out its actual parameter. To omit the first parameter and specify the second, use named notation: create_dept(new_loc => 'NEW YORK'); You cannot assign a null to an uninitialized formal parameter by leaving out its actual parameter. You must pass thenull explicitly, or you can specify a default value of NULL in the declaration. Overloading Subprogram Names PL/SQL lets you overload subprogram names and typemethods. You can use the same name for several different subprograms as long as their formal parameters differ in number, order, or datatypefamily. Supposeyou want to initialize the first n rows in two index-by tables that were declared as follows: DECLARE TYPE DateTabTyp IS TABLE OF DATE INDEX BY BINARY_INTEGER; TYPE RealTabTyp IS TABLE OF REAL INDEX BY BINARY_INTEGER; hiredate_tab DateTabTyp; sal_tab RealTabTyp;
  • 59. Page 59 of 77 BEGIN NULL; END; / You might writea procedure to initialize one kind of collection: PROCEDURE initialize (tab OUT DateTabTyp, n INTEGER) IS BEGIN FOR i IN 1..n LOOP tab(i) := SYSDATE; END LOOP; END initialize; / You might also write a procedure to initialize another kind of collection: PROCEDURE initialize (tab OUT RealTabTyp, n INTEGER) IS BEGIN FOR i IN 1..n LOOP tab(i) := 0.0; END LOOP; END initialize; / Because the processing in these two procedures is thesame, it is logical to give them the same name. You can place thetwo overloaded initialize procedures in the same block, subprogram, package, or object type. PL/SQLdetermines which procedure to call by checking their formal parameters. In thefollowing example, theversion of initialize that PL/SQLuses depends on whether you call the procedure with a DateTabTyp or RealTabTyp parameter: DECLARE TYPE DateTabTyp IS TABLE OF DATE INDEX BY BINARY_INTEGER; TYPE RealTabTyp IS TABLE OF REAL INDEX BY BINARY_INTEGER; hiredate_tab DateTabTyp; comm_tab RealTabTyp; indx BINARY_INTEGER; PROCEDURE initialize (tab OUT DateTabTyp, n INTEGER) IS BEGIN NULL; END; PROCEDURE initialize (tab OUT RealTabTyp, n INTEGER) IS BEGIN NULL; END; BEGIN indx := 50; initialize(hiredate_tab, indx); -- calls first version initialize(comm_tab, indx); -- calls second version END; / Using Invoker's Rights Versus Definer's Rights (AUTHID Clause) By default, stored procedures and SQL methods execute with the privileges of their owner, not their current user. Such definer's rights subprograms are bound to theschema in which they reside, allowing you to refer to objects in the same schema without qualifying their names. For example, if schemas SCOTT and BLAKE both have a table called dept, a procedure owned by SCOTT can refer to dept rather than SCOTT.DEPT. If user BLAKE calls SCOTT's procedure, the procedure still accesses the dept table owned by SCOTT. If you compile thesame procedure in both schemas, you can define the schema name as a variable in SQL*Plus and refer to the table like &schema..dept. Thecode is portable, but if you change it, you must recompile it in each schema. A more maintainable way is to use the AUTHID clause, which makes stored procedures and SQL methods execute with the privileges and schema context of the calling user. You can create one instance of the procedure, and many users can call it to access their own data. Such invoker's rights subprograms are not bound to a particular schema. The following version of procedure create_dept executes with the privileges of the calling user and inserts rows into that user's dept table: CREATE PROCEDURE create_dept ( my_deptno NUMBER, my_dname VARCHAR2, my_loc VARCHAR2) AUTHID CURRENT_USER AS BEGIN INSERT INTO dept VALUES (my_deptno, my_dname, my_loc); END; / Advantages of Invoker's Rights Invoker's rights subprograms let you reuse code and centralize application logic. They are especially useful in applications that storedata using identical tables in different schemas. All the schemas in one instance can call procedures owned by a central schema. You can even have schemas in different instances call centralized procedures using a database link. Consider a company that uses a stored procedure to analyze sales. If the company has several schemas, each with a similar SALES table, normally it would also need several copies of thestored procedure, one in each schema. To solve theproblem, the company installs an invoker's rights version of thestored procedure in a central schema. Now, all theother schemas can call thesame procedure, which queries the appropriateto SALES table in each case. You can restrict access to sensitive data by calling from an invoker's rights subprogram to a definer's rights subprogram that queries or updates thetable containing the sensitive data. Although multiple users can call the invoker's rights subprogram, they do not have direct access to the sensitive data. Specifying the Privileges for a Subprogram with the AUTHID Clause To implement invoker's rights, use the AUTHID clause, which specifies whether a subprogram executes with the privileges of its owner or its current user. It also specifies whether external references (that is, references to objects outsidethe subprogram) are resolved in the schema of theowner or the current user. The AUTHID clause is allowed only in the header of a standalone subprogram, a package spec, or an object typespec. In the CREATE FUNCTION, CREATE PROCEDURE, CREATE PACKAGE, or CREATE TYPE statement, you can include either AUTHID CURRENT_USER or AUTHID DEFINER immediately before the IS or AS keyword that begins the declaration section. DEFINER is the default option. In a package or object type, the AUTHID clause applies to all subprograms. Note: Most supplied PL/SQL packages (such as DBMS_LOB, DBMS_PIPE, DBMS_ROWID, DBMS_SQL, and UTL_REF) are invoker's rights packages. Who Is the Current User During Subprogram Execution? In a sequence of calls, whenever control is inside an invoker's rights subprogram, the current user is the session user. When a definer's rights subprogram is called, theowner of
  • 60. Page 60 of 77 that subprogram becomes the current user. The current user might change as new subprograms are called or as subprograms exit. To verify who thecurrent user is at any time, you can check the USER_USERS data dictionary view. Inside an invoker's rights subprogram, the value from this view might be different from thevalue of the USER built-in function, which always returns the name of the session user. How External References Are Resolved in Invoker's Rights Subprograms If you specify AUTHID CURRENT_USER, theprivileges of thecurrent user are checked at run time, and external references are resolved in the schema of the current user. However, this applies only to external references in:  SELECT, INSERT, UPDATE, and DELETE data manipulation statements  The LOCK TABLE transaction control statement  OPEN and OPEN-FOR cursor control statements  EXECUTE IMMEDIATE and OPEN-FOR-USING dynamic SQL statements  SQL statements parsed using DBMS_SQL.PARSE() For all other statements, the privileges of the owner are checked at compile time, and external references are resolved in theschema of the owner. For example, the assignment statement below refers to the packaged function balance. This external reference is resolved in the schema of the owner of procedure reconcile. CREATE PROCEDURE reconcile (acc_id IN INTEGER) AUTHID CURRENT_USER AS bal NUMBER; BEGIN bal := bank_ops.balance(acct_id); ... END; / Overriding Default Name Resolution in Invoker's Rights Subprograms Occasionally, you might want an unqualified name to refer to some particular schema, not the schema of thecaller. In the same schema as the invoker's rights subprogram, create a public synonymfor the table, procedure, function, or other object using the CREATE SYNONYM statement: CREATE PUBLIC SYNONYM emp FOR hr.employees; When theinvoker's rights subprogram refers to this name, it will match the synonymin its own schema, which resolves to theobject in the specified schema. This technique does not work if the calling schema already has a schema object or private synonymwith thesame name. In that case, theinvoker's rights subprogram must fully qualify thereference. Granting Privileges on Invoker's Rights Subprograms To call a subprogram directly, users must have the EXECUTE privilege on that subprogram. By granting the privilege, you allow a user to:  Call the subprogram directly  Compile functions and procedures that call the subprogram For external references resolved in thecurrent user's schema (such as those in DML statements), thecurrent user must have the privileges needed to access schema objects referenced by thesubprogram. For all other external references (such as function calls), the owner's privileges are checked at compile time, and no run-time check is done. A definer's rights subprogram operates under thesecurity domain of its owner, no matter who is executing it. The owner must have theprivileges needed to access schema objects referenced by thesubprogram. You can write a program consisting of multiple subprograms, some with definer's rights and others with invoker's rights. Then, you can use the EXECUTE privilege to restrict program entry points. That way, users of an entry-point subprogramcan execute theother subprograms indirectly but not directly. Granting Privileges on an Invoker's Rights Subprogram: Example Supposeuser UTIL grants the EXECUTE privilege on subprogram FFT to user APP: GRANT EXECUTE ON util.fft TO app; Now, user APP can compile functions and procedures that call subprogram FFT. At run time, no privilege checks on thecalls are done. As Figure 8-2 shows, user UTIL need not grant theEXECUTE privilege to every user who might call FFT indirectly. Since subprogram util.fft is called directly only from invoker's rights subprogram app.entry, user util must grant the EXECUTE privilege only to user APP. When UTIL.FFT is executed, its current user could be APP, SCOTT, or BLAKE even though SCOTT and BLAKE were not granted the EXECUTE privilege. Figure 8-2 Indirect Calls to an Invoker's Rights Subprogram Description of the illustration lnpls026.gif Using Roles with Invoker's Rights Subprograms The use of roles in a subprogram depends on whether it executes with definer's rights or invoker's rights. Within a definer's rights subprogram, all roles are disabled. Roles are not used for privilege checking, and you cannot set roles. Within an invoker's rights subprogram, roles are enabled (unless the subprogram was called directly or indirectly by a definer's rights subprogram). Roles are used for privilege checking, and you can use native dynamic SQL to set roles for thesession. However, you cannot use roles to grant privileges on templateobjects because roles apply at run time, not at compile time. Using Views and Database Triggers with Invoker's Rights Subprograms For invoker's rights subprograms executed within a view expression, the schema that created theview, not the schema that is querying the view, is considered to be the current user. This rule also applies to database triggers.
  • 61. Page 61 of 77 Using Database Links with Invoker's Rights Subprograms You can create a database link to use invoker's rights: CREATE DATABASE LINK link_name CONNECT TO CURRENT_USER USING connect_string; A current-user link lets you connect to a remote database as another user, with that user's privileges. To connect, Oracle uses theusername of the current user (who must be a global user). Supposean invoker's rights subprogram owned by user BLAKE references the database link below. If global user SCOTT calls the subprogram, it connects to the Dallas database as user SCOTT, who is thecurrent user. CREATE DATABASE LINK dallas CONNECT TO CURRENT_USER USING ... If it were a definer's rights subprogram, the current user would be BLAKE, and the subprogram would connect to the Dallas database as global user BLAKE. Using Object Types with Invoker's Rights Subprograms To define object types for usein any schema, specify the AUTHID CURRENT_USER clause. Supposeuser BLAKE creates the following object type: CREATE TYPE NumAUTHID CURRENT_USER AS OBJECT ( x NUMBER, STATIC PROCEDURE new_num( n NUMBER, schema_name VARCHAR2, table_name VARCHAR2) ); / CREATE TYPE BODY NumAS STATIC PROCEDURE new_num( n NUMBER, schema_name VARCHAR2, table_name VARCHAR2) IS sql_stmt VARCHAR2(200); BEGIN sql_stmt := 'INSERT INTO ' || schema_name || '.' || table_name || ' VALUES (blake.Num(:1))'; EXECUTE IMMEDIATE sql_stmt USING n; END; END; / Then, user BLAKE grants the EXECUTE privilege on object type Num to user SCOTT: GRANT EXECUTE ON Num TO scott; Finally, user SCOTT creates an object table to store objects of type Num, then calls procedure new_num to populatethetable: CONNECT scott/tiger; CREATE TABLE num_tab OF blake.Num; / BEGIN blake.Num.new_num(1001, 'scott', 'num_tab'); blake.Num.new_num(1002, 'scott', 'num_tab'); blake.Num.new_num(1003, 'scott', 'num_tab'); END; / The calls succeed because theprocedure executes with the privileges of its current user (SCOTT), not its owner (BLAKE). For subtypes in an object typehierarchy, thefollowing rules apply:  If a subtypedoes not explicitly specify an AUTHID clause, it inherits the AUTHID of its supertype.  If a subtypedoes specify an AUTHID clause, its AUTHID must match the AUTHID of its supertype. Also, if the AUTHID is DEFINER, both the supertypeand subtype must have been created in thesame schema. Calling Invoker's Rights Instance Methods An invoker's rights instance method executes with theprivileges of theinvoker, not the creator of the instance. Supposethat Person is an invoker's rights object type, and that user SCOTT creates p1, an object of typePerson. If user BLAKE calls instance method change_job to operateon object p1, the current user of themethod is BLAKE, not SCOTT. Consider the following example: -- user blake creates a definer-rights procedure CREATE PROCEDURE reassign (p Person, new_job VARCHAR2) AS BEGIN -- user blake calls method change_job, so the -- method executes with the privileges ofblake p.change_job(new_job); ... END; / -- user scott passes a Person object to the procedure DECLARE p1 Person; BEGIN p1 := Person(...); blake.reassign(p1, 'CLERK'); ... END; / Using Recursion with PL/SQL Recursion is a powerfultechnique for simplifying the design of algorithms. Basically, recursion means self-reference. In a recursive mathematical sequence, each term is derived by applyinga formula to preceding terms. TheFibonacci sequence (0, 1, 1, 2, 3, 5, 8, 13, 21, ...), is an example. Each term in the sequence (after the second) is the sum of thetwo terms that immediately precede it. In a recursive definition, something is defined as simpler versions of itself. Consider the definition of n factorial (n!), theproduct of all integers from 1 to n: n! = n * (n - 1)! What Is a Recursive Subprogram? A recursive subprogram is one that calls itself. Each recursive call creates a new instance of any items declared in the subprogram, including parameters, variables, cursors, and exceptions. Likewise, new instances of SQL statements are created at each level in the recursive descent. Be careful where you place a recursive call. If you place it inside a cursor FOR loop or between OPEN and CLOSE statements, another cursor is opened at each call, which might exceed the limit set by the Oracle initialization parameter OPEN_CURSORS. There must be at least two paths through a recursive subprogram: one that leads to the recursive call and one that does not. At least one path must lead to a terminating condition. Otherwise, the recursion would go on until PL/SQL runs out of memory and raises the predefined exception STORAGE_ERROR. Calling External Subprograms
  • 62. Page 62 of 77 Although PL/SQL is a powerful, flexible language, some tasks are more easily done in another language. Low-level languages such as C are very fast. Widely used languages such as Java have reusable libraries for common design patterns. You can use PL/SQL call specs to invoke external subprograms written in other languages, making their capabilities and libraries available from PL/SQL. For example, you can call Java stored procedures from any PL/SQL block, subprogram, or package. Supposeyou storethe following Java class in thedatabase: import java.sql.*; import oracle.jdbc.driver.*; public class Adjuster { public static void raiseSalary (int empNo, float percent) throws SQLException { Connection conn = new OracleDriver().defaultConnection(); String sql = "UPDATE emp SET sal = sal * ? WHERE empno = ?"; try { PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setFloat(1, (1 + percent / 100)); pstmt.setInt(2, empNo); pstmt.executeUpdate(); pstmt.close(); } catch (SQLException e) {System.err.println(e.getMessage());} } } The class Adjuster has one method, which raises the salary of an employee by a given percentage. Because raiseSalary is a void method, you publish it as a procedure using this call spec: CREATE PROCEDURE raise_salary (empno NUMBER, pct NUMBER) AS LANGUAGE JAVA NAME 'Adjuster.raiseSalary(int, float)'; You might call procedure raise_salary from an anonymous PL/SQL block: DECLARE emp_id NUMBER; percent NUMBER; BEGIN -- get values for emp_id and percent raise_salary(emp_id, percent); -- call external subprogram END; / External C subprograms are used to interface with embedded systems, solve engineering problems, analyzedata, or control real-time devices and processes. External C subprograms extend the functionality of the database server, and move computation-bound programs from client to server, where they execute faster. 9 - Using PL/SQL Packages This chapter shows how to bundle related PL/SQL code and data into a package. The package might include a set of procedures that forms an API, or a poolof typedefinitions and variable declarations. The package is compiled and stored in thedatabase, where its contents can be shared by many applications. What Is a PL/SQL Package? A package is a schema object that groups logically related PL/SQL types, variables, and subprograms. Packages usually have two parts, a specification and a body;sometimes the body is unnecessary. Thespecification (specfor short) is the interface to the package. It declares the types, variables, constants, exceptions, cursors, and subprograms that can be referenced from outside thepackage. The body defines the queries for thecursors and the code for thesubprograms. The specholds public declarations, which are visible to stored procedures and other code outside thepackage. You must declare subprograms at the end of the specafter all other items (except pragmas that name a specific function; such pragmas must follow the function spec). The body holds implementation details and private declarations, which are hidden from code outsidethe package. Following thedeclarative part of thepackage body is the optionalinitialization part, which holds statements that initialize package variables and do any other one-time setup steps. The AUTHID clause determines whether all thepackaged subprograms execute with the privileges of their definer (the default) or invoker, and whether their unqualified references to schema objects are resolved in the schema of thedefiner or invoker. A call spec lets you map a package subprogram to a Java method or external C function. The call spec maps the Java or C name, parameter types, and return typeto their SQL counterparts. What Goes In a PL/SQL Package?  "Get" and "Set" methods for the package variables, if you want to avoid letting other procedures read and write them directly.  Cursor declarations with the text of SQL queries. Reusing exactly the same query text in multiple locations is faster than retypingthe same query each time with slight differences. It is also easier to maintain if you need to change a query that is used in many places.  Declarations for exceptions. Typically, you need to be able to reference these from different procedures, so that you can handle exceptions within called subprograms.  Declarations for procedures and functions that call each other. You do not need to worry about compilation order for packaged procedures and functions, making them more convenient than standalone stored procedures and functions when they call back and forth to each other.  Declarations for overloaded procedures and functions. You can create multiple variations of a procedure or function, using thesame names but different sets of parameters.  Variables that you want to remain available between procedure calls in thesame session. You can treat variables in a package like global variables.  Typedeclarations for PL/SQL collection types. To pass acollection as a parameter between stored procedures or functions, you must declare thetypein a package so that both the calling and called subprogram can refer to it. Example of a PL/SQL Package The example below packages a record type, acursor, and two employment procedures. The procedure hire_employee uses thesequence empno_seq and the function SYSDATE to insert a new employee number and hire date. CREATE OR REPLACE PACKAGE emp_actions AS -- spec TYPE EmpRecTyp IS RECORD (emp_id INT, salary REAL); CURSOR desc_salary RETURN EmpRecTyp; PROCEDURE hire_employee (
  • 63. Page 63 of 77 ename VARCHAR2, job VARCHAR2, mgr NUMBER, sal NUMBER, comm NUMBER, deptno NUMBER); PROCEDURE fire_employee (emp_id NUMBER); END emp_actions; / CREATE OR REPLACE PACKAGE BODY emp_actions AS -- body CURSOR desc_salary RETURN EmpRecTyp IS SELECT empno, sal FROM emp ORDER BY sal DESC; PROCEDURE hire_employee ( ename VARCHAR2, job VARCHAR2, mgr NUMBER, sal NUMBER, comm NUMBER, deptno NUMBER) IS BEGIN INSERT INTO emp VALUES (empno_seq.NEXTVAL, ename, job, mgr, SYSDATE, sal, comm, deptno); END hire_employee; PROCEDURE fire_employee (emp_id NUMBER) IS BEGIN DELETE FROM emp WHERE empno = emp_id; END fire_employee; END emp_actions; / Only the declarations in the package specare visible and accessible to applications. Implementation details in thepackage body are hidden and inaccessible. You can change the body (implementation) without having to recompile calling programs. Advantages of PL/SQL Packages Packages have a long history in softwareengineering, offering important features for reliable, maintainable, reusable code, often in team development efforts for large systems. Modularity Packages let you encapsulate logically related types, items, and subprograms in a named PL/SQL module. Each package is easy to understand, and the interfaces between packages are simple, clear, and well defined. This aids application development. Easier Application Design When designing an application, all you need initially is the interface information in the package specs. You can code and compile a specwithout its body. Then, stored subprograms that reference thepackage can be compiled as well. You need not define the package bodies fully until you are ready to complete the application. Information Hiding With packages, you can specify which types, items, and subprograms are public (visible and accessible) or private(hidden and inaccessible). For example, if a package contains four subprograms, three might be public and one private. The package hides the implementation of the privatesubprogram so that only thepackage (not your application) is affected if the implementation changes. This simplifies maintenance and enhancement. Also, by hiding implementation details from users, you protect theintegrity of the package. Added Functionality Packaged public variables and cursors persist for theduration of a session. They can be shared by all subprograms that execute in the environment. They let you maintain data across transactions without storing it in the database. Better Performance When you call a packaged subprogram for the first time, the whole package is loaded into memory. Later calls to related subprograms in thepackage require no disk I/O. Packages stop cascading dependencies and avoid unnecessary recompiling. For example, if you change the body of a packaged function, Oracle does not recompile other subprograms that call the function; these subprograms only depend on the parameters and return value that are declared in thespec, so they are only recompiled if the specchanges. Understanding The Package Specification The package specification contains public declarations. The declared items are accessible from anywhere in the package and to any other subprograms in the same schema. Figure 9- 1 illustrates thescoping. Figure 9-1 Package Scope Description of the illustration lnpls014.gif The speclists the package resources available to applications. All theinformation your application needs to use the resources is in the spec. For example, thefollowing declaration shows that the function named fac takes one argument of type INTEGER and returns a value of type INTEGER: FUNCTION fac (n INTEGER) RETURN INTEGER; -- returns n! That is all the information you need to call thefunction. You need not consider its underlying implementation (whether it is iterative or recursive for example). If a spec declares only types, constants, variables, exceptions, and call specs, thepackage body is unnecessary. Only subprograms and cursors have an underlying implementation. In the following example, the package needs no body because it declares types, exceptions, and variables, but no subprograms or cursors. Such packages let you define global variables—usable by stored procedures and functions and triggers—that persist throughout a session. CREATE PACKAGE trans_data AS -- bodiless package TYPE TimeRec IS RECORD (
  • 64. Page 64 of 77 minutes SMALLINT, hours SMALLINT); TYPE TransRec IS RECORD ( category VARCHAR2, account INT, amount REAL, time_of TimeRec); minimum_balance CONSTANT REAL := 10.00; number_processed INT; insufficient_funds EXCEPTION; END trans_data; / Package Body The package body has the codes for various methods declared in the package specification and other privatedeclarations, which are hidden from code outside thepackage. The CREATEPACKAGEBODY Statement is used for creating thepackage body. The following code snippet shows thepackage body declaration for the cust_sal package created above. CREATE OR REPLACE PACKAGE BODY cust_sal AS PROCEDURE find_sal(c_id customers.id%TYPE) IS c_sal customers.salary%TYPE; BEGIN SELECT salary INTO c_sal FROM customers WHERE id = c_id; dbms_output.put_line('Salary: '|| c_sal); END find_sal; END cust_sal; / When theabove code is executed at SQL prompt, it produces following result: Package body created. Using the Package Elements The package elements ( variables, procedures or functions) are accessed with the following syntax: package_name.element_name; Consider, we already have created above package in our database schema, thefollowing program uses the find_sal method of thecust_sal package: DECLARE code customers.id%type := &cc_id; BEGIN cust_sal.find_sal(code); END; / When theabove code is executed at SQL prompt, it prompt to enter customer ID and when you enter an ID, it displays corresponding salary as follows: Enter value for cc_id: 1 Salary: 3000 PL/SQL procedure successfully completed. Example: The following program provides a more complete package. We will use the CUSTOMERS table stored in our database with thefollowing records: Select * from customers; +----+----------+-----+-----------+----------+ | ID | NAME | AGE | ADDRESS | SALARY | +----+----------+-----+-----------+----------+ | 1 | Ramesh | 32 | Ahmedabad | 3000.00 | | 2 | Khilan | 25 | Delhi | 3000.00 | | 3 | kaushik | 23 | Kota | 3000.00 | | 4 | Chaitali | 25 | Mumbai | 7500.00 | | 5 | Hardik | 27 | Bhopal | 9500.00 | | 6 | Komal | 22 | MP | 5500.00 | +----+----------+-----+-----------+----------+ The package specification: CREATE OR REPLACE PACKAGE c_package AS -- Adds a customer PROCEDURE addCustomer(c_id customers.id%type, c_name customers.name%type, c_age customers.age%type, c_addr customers.address%type, c_sal customers.salary%type); -- Removes a customer PROCEDURE delCustomer(c_id customers.id%TYPE); --Lists all customers PROCEDURE listCustomer; END c_package; / When theabove code is executed at SQL prompt, it creates the above package and displays following result: Package created. Creatingthe package body: CREATE OR REPLACE PACKAGE BODY c_package AS PROCEDURE addCustomer(c_id customers.id%type, c_name customers.name%type, c_age customers.age%type, c_addr customers.address%type, c_sal customers.salary%type) IS BEGIN INSERT INTO customers (id,name,age,address,salary) VALUES(c_id, c_name, c_age, c_addr, c_sal); END addCustomer; PROCEDURE delCustomer(c_id customers.id%type) IS BEGIN DELETE FROM customers WHERE id = c_id; END delCustomer; PROCEDURE listCustomer IS CURSOR c_customers is SELECT name FROM customers; TYPE c_list is TABLE OF customers.name%type; name_list c_list := c_list(); counter integer :=0; BEGIN FOR n IN c_customers LOOP counter := counter +1; name_list.extend; name_list(counter) := n.name; dbms_output.put_line('Customer(' ||counter|| ')'||name_list(counter)); END LOOP; END listCustomer;
  • 65. Page 65 of 77 END c_package; / Above example makes use of nestedtable which we will discuss in the next chapter. When theabove code is executed at SQL prompt, it produces following result: Package body created. Usingthe Package: The following program uses themethods declared and defined in the package c_package. DECLARE code customers.id%type:= 8; BEGIN c_package.addcustomer(7, 'Rajnish', 25, 'Chennai', 3500); c_package.addcustomer(8, 'Subham', 32, 'Delhi', 7500); c_package.listcustomer; c_package.delcustomer(code); c_package.listcustomer; END; / When theabove code is executed at SQL prompt, it produces following result: Customer(1): Ramesh Customer(2): Khilan Customer(3): kaushik Customer(4): Chaitali Customer(5): Hardik Customer(6): Komal Customer(7): Rajnish Customer(8): Subham Customer(1): Ramesh Customer(2): Khilan Customer(3): kaushik Customer(4): Chaitali Customer(5): Hardik Customer(6): Komal Customer(7): Rajnish PL/SQL procedure successfully completed Referencing Package Contents To reference the types, items, subprograms, and call specs declared within a package spec, use dot notation: package_name.type_name package_name.item_name package_name.subprogram_name package_name.call_spec_name You can reference package contents from database triggers, stored subprograms, 3GL application programs, and various Oracle tools. For example, you might call the packaged procedure hire_employee from SQL*Plus, as follows: CALL emp_actions.hire_employee('TATE', 'CLERK', ...); The following example calls the same procedure from an anonymous block in a Pro*C program. Theactual parameters emp_name and job_title arehost variables. EXEC SQL EXECUTE BEGIN emp_actions.hire_employee(:emp_name, :job_title, ...); Restrictions You cannot reference remote packaged variables, either directly or indirectly. For example, you cannot call the a procedure through a database link if the procedure refers to a packaged variable. Inside a package, you cannot reference host variables. Understanding The Package Body The package body contains the implementation of every cursor and subprogram declared in the package spec. Subprograms defined in a package body are accessible outside the package only if their specs also appear in thepackage spec. If a subprogram specis not included in thepackage spec, that subprogram can only be called by other subprograms in the same package. To match subprogram specs and bodies, PL/SQL does a token-by-token comparison of their headers. Except for white space, the headers must match word for word. Otherwise, PL/SQL raises an exception, as the following example shows: CREATE PACKAGE emp_actions AS ... PROCEDURE calc_bonus (date_hired emp.hiredate%TYPE, ...); END emp_actions; / CREATE PACKAGE BODY emp_actions AS ... PROCEDURE calc_bonus (date_hired DATE, ...) IS -- parameter declaration raises an exception because 'DATE' -- does not match 'emp.hiredate%TYPE' word for word BEGIN ... END; END emp_actions; / The package body can also contain private declarations, which define types and items necessary for theinternal workings of thepackage. Thescope of thesedeclarations is local to the package body. Therefore, thedeclared types and items are inaccessible except from within thepackage body. Unlike a package spec, the declarative part of a package body can contain subprogram bodies. Following the declarative part of a package body is the optionalinitialization part, which typically holds statements that initialize some of the variables previously declared in the package. The initialization part of a package plays aminor role because, unlike subprograms, a package cannot be called or passed parameters. As a result, the initialization part of a package is run only once, the first time you reference the package. Remember, if a package specdeclares only types, constants, variables, exceptions, and call specs, the package body is unnecessary. However, thebody can still be used to initialize items declared in the package spec. Private Versus Public Items in Packages In the package emp_actions, the package body declares a variable named number_hired, which is initialized to zero. Items declared in thebody are restricted to use within thepackage. PL/SQL code outside the package cannot reference the variable number_hired. Such items are called private. Items declared in thespec of emp_actions, such as the exception invalid_salary, arevisible outside thepackage. Any PL/SQL code can reference the exception invalid_salary. Such items are called public. To maintain items throughout a session or across transactions, place them in the declarative part of the package body. For example, thevalue of number_hired is kept between calls to hire_employee within thesame session. Thevalue is lost when thesession ends.
  • 66. Page 66 of 77 To make the items public, place them in thepackage spec. For example, the constant minimum_balance declared in the specof the package bank_transactions is available for general use. Overloading Packaged Subprograms PL/SQL allows two or more packaged subprograms to have the same name. This option is useful when you want a subprogram to accept similar sets of parameters that have different datatypes. For example, thefollowing package defines two procedures named journalize: CREATE PACKAGE journal_entries AS ... PROCEDURE journalize (amount REAL, trans_date VARCHAR2); PROCEDURE journalize (amount REAL, trans_date INT); END journal_entries; / CREATE PACKAGE BODY journal_entries AS ... PROCEDURE journalize (amount REAL, trans_date VARCHAR2) IS BEGIN INSERT INTO journal VALUES (amount, TO_DATE(trans_date, 'DD-MON-YYYY')); END journalize; PROCEDURE journalize (amount REAL, trans_date INT) IS BEGIN INSERT INTO journal VALUES (amount, TO_DATE(trans_date, 'J')); END journalize; END journal_entries; / The first procedure accepts trans_date as a character string, while the second procedure accepts it as a number (the Julian day). Each procedure handles the data appropriately. Overview of Product-Specific Packages Oracle and various Oracle tools are supplied with product-specificpackages that define APIs you can call from PL/SQL, SQL, Java, or other programming environments. Here we mention a few of themore widely used ones. About the DBMS_ALERT Package Package DBMS_ALERT lets you use database triggers to alert an application when specific database values change. The alerts are transaction based and asynchronous (that is, they operateindependently of any timing mechanism). For example, a company might use this package to updatethevalue of its investment portfolio as new stock and bond quotes arrive. About the DBMS_OUTPUT Package Package DBMS_OUTPUT enables you to display output from PL/SQL blocks and subprograms, which makes it easier to test and debug them. The procedure put_line outputs information to a buffer in the SGA. You display the information by calling the procedure get_line or by setting SERVEROUTPUT ON in SQL*Plus. For example, supposeyou create the following stored procedure: CREATE OR REPLACE PROCEDURE list_tables AS BEGIN dbms_output.put_line('These are the tables you own:'); FOR itemIN (SELECT table_name FROM user_tables) LOOP dbms_output.put_line(item.table_name); END LOOP; END; / When you issue thefollowing commands, SQL*Plus displays theoutput fromthe procedure: SQL> SET SERVEROUTPUT ON SQL> EXEC list_tables; If the output is long, you might need to issue SET SERVEROUTPUT ON SIZE 1000000 to usea bigger output buffer. About the DBMS_PIPE Package Package DBMS_PIPE allows different sessions to communicate over named pipes. (A pipe is an area of memory used by one process to pass information to another.) You can use the procedures pack_message and send_message to pack a message into a pipe, then send it to another session in the same instance or to a waiting application such as a UNIX program. At the other end of thepipe, you can use theprocedures receive_message and unpack_message to receive and unpack (read) the message. Named pipes are useful in many ways. For example, you can write a C program to collect data, then send it through pipes to stored procedures in an Oracle database. About the UTL_FILE Package Package UTL_FILE lets PL/SQL programs read and writeoperating system(OS) text files. It provides a restricted version of standard OS stream file I/O, including open, put, get, and close operations. When you want to read or write a text file, you call the function fopen, which returns a file handle for use in subsequent procedure calls. For example, the procedure put_line writes a text string and line terminator to an open file, and the procedure get_line reads a line of text from an open file into an output buffer. About the UTL_HTTP Package Package UTL_HTTP allows your PL/SQL programs to make hypertext transfer protocol (HTTP) callouts. It can retrieve data from the Internet or call Oracle Web Server cartridges. The package has two entry points, each of which accepts a URL (uniform resource locator) string, contacts thespecified site, and returns the requested data, which is usually in hypertext markup language (HTML) format. 10 - Handling PL/SQL Errors Run-time errors arise from design faults, coding mistakes, hardware failures, and many other sources. Although you cannot anticipate all possibleerrors, you can plan to handle certain kinds of errors meaningful to your PL/SQL program. With many programming languages, unless you disable error checking, a run-time error such as stack overflow or division by zero stops normal processing and returns control to the operating system. With PL/SQL, a mechanism called exception handling lets you "bulletproof" your program so that it can continue operating in the presence of errors. Overview of PL/SQL Runtime Error Handling In PL/SQL, an error condition is called an exception. Exceptions can be internally defined (by the runtime system) or user defined. Examples of internally defined exceptions include division by zero and out of memory. Some common internal exceptions have predefined
  • 67. Page 67 of 77 names, such as ZERO_DIVIDE and STORAGE_ERROR. The other internal exceptions can be given names. When an error occurs, an exception is raised. That is, normal execution stops and control transfers to theexception-handling part of your PL/SQL block or subprogram. Internal exceptions are raised implicitly (automatically) by therun-time system. User-defined exceptions must be raised explicitly by RAISE statements, which can also raise predefined exceptions. To handle raised exceptions, you write separateroutines called exception handlers. After an exception handler runs, thecurrent block stops executing and theenclosing block resumes with thenext statement. If there is no enclosing block, control returns to the host environment. The following example calculates a price-to-earnings ratio for a company. If the company has zero earnings, the division operation raises the predefined exception ZERO_DIVIDE, the execution of theblock is interrupted, and control is transferred to the exception handlers. The optionalOTHERS handler catches all exceptions that the block does not name specifically. SET SERVEROUTPUT ON; DECLARE stock_price NUMBER := 9.73; net_earnings NUMBER := 0; pe_ratio NUMBER; BEGIN -- Calculation might cause division-by-zero error. pe_ratio := stock_price / net_earnings; dbms_output.put_line('Price/earnings ratio = ' || pe_ratio); EXCEPTION -- exception handlers begin -- Only one ofthe WHEN blocks is executed. WHEN ZERO_DIVIDE THEN -- handles 'division by zero' error dbms_output.put_line('Company must have had zero earnings.'); pe_ratio := null; WHEN OTHERS THEN -- handles all other errors dbms_output.put_line('Some other kind oferror occurred.'); pe_ratio := null; END; -- exception handlers and block end here / The last example illustrates exception handling. With some better error checking, we could have avoided theexception entirely, by substitutinga null for theanswer if the denominator was zero: DECLARE stock_price NUMBER := 9.73; net_earnings NUMBER := 0; pe_ratio NUMBER; BEGIN pe_ratio := case net_earnings when 0 then null else stock_price / net_earnings end; END; / Advantages of PL/SQL Exceptions Using exceptions for error handling has several advantages. With exceptions, you can reliably handle potentialerrors from many statements with a single exception handler: BEGIN SELECT ... SELECT ... procedure_that_performs_select(); ... EXCEPTION WHEN NO_DATA_FOUND THEN -- catches all 'no data found' errors Instead of checking for an error at every point it might occur, just add an exception handler to your PL/SQL block. If the exception is ever raised in that block (or any sub-block), you can be sure it will be handled. Sometimes the error is not immediately obvious, and could not be detected until later when you perform calculations using bad data. Again, a single exception handler can trap all division-by-zero errors, bad array subscripts, and so on. If you need to check for errors at a specific spot, you can enclose a single statement or a group of statements inside its own BEGIN-END block with its own exception handler. You can make the checking as general or as precise as you like. Isolating error-handling routines makes therest of theprogram easier to read and understand. Summary of Predefined PL/SQL Exceptions You can use the pragma EXCEPTION_INIT to associate exception names with other Oracle error codes that you can anticipate. To handle unexpected Oracle errors, you can use the OTHERS handler. Within this handler, you can call the functions SQLCODE and SQLERRM to return theOracle error code and message text. Once you know the error code, you can use it with pragma EXCEPTION_INIT and writea handler specifically for that error. PL/SQL declares predefined exceptions globally in package STANDARD. You need not declare them yourself. You can write handlers for predefined exceptions using the names in the following list: Exception Oracle Error SQLCODEValue ACCESS_INTO_NULL ORA-06530 -6530 CASE_NOT_FOUND ORA-06592 -6592 COLLECTION_IS_NULL ORA-06531 -6531 CURSOR_ALREADY_OPEN ORA-06511 -6511 DUP_VAL_ON_INDEX ORA-00001 -1 INVALID_CURSOR ORA-01001 -1001 INVALID_NUMBER ORA-01722 -1722 LOGIN_DENIED ORA-01017 -1017 NO_DATA_FOUND ORA-01403 +100 NOT_LOGGED_ON ORA-01012 -1012 PROGRAM_ERROR ORA-06501 -6501 ROWTYPE_MISMATCH ORA-06504 -6504 SELF_IS_NULL ORA-30625 -30625 STORAGE_ERROR ORA-06500 -6500 SUBSCRIPT_BEYOND_COUNT ORA-06533 -6533 SUBSCRIPT_OUTSIDE_LIMIT ORA-06532 -6532
  • 68. Page 68 of 77 SYS_INVALID_ROWID ORA-01410 -1410 TIMEOUT_ON_RESOURCE ORA-00051 -51 TOO_MANY_ROWS ORA-01422 -1422 VALUE_ERROR ORA-06502 -6502 ZERO_DIVIDE ORA-01476 -1476 Brief descriptions of thepredefined exceptions follow: Exception Raised when ... ACCESS_INTO_NULL A program attempts to assign values to the attributes of an uninitialized object. CASE_NOT_FOUND None of the choices in the WHEN clauses of a CASE statement is selected, and there is no ELSE clause. COLLECTION_IS_NULL A program attempts to apply collection methods other than EXISTS to an uninitialized nested table or varray, or the program attempts to assign values to theelements of an uninitialized nested table or varray. CURSOR_ALREADY_OPEN A program attempts to open an already open cursor. A cursor must be closed before it can be reopened. A cursor FOR loop automatically opens the cursor to which it refers, so your program cannot open that cursor inside the loop. DUP_VAL_ON_INDEX A program attempts to storeduplicate values in a database column that is constrained by a unique index. INVALID_CURSOR A program attempts acursor operation that is not allowed, such as closing an unopened cursor. INVALID_NUMBER In a SQL statement, the conversion of a character string into a number fails because the string does not represent a valid number. (In procedural statements, VALUE_ERROR is raised.) This exception is also raised when the LIMIT-clause expression in a bulk FETCH statement does not evaluate to a positivenumber. LOGIN_DENIED A program attempts to log on to Oracle with an invalid username or password. NO_DATA_FOUND A SELECT INTO statement returns no rows, or your program references a deleted element in a nested table or an uninitialized element in an index-by table. Because this exception is used internally by some SQL functions to signal that they are finished, you should not rely on this exception being propagated if you raise it within a function that is called as part of a query. NOT_LOGGED_ON A program issues a database call without being connected to Oracle. PROGRAM_ERROR PL/SQL has an internal problem. ROWTYPE_MISMATCH The host cursor variable and PL/SQL cursor variable involved in an assignment have incompatible return types. For example, when an open host cursor variable is passed to a stored subprogram, the return types of theactual and formal parameters must be compatible. SELF_IS_NULL A program attempts to call a MEMBER method, but the instance of the object typehas not been initialized. The built-in parameter SELF points to theobject, and is always the first parameter passed to a MEMBER method. STORAGE_ERROR PL/SQL runs out of memory or memory has been corrupted. SUBSCRIPT_BEYOND_COUNT A program references a nested table or varray element using an index number larger than the number of elements in the collection. SUBSCRIPT_OUTSIDE_LIMIT A program references a nested table or varray element using an index number (-1 for example) that is outside the legal range. SYS_INVALID_ROWID The conversion of a character string into a universal rowid fails because thecharacter string does not represent a valid rowid. TIMEOUT_ON_RESOURCE A time-out occurs while Oracle is waiting for a resource. TOO_MANY_ROWS A SELECT INTO statement returns more than one row. VALUE_ERROR An arithmetic, conversion, truncation, or size-constraint error occurs. For example, when your program selects a column value into a character variable, if the value is longer than the declared length of thevariable, PL/SQL aborts the assignment and raises VALUE_ERROR. In procedural statements, VALUE_ERROR is raised if the conversion of a character string into a number fails. (In SQL statements, INVALID_NUMBER is raised.) ZERO_DIVIDE A program attempts to divide a number by zero. Defining Your Own PL/SQL Exceptions PL/SQL lets you define exceptions of your own. Unlike predefined exceptions, user- defined exceptions must be declared and must be raised explicitly by RAISE statements. Declaring PL/SQL Exceptions Exceptions can be declared only in thedeclarative part of a PL/SQL block, subprogram, or package. You declare an exception by introducing its name, followed by the keyword EXCEPTION. In the following example, you declare an exception named past_due: DECLARE past_due EXCEPTION; Exception and variable declarations are similar. But remember, an exception is an error condition, not a data item. Unlike variables, exceptions cannot appear in assignment statements or SQL statements. However, the same scope rules apply to variables and exceptions. Scope Rules for PL/SQL Exceptions You cannot declare an exception twice in thesame block. You can, however, declare the same exception in two different blocks.
  • 69. Page 69 of 77 Exceptions declared in a block are considered local to that block and global to all its sub- blocks. Because a block can reference only local or global exceptions, enclosing blocks cannot reference exceptions declared in a sub-block. If you redeclare a global exception in a sub-block, the local declaration prevails. The sub- block cannot reference the global exception, unless theexception is declared in a labeled block and you qualify its name with theblock label: block_label.exception_name The following example illustrates thescope rules: DECLARE past_due EXCEPTION; acct_num NUMBER; BEGIN DECLARE ---------- sub-block begins past_due EXCEPTION; -- this declaration prevails acct_num NUMBER; due_date DATE := SYSDATE - 1; todays_date DATE := SYSDATE; BEGIN IF due_date < todays_date THEN RAISE past_due; -- this is not handled END IF; END; ------------- sub-block ends EXCEPTION WHEN past_due THEN -- does not handle RAISEd exception dbms_output.put_line('Handling PAST_DUE exception.'); WHEN OTHERS THEN dbms_output.put_line('Could not recognize PAST_DUE_EXCEPTION in this scope.'); END; / The enclosing block does not handle theraised exception because thedeclaration of past_due in thesub-block prevails. Though they share the same name, the two past_due exceptions are different, just as thetwo acct_num variables share the same name but are different variables. Thus, the RAISE statement and the WHEN clause refer to different exceptions. To have the enclosing block handle theraised exception, you must remove its declaration from thesub-block or define an OTHERS handler. Associating a PL/SQL Exception with a Number: Pragma EXCEPTION_INIT To handle error conditions (typically ORA- messages) that have no predefined name, you must use the OTHERS handler or the pragma EXCEPTION_INIT. A pragma is a compiler directive that is processed at compile time, not at run time. In PL/SQL, the pragma EXCEPTION_INIT tells the compiler to associate an exception name with an Oracle error number. That lets you refer to any internal exception by name and to write a specific handler for it. When you see an error stack, or sequence of error messages, the one on top is theone that you can trap and handle. You code the pragma EXCEPTION_INIT in thedeclarative part of a PL/SQL block, subprogram, or package using thesyntax PRAGMA EXCEPTION_INIT(exception_name, -Oracle_error_number); where exception_name is thename of a previously declared exception and the number is a negative value corresponding to an ORA- error number. The pragma must appear somewhere after theexception declaration in thesame declarative section, as shown in the following example: DECLARE deadlock_detected EXCEPTION; PRAGMA EXCEPTION_INIT(deadlock_detected, -60); BEGIN null; -- Some operation that causes an ORA-00060 error EXCEPTION WHEN deadlock_detected THEN null; -- handle the error END; / Defining Your Own Error Messages: Procedure RAISE_APPLICATION_ERROR The procedure RAISE_APPLICATION_ERROR lets you issueuser-defined ORA- error messages from stored subprograms. That way, you can report errors to your application and avoid returning unhandled exceptions. To call RAISE_APPLICATION_ERROR, usethesyntax raise_application_error(error_number, message[, {TRUE | FALSE}]); where error_number is a negative integer in the range -20000 .. -20999 and message is a character string up to 2048 bytes long. If the optionalthird parameter is TRUE, the error is placed on the stack of previous errors. If the parameter is FALSE (the default), the error replaces all previous errors. RAISE_APPLICATION_ERROR is part of package DBMS_STANDARD, and as with package STANDARD, you do not need to qualify references to it. An application can call raise_application_error only froman executing stored subprogram (or method). When called, raise_application_error ends thesubprogram and returns a user-defined error number and message to the application. The error number and message can be trapped like any Oracle error. In the following example, you call raise_application_error if an error condition of your choosing happens (in this case, if the current schema owns less than 1000 tables): DECLARE num_tables NUMBER; BEGIN SELECT COUNT(*) INTO num_tables FROM USER_TABLES; IF num_tables < 1000 THEN /* Issue your own error code (ORA-20101) with your own error message. */ raise_application_error(-20101, 'Expecting at least 1000 tables'); ELSE NULL; -- Do the rest ofthe processing (for the non-error case). END IF; END; / The calling application gets a PL/SQL exception, which it can process using theerror- reporting functions SQLCODE and SQLERRM in an OTHERS handler. Also, it can use the pragma EXCEPTION_INIT to map specific error numbers returned by raise_application_error to exceptions of its own, as the following Pro*C example shows: EXEC SQL EXECUTE /* Execute embedded PL/SQL block using host variables my_emp_id and my_amount, which were assigned values in the host environment. */ DECLARE null_salary EXCEPTION; /* Map error number returned by raise_application_error to user-defined exception. */
  • 70. Page 70 of 77 PRAGMA EXCEPTION_INIT(null_salary, -20101); BEGIN raise_salary(:my_emp_id, :my_amount); EXCEPTION WHEN null_salary THEN INSERT INTO emp_audit VALUES (:my_emp_id, ...); END; END-EXEC; This technique allows the calling application to handle error conditions in specific exception handlers. Redeclaring Predefined Exceptions Remember, PL/SQL declares predefined exceptions globally in package STANDARD, so you need not declare them yourself. Redeclaring predefined exceptions is error prone because your local declaration overrides the global declaration. For example, if you declare an exception named invalid_number and then PL/SQL raises the predefined exception INVALID_NUMBER internally, a handler written for INVALID_NUMBER will not catch the internal exception. In such cases, you must use dot notation to specify the predefined exception, as follows: EXCEPTION WHEN invalid_number OR STANDARD.INVALID_NUMBER THEN -- handle the error END; How PL/SQL Exceptions Are Raised Internal exceptions are raised implicitly by therun-time system, as are user-defined exceptions that you have associated with an Oracle error number using EXCEPTION_INIT. However, other user-defined exceptions must be raised explicitly by RAISE statements. Raising Exceptions with the RAISE Statement PL/SQL blocks and subprograms should raise an exception only when an error makes it undesirable or impossible to finish processing. You can place RAISE statements for a given exception anywherewithin the scopeof that exception. In thefollowing example, you alert your PL/SQL block to a user-defined exception named out_of_stock: DECLARE out_of_stock EXCEPTION; number_on_hand NUMBER := 0; BEGIN IF number_on_hand < 1 THEN RAISE out_of_stock; -- raise an exception that we defined END IF; EXCEPTION WHEN out_of_stock THEN -- handle the error dbms_output.put_line('Encountered out-of-stock error.'); END; / You can also raise a predefined exception explicitly. That way, an exception handler written for the predefined exception can process other errors, as the following example shows: DECLARE acct_type INTEGER := 7; BEGIN IF acct_type NOT IN (1, 2, 3) THEN RAISE INVALID_NUMBER; -- raise predefined exception END IF; EXCEPTION WHEN INVALID_NUMBER THEN dbms_output.put_line('Handling invalid input by rolling back.'); ROLLBACK; END; / How PL/SQL Exceptions Propagate When an exception is raised, if PL/SQL cannot find a handler for it in the current block or subprogram, the exception propagates. That is, theexception reproduces itself in successive enclosing blocks until a handler is found or there are no more blocks to search. If no handler is found, PL/SQL returns an unhandled exception error to the host environment. Exceptions cannot propagate across remote procedure calls done through database links. An exception can propagatebeyond its scope, that is, beyond theblock in which it was declared. Consider the following example: BEGIN DECLARE ---------- sub-block begins past_due EXCEPTION; due_date DATE := trunc(SYSDATE) - 1; todays_date DATE := trunc(SYSDATE); BEGIN IF due_date < todays_date THEN RAISE past_due; END IF; END; ------------- sub-block ends EXCEPTION WHEN OTHERS THEN ROLLBACK; END; / Because the block that declares theexception past_due has no handler for it, the exception propagates to the enclosing block. But the enclosing block cannot reference the name PAST_DUE, because thescope where it was declared no longer exists. Once the exception name is lost, only an OTHERS handler can catch the exception. If there is no handler for a user-defined exception, thecalling application gets this error: ORA-06510: PL/SQL: unhandled user-defined exception Reraising a PL/SQL Exception Sometimes, you want to reraise an exception, that is, handle it locally, then pass it to an enclosing block. For example, you might want to roll back a transaction in the current block, then log theerror in an enclosing block. To reraise an exception, use a RAISE statement without an exception name, which is allowed only in an exception handler: DECLARE salary_too_high EXCEPTION; current_salary NUMBER := 20000; max_salary NUMBER := 10000; erroneous_salary NUMBER; BEGIN BEGIN ---------- sub-block begins IF current_salary > max_salary THEN RAISE salary_too_high; -- raise the exception END IF; EXCEPTION WHEN salary_too_high THEN
  • 71. Page 71 of 77 -- first step in handling the error dbms_output.put_line('Salary ' || erroneous_salary || ' is out ofrange.'); dbms_output.put_line('Maximumsalary is ' || max_salary || '.'); RAISE; -- reraise the current exception END; ------------ sub-block ends EXCEPTION WHEN salary_too_high THEN -- handle the error more thoroughly erroneous_salary := current_salary; current_salary := max_salary; dbms_output.put_line('Revising salary from' || erroneous_salary || 'to ' || current_salary || '.'); END; / Handling RaisedPL/SQL Exceptions When an exception is raised, normal execution of your PL/SQL block or subprogram stops and control transfers to its exception-handling part, which is formatted as follows: EXCEPTION WHEN exception_name1 THEN -- handler sequence_of_statements1 WHEN exception_name2 THEN -- another handler sequence_of_statements2 ... WHEN OTHERS THEN -- optional handler sequence_of_statements3 END; To catch raised exceptions, you writeexception handlers. Each handler consists of a WHEN clause, which specifies an exception, followed by a sequence of statements to be executed when that exception is raised. These statements complete execution of the block or subprogram; control does not return to where the exception was raised. In other words, you cannot resume processing where you left off. The optionalOTHERS exception handler, which is always thelast handler in a block or subprogram, acts as the handler for all exceptions not named specifically. Thus, a block or subprogram can have only one OTHERS handler. As the following example shows, use of the OTHERS handler guarantees that no exception will go unhandled: EXCEPTION WHEN ... THEN -- handle the error WHEN ... THEN -- handle the error WHEN OTHERS THEN -- handle all other errors END; If you want two or more exceptions to execute the same sequence of statements, list the exception names in the WHEN clause, separating them by thekeyword OR, as follows: EXCEPTION WHEN over_limit OR under_limit OR VALUE_ERROR THEN -- handle the error If any of the exceptions in thelist is raised, theassociated sequence of statements is executed. Thekeyword OTHERS cannot appear in the list of exception names; it must appear by itself. You can have any number of exception handlers, and each handler can associate a list of exceptions with a sequence of statements. However, an exception name can appear only once in the exception-handling part of a PL/SQL block or subprogram. The usual scoping rules for PL/SQL variables apply, so you can reference local and global variables in an exception handler. However, when an exception is raised inside a cursor FOR loop, thecursor is closed implicitly before the handler is invoked. Therefore, the values of explicit cursor attributes are not available in the handler. Handling Exceptions Raised in Declarations Exceptions can be raised in declarations by faulty initialization expressions. For example, the following declaration raises an exception because the constant credit_limit cannot store numbers larger than 999: DECLARE credit_limit CONSTANT NUMBER(3) := 5000; -- raises an exception BEGIN NULL; EXCEPTION WHEN OTHERS THEN -- Cannot catch the exception. This handler is never called. dbms_output.put_line('Can''t handle an exception in a declaration.'); END; / Handlers in thecurrent block cannot catch the raised exception because an exception raised in a declaration propagates immediately to theenclosing block. Handling Exceptions Raised in Handlers When an exception occurs within an exception handler, that same handler cannot catch the exception. An exception raised inside a handler propagates immediately to the enclosing block, which is searched to find a handler for this new exception. From there on, the exception propagates normally. For example: EXCEPTION WHEN INVALID_NUMBER THEN INSERT INTO ... -- might raise DUP_VAL_ON_INDEX WHEN DUP_VAL_ON_INDEX THEN ... -- cannot catch the exception END; 11 – Triggers This chapter discusses triggers, which are procedures stored in PL/SQL or Java that run (fire) implicitly whenever a table or view is modified or when some user actions or database systemactions occur. Introduction to Triggers You can write triggers that fire whenever one of the following operations occurs: 1. DMLstatements (INSERT, UPDATE, DELETE) on a particular table or view, issued by any user 2. DDL statements (CREATE or ALTER primarily) issued either by a particular schema/user or by any schema/user in the database 3. Database events, such as logon/logoff, errors, or startup/shutdown, also issued either by a particular schema/user or by any schema/user in the database Triggers are similar to stored procedures. A trigger stored in the database can include SQL and PL/SQL or Java statements to run as a unit and can invoke stored procedures.
  • 72. Page 72 of 77 However, procedures and triggers differ in theway that they are invoked. A procedure is explicitly run by a user, application, or trigger. Triggers are implicitly fired by Oracle when a triggering event occurs, no matter which user is connected or which application is being used. Figure 22-1 shows a database application with some SQL statements that implicitly fire several triggers stored in the database. Notice that the database stores triggers separately from their associated tables. Figure 22-1 Triggers Description of the illustration cncpt076.gif A trigger can also call out to a C procedure, which is useful for computationally intensive operations. The events that fire a trigger include thefollowing:  DMLstatements that modify data in a table (INSERT, UPDATE, or DELETE)  DDL statements  Systemevents such as startup, shutdown, and error messages  User events such as logon and logoff Note: Oracle Forms can define, store, and run triggers of a different sort. However, do not confuse Oracle Forms triggers with the triggers discussed in this chapter. How Triggers Are Used Triggers supplement thestandard capabilities of Oracle to provide a highly customized database management system. For example, a trigger can restrict DMLoperations against a table to thoseissued during regular business hours. You can also use triggers to:  Automatically generate derived column values  Prevent invalid transactions  Enforce complex security authorizations  Enforce referential integrity across nodes in a distributed database  Enforce complex business rules  Provide transparent event logging  Provide auditing  Maintain synchronous table replicates  Gather statistics on table access  Modify tabledata when DMLstatements are issued against views  Publish information about database events, user events, and SQL statements to subscribing applications Some Cautionary Notes about Triggers Although triggers are useful for customizing a database, use them only when necessary. Excessive use of triggers can result in complex interdependencies, which can be difficult to maintain in a large application. For example, when a trigger fires, a SQL statement within its trigger action potentially can fire other triggers, resulting in cascading triggers. This can produce unintended effects. Figure 22-2 illustrates cascading triggers. Figure 22-2 Cascading Triggers Description of the illustration cncpt077.gif Triggers Compared with Declarative Integrity Constraints You can use both triggers and integrity constraints to define and enforce any typeof integrity rule. However, Oracle strongly recommends that you use triggers to constrain data input only in the following situations:  To enforce referential integrity when child and parent tables are on different nodes of a distributed database  To enforce complex business rules not definable using integrity constraints  When a required referential integrity rule cannot be enforced using the following integrity constraints: o NOT NULL, UNIQUE o PRIMARY KEY o FOREIGN KEY o CHECK o DELETE CASCADE o DELETE SET NULL Parts of a Trigger
  • 73. Page 73 of 77 A trigger has three basic parts:  A triggering event or statement  A trigger restriction  A trigger action Figure 22-3 represents each of these parts of a trigger and is not meant to show exact syntax. The sections that follow explain each part of a trigger in greater detail. Figure 22-3 The REORDER Trigger Description of the illustration cncpt078.gif The Triggering Event or Statement A triggering event or statement is the SQL statement, database event, or user event that causes a trigger to fire. A triggering event can be one or more of thefollowing:  An INSERT, UPDATE, or DELETE statement on a specific table (or view, in some cases)  A CREATE, ALTER, or DROP statement on any schema object  A database startup or instance shutdown  A specific error message or any error message  A user logon or logoff For example, in Figure 22-3, the triggering statement is: ... UPDATE OF parts_on_hand ON inventory ... This statement means that when the parts_on_hand column of a row in the inventory tableis updated, fire thetrigger. When the triggering event is an UPDATE statement, you can include a column list to identify which columns must be updated to fire thetrigger. You cannot specify a column list for INSERT and DELETE statements, because they affect entire rows of information. A triggering event can specify multipleSQL statements: ... INSERT OR UPDATE OR DELETE OF inventory ... This part means that when an INSERT, UPDATE, or DELETE statement is issued against the inventory table, fire thetrigger. When multiple types of SQL statements can fire a trigger, you can use conditional predicates to detect the typeof triggering statement. In this way, you can create a single trigger that runs different code based on the typeof statement that fires the trigger. Trigger Restriction A trigger restriction specifies a Boolean expression that must be true for the trigger to fire. The trigger action is not run if thetrigger restriction evaluates to false or unknown. In the example, thetrigger restriction is: new.parts_on_hand < new.reorder_point Consequently, the trigger does not fire unless thenumber of available parts is less than a present reorder amount. Trigger Action A trigger action is the procedure (PL/SQL block, Java program, or C callout) that contains the SQL statements and code to be run when thefollowing events occur:  A triggering statement is issued.  The trigger restriction evaluates to true. Like stored procedures, a trigger action can:  Contain SQL, PL/SQL, or Java statements  Define PL/SQL language constructs such as variables, constants, cursors, exceptions  Define Java language constructs  Call stored procedures If the triggers are row triggers, the statements in a trigger action have access to column values of therow being processed by the trigger. Correlation names provide access to the old and new values for each column. Types of Triggers Row Triggers and Statement Triggers When you define a trigger, you can specify thenumber of times thetrigger action is to be run:  Once for every row affected by the triggering statement, such as a trigger fired by an UPDATE statement that updates many rows  Once for the triggering statement, no matter how many rows it affects Row Triggers A row triggeris fired each time the table is affected by thetriggering statement. For example, if an UPDATE statement updates multiple rows of a table, a row trigger is fired once for each row affected by the UPDATE statement. If a triggering statement affects no rows, a row trigger is not run. Row triggers are useful if thecode in the trigger action depends on data provided by the triggering statement or rows that are affected. For example, Figure 22-3 illustrates a row trigger that uses thevalues of each row affected by the triggering statement. Statement Triggers A statement trigger is fired once on behalf of the triggering statement, regardless of the number of rows in the table that the triggering statement affects, even if no rows are affected. For example, if a DELETE statement deletes several rows from a table, a statement-level DELETE trigger is fired only once. Statement triggers are useful if the code in thetrigger action does not depend on thedata provided by thetriggering statement or therows affected. For example, use a statement trigger to:
  • 74. Page 74 of 77  Makea complex security check on the current time or user  Generate a single audit record BEFORE and AFTER Triggers When defining a trigger, you can specify thetriggertiming—whether thetrigger action is to be run before or after the triggering statement. BEFORE and AFTER apply to both statement and row triggers. BEFORE and AFTER triggers fired by DMLstatements can be defined only on tables, not on views. However, triggers on thebase tables of a view are fired if an INSERT, UPDATE, or DELETE statement is issued against the view. BEFORE and AFTER triggers fired by DDL statements can be defined only on the database or a schema, not on particular tables. BEFORE Triggers BEFORE triggers run the trigger action before the triggering statement is run. This typeof trigger is commonly used in thefollowing situations:  When thetrigger action determines whether the triggering statement should be allowed to complete. Using a BEFORE trigger for this purpose, you can eliminate unnecessary processing of the triggering statement and its eventual rollback in cases where an exception is raised in thetrigger action.  To derive specific column values before completing a triggering INSERT or UPDATE statement. AFTER Triggers AFTER triggers run the trigger action after the triggering statement is run. TriggerType Combinations Using theoptions listed previously, you can create four types of row and statement triggers:  BEFORE statement trigger Before executing the triggering statement, the trigger action is run.  BEFORE row trigger Before modifying each row affected by the triggering statement and before checking appropriateintegrity constraints, thetrigger action is run, if the trigger restriction was not violated.  AFTER statement trigger After executing the triggering statement and applyingany deferred integrity constraints, the trigger action is run.  AFTER row trigger After modifying each row affected by thetriggering statement and possibly applyingappropriateintegrity constraints, thetrigger action is run for the current row provided the trigger restriction was not violated. Unlike BEFORE row triggers, AFTER row triggers lock rows. You can have multiple triggers of thesame typefor thesame statement for any given table. For example, you can have two BEFORE statement triggers for UPDATE statements on the employees table. Multipletriggers of the same typepermit modular installation of applications that have triggers on the same tables. Also, Oracle materialized view logs use AFTER row triggers, so you can design your own AFTER row trigger in addition to the Oracle- defined AFTER row trigger. You can create as many triggers of the preceding different types as you need for each type of DMLstatement, (INSERT, UPDATE, or DELETE). INSTEAD OF Triggers INSTEAD OF triggers providea transparent way of modifying views that cannot be modified directly through DMLstatements (INSERT, UPDATE, and DELETE). These triggers are called INSTEAD OF triggers because, unlike other types of triggers, Oracle fires thetrigger instead of executing thetriggering statement. You can write normal INSERT, UPDATE, and DELETE statements against theview and the INSTEAD OF trigger is fired to updatethe underlying tables appropriately. INSTEAD OF triggers are activated for each row of the view that gets modified. INSTEAD OF Triggers on NestedTables You cannot modify the elements of a nested table column in a view directly with the TABLE clause. However, you can do so by defining an INSTEAD OF trigger on thenested table column of the view. The triggers on thenested tables fire if a nested table element is updated, inserted, or deleted and handle the actual modifications to theunderlying tables. Triggers on System Events and User Events You can use triggers to publish information about database events to subscribers. Applications can subscribe to database events just as they subscribe to messages from other applications. Thesedatabase events can include:  Systemevents o Database startup and shutdown o Server error message events  User events o User logon and logoff o DDL statements (CREATE, ALTER, and DROP) o DMLstatements (INSERT, DELETE, and UPDATE) Triggers on systemevents can be defined at the database level or schema level. The DBMS_AQ package is one example of using database triggers to perform certain actions. For example, a database shutdown trigger is defined at the database level: CREATE TRIGGER register_shutdown ON DATABASE SHUTDOWN BEGIN ... DBMS_AQ.ENQUEUE(...); ... END; Triggers on DDLstatements or logon/logoff events can also be defined at thedatabase level or schema level. Triggers on DMLstatements can be defined on a table or view. A trigger defined at thedatabase level fires for all users, and a trigger defined at the schema or table level fires only when the triggering event involves that schema or table. Event Publication Event publication uses thepublish-subscribe mechanism of Oracle Streams Advanced Queuing. A queue serves as a message repository for subjects of interest to various subscribers. Triggers use the DBMS_AQ package to enqueue a message when specific systemor user events occur. Event Attributes Each event allows the use of attributes within the trigger text. For example, the database startup and shutdown triggers have attributes for theinstance number and thedatabase name, and the logon and logoff triggers have attributes for the user name. You can specify a function with thesame name as an attributewhen you create a trigger if you want to
  • 75. Page 75 of 77 publish that attributewhen the event occurs. The attribute's value is then passed to the function or payload when the trigger fires. For triggers on DMLstatements, the:OLD column values pass theattribute's value to the :NEW column value. System Events Systemevents that can fire triggers are related to instance startup and shutdown and error messages. Triggers created on startup and shutdown events have to be associated with the database. Triggers created on error events can be associated with the database or with a schema.  STARTUP triggers fire when the database is opened by an instance. Their attributes include the systemevent, instance number, and database name.  SHUTDOWN triggers fire just before theserver starts shuttingdown an instance. You can use these triggers to make subscribing applications shut down completely when the database shuts down. For abnormal instance shutdown, these triggers cannot be fired. The attributes of SHUTDOWN triggers include the systemevent, instance number, and database name.  SERVERERROR triggers fire when a specified error occurs, or when any error occurs if no error number is specified. Their attributes include the systemevent and error number. User Events User events that can fire triggers are related to user logon and logoff, DDL statements, and DMLstatements. Triggers on LOGON and LOGOFF Events LOGON and LOGOFF triggers can be associated with thedatabase or with a schema. Their attributes include the systemevent and user name, and they can specify simple conditions on USERID and USERNAME.  LOGON triggers fire after a successful logon of a user.  LOGOFF triggers fire at the start of a user logoff. Triggers on DDL Statements DDL triggers can be associated with the database or with a schema. Their attributes include the systemevent, the typeof schema object, and its name. They can specify simple conditions on the typeand name of theschema object, as well as functions like USERID and USERNAME. DDL triggers include the following types of triggers:  BEFORE CREATE and AFTER CREATE triggers fire when a schema object is created in the database or schema.  BEFORE ALTER and AFTER ALTER triggers fire when a schema object is altered in the database or schema.  BEFORE DROP and AFTER DROP triggers fire when a schema object is dropped from the database or schema. Triggers on DML Statements DMLtriggers for event publication are associated with a table. They can be either BEFORE or AFTER triggers that fire for each row on which the specified DMLoperation occurs. You cannot use INSTEAD OF triggers on views to publish events related to DMLstatements— instead, you can publish events using BEFORE or AFTER triggers for theDMLoperations on a view's underlying tables that are caused by INSTEAD OF triggers. The attributes of DMLtriggers for event publication include the systemevent and the columns defined by theuser in the SELECT list. They can specify simpleconditions on the typeand name of the schema object, as well as functions (such as UID, USER, USERENV, and SYSDATE), pseudocolumns, and columns. Thecolumns can be prefixed by :OLD and :NEW for old and new values. Triggers on DMLstatements include thefollowing triggers:  BEFORE INSERT and AFTER INSERT triggers fire for each row inserted into the table.  BEFORE UPDATE and AFTER UPDATE triggers fire for each row updated in the table.  BEFORE DELETE and AFTER DELETE triggers fire for each row deleted from the table. Trigger Execution A trigger is in either of two distinct modes: Trigger Mode Definition Enabled An enabled trigger runs its trigger action if a triggering statement is issued and the trigger restriction (if any) evaluates to true. Disabled A disabled trigger does not run its trigger action, even if a triggering statement is issued and the trigger restriction (if any) would evaluate to true. For enabled triggers, Oracle automatically performs the following actions:  Oracle runs triggers of each typein a planned firing sequence when more than one trigger is fired by a single SQL statement. First, statement level triggers are fired, and then row level triggers are fired.  Oracle performs integrity constraint checking at a set point in time with respect to the different types of triggers and guarantees that triggers cannot compromise integrity constraints.  Oracle provides read-consistent views for queries and constraints.  Oracle manages the dependencies among triggers and schema objects referenced in the code of thetrigger action  Oracle uses two-phasecommit if a trigger updates remote tables in a distributed database.  Oracle fires multiple triggers in an unspecified, random order, if more than one trigger of the same typeexists for a given statement; that is, triggers of the same typefor thesame statement are not guaranteed to fire in any specific order. The Execution Model for Triggers and Integrity Constraint Checking A single SQL statement can potentially fire up to four types of triggers:  BEFORE row triggers  BEFORE statement triggers  AFTER row triggers  AFTER statement triggers A triggering statement or a statement within a trigger can cause one or more integrity constraints to be checked. Also, triggers can contain statements that cause other triggers to fire (cascading triggers). Oracle uses thefollowing execution model to maintain the proper firing sequence of multiple triggers and constraint checking: 1. Run all BEFORE statement triggers that apply to thestatement.
  • 76. Page 76 of 77 2. Loop for each row affected by theSQL statement. a. Run all BEFORE row triggers that apply to thestatement. b. Lock and change row, and perform integrity constraint checking. (The lock is not released until the transaction is committed.) c. Run all AFTER row triggers that apply to thestatement. 3. Complete deferred integrity constraint checking. 4. Run all AFTER statement triggers that apply to thestatement. The definition of the execution model is recursive. For example, a given SQL statement can cause a BEFORE row trigger to be fired and an integrity constraint to be checked. That BEFORE row trigger, in turn, might perform an updatethat causes an integrity constraint to be checked and an AFTER statement trigger to be fired. The AFTER statement trigger causes an integrity constraint to be checked. In this case, the execution model runs the steps recursively, as follows: Original SQL statement issued. 1. BEFORE row triggers fired. a. AFTER statement triggers fired by UPDATE in BEFORE row trigger. i. Statements of AFTER statement triggers run. ii. Integrity constraint checked on tables changed by AFTER statement triggers. b. Statements of BEFORE row triggers run. c. Integrity constraint checked on tables changed by BEFORE row triggers. 2. SQL statement run. 3. Integrity constraint from SQL statement checked. There are two exceptions to this recursion:  When a triggering statement modifies one table in a referential constraint (either the primary key or foreign key table), and a triggered statement modifies the other, only the triggering statement will check theintegrity constraint. This allows row triggers to enhance referential integrity.  Statement triggers fired due to DELETE CASCADE and DELETE SET NULL are fired before and after the user DELETE statement, not before and after the individual enforcement statements. This prevents thosestatement triggers from encountering mutating errors. An important property of theexecution model is that all actions and checks done as a result of a SQL statement must succeed. If an exception is raised within a trigger, and the exception is not explicitly handled, all actions performed as a result of the original SQL statement, including the actions performed by fired triggers, are rolled back. Thus, integrity constraints cannot be compromised by triggers. The execution model takes into account integrity constraints and disallows triggers that violate declarative integrity constraints. For example, in thepreviously outlined scenario, supposethat theintegrity constraint is violated. As a result of this violation, all changes made by theSQL statement, the fired BEFORE row trigger, and thefired AFTER statement trigger are rolled back. Note: Although triggers of different types arefired in a specific order, triggers of the same typefor the same statement are not guaranteed to fire in any specific order. For example, all BEFORE row triggers for a single UPDATE statement may not always fire in the same order. Design your applications so they do not rely on the firing order of multiple triggers of the same type. Data Access for Triggers When a trigger is fired, thetables referenced in the trigger action might be currently undergoing changes by SQL statements in other users' transactions. In all cases, the SQL statements run within triggers follow the common rules used for standalone SQL statements. In particular, if an uncommitted transaction has modified values that a trigger being fired either needs to read (query) or write (update), then theSQL statements in the body of the trigger being fired use the following guidelines:  Queries see thecurrent read-consistent materialized view of referenced tables and any data changed within the same transaction.  Updates wait for existing data locks to be released before proceeding. Storage of PL/SQL Triggers Oracle stores PL/SQL triggers in compiled form, just like stored procedures. When a CREATE TRIGGER statement commits, the compiled PL/SQL code, called P code (for pseudocode), is stored in the database and thesource code of the trigger is flushed from the shared pool. Execution of Triggers Oracle runs a trigger internally using the same steps used for procedure execution. The only subtle difference is that a user has the right to fire a trigger if he or she has the privilege to run the triggering statement. Other than this, triggers are validated and run the same way as stored procedures. Dependency Maintenance for Triggers Like procedures, triggers depend on referenced objects. Oracle automatically manages the dependencies of a trigger on the schema objects referenced in its trigger action. The dependency issues for triggers are the same as thosefor stored procedures. Triggers are treated like stored procedures. They are inserted into thedata dictionary. Creating Triggers The Syntax for creating a trigger is: CREATE [OR REPLACE ] TRIGGER trigger_name {BEFORE | AFTER | INSTEAD OF } {INSERT [OR] | UPDATE[OR] | DELETE} [OF col_name] ON table_name [REFERENCING OLD AS o NEW AS n] [FOR EACH ROW] WHEN (condition) DECLARE Declaration-statements BEGIN Executable-statements EXCEPTION Exception-handling-statements
  • 77. Page 77 of 77 END; Where,  CREATE [OR REPLACE] TRIGGER trigger_name : Creates or replace an existing trigger with the trigger_name.  {BEFORE | AFTER | INSTEAD OF} : This specifies when thetrigger would be executed. TheINSTEAD OF clause is used for creating trigger on a view.  {INSERT [OR] | UPDATE[OR] | DELETE}: This specifies the DMLoperation.  [OF col_name]: This specifies thecolumn name that would be updated.  [ON table_name]: This specifies the name of the table associated with the trigger.  [REFERENCING OLD AS o NEW AS n]: This allows you to refer new and old values for various DMLstatements, like INSERT, UPDATE, and DELETE.  [FOR EACH ROW]: This specifies a row level trigger, i.e., thetrigger would be executed for each row being affected. Otherwisethe trigger will execute just once when theSQL statement is executed, which is called a table level trigger.  WHEN (condition): This provides a condition for rows for which thetrigger would fire. This clause is valid only for row level triggers. Example: To start with, we will be using the CUSTOMERStable we had created and used in the previous chapters: Select * from customers; +----+----------+-----+-----------+----------+ | ID | NAME | AGE | ADDRESS | SALARY | +----+----------+-----+-----------+----------+ | 1 | Ramesh | 32 | Ahmedabad | 2000.00 | | 2 | Khilan | 25 | Delhi | 1500.00 | | 3 | kaushik | 23 | Kota | 2000.00 | | 4 | Chaitali | 25 | Mumbai | 6500.00 | | 5 | Hardik | 27 | Bhopal | 8500.00 | | 6 | Komal | 22 | MP | 4500.00 | +----+----------+-----+-----------+----------+ The following program creates a row level trigger for the customers table that would fire for INSERT or UPDATEor DELETE operations performed on theCUSTOMERStable. This trigger will display thesalary difference between theold values and new values: CREATE OR REPLACE TRIGGER display_salary_changes BEFORE DELETE OR INSERT OR UPDATEON customers FOR EACH ROW WHEN (NEW.ID > 0) DECLARE sal_diff number; BEGIN sal_diff := :NEW.salary - :OLD.salary; dbms_output.put_line('Old salary:' || :OLD.salary); dbms_output.put_line('New salary:' || :NEW.salary); dbms_output.put_line('Salary difference: ' || sal_diff); END; / When theabove code is executed at SQL prompt, it produces thefollowing result: Trigger created. Here following two points are important and should be noted carefully:  OLD and NEW references are not available for table level triggers, rather you can use them for record level triggers.  If you want to query the table in the same trigger, then you should use the AFTER keyword, because triggers can query thetable or change it again only after the initial changes are applied and thetable is back in a consistent state.  Above trigger has been written in such a way that it will fire before any DELETE or INSERT or UPDATEoperation on the table, but you can write your trigger on a single or multiple operations, for example BEFORE DELETE, which will fire whenever a record will be deleted using DELETE operation on the table. Triggering a Trigger Let us performsome DMLoperations on the CUSTOMERStable. Here is one INSERT statement which will create a new record in the table: INSERT INTO CUSTOMERS(ID,NAME,AGE,ADDRESS,SALARY) VALUES (7, 'Kriti', 22, 'HP', 7500.00 ); When a record is created in CUSTOMERStable, above create trigger display_salary_changes willbe fired and it will display following result: Old salary: New salary: 7500 Salary difference: Because this is a new record so old salary is not available and above result is coming as null. Now, let us performone more DMLoperation on the CUSTOMERS table. Here is one UPDATEstatement which will updatean existing record in the table: UPDATEcustomers SET salary = salary + 500 WHERE id = 2; When a record is updated in CUSTOMERStable, above create trigger display_salary_changes willbe fired and it will display following result: Old salary: 1500 New salary: 2000 Salary difference: 500