PLSQL Intrv Guide
PLSQL Intrv Guide
Q. What is PL/SQL?
A. PL/SQL is Oracle's Procedural Language Extension to SQL, the relational
database language.
*** PL/SQL combines the data manipulating power of SQL with the data
processing power of procedural languages.
*** A block (or sub-block) lets you group logically related declarations and
statements. The declarations are local to the block and cease to exist when
the block completes.
1
1. Declarative Part (Optional)
2. An Executable Part (Required)
3. An Exception Handling Part (Optional)
Following figure shows a PL/SQL block :-
[DECLARE
-- declarations]
BEGIN
-- statements
[EXCEPTION
-- handler]
END;
Q. What is an exception?
A. In PL/SQL, a warning or error condition is called an exception.
You use dot notation to reference fields, as the following example shows :-
my_deptno := dept_rec.deptno;
*** If you declare a cursor that retrieves the last name, salary, hire date, and job title of
3
an employee, you can use %ROWTYPE to declare a record that stores the same
information, as follows :-
DECLARE
CURSOR c1 IS SELECT ename, sal, hiredate, job FROM emp;
emp_rec c1%ROWTYPE; -- declare record variable that
-- represents a row in the emp table
When you execute the statement
FETCH c1 INTO emp_rec;
the value in the ename column of the emp table is assigned to the ename field of
emp_rec, the value in the sal column is assigned to the sal field, and so on. Following
fig. shows how the result might appear :-
Cursor variables are true PL/SQL variables, to which you can assign new values and
which you can pass to subprograms stored in an Oracle database. This gives you more
flexibility and a convenient way to centralize data retrieval.
Typically, you open a cursor variable by passing it to a stored procedure that declares a
cursor variable as one of its formal parameters. The following packaged procedure
opens the cursor variable generic_cv for the chosen query:
5
OPEN generic_cv FOR SELECT * FROM salgrade;
END IF;
END open_cv;
END emp_data;
In the following example, you write a PL/SQL standalone function named interp
that registers the C routine c_interp as an external function:
Q. What is modularity?
A. Modularity lets you break an application down into manageable, well-defined logic
modules. Through successive refinement, you can reduce a complex problem to a set
of simple problems that have easy-to-implement solutions.
PL/SQL has collection types - TABLE and VARRAY which allow you to declare
nested tables and variable-size arrays (varrays for shor )
To reference an element, you use standard subscripting syntax. For example, the
following call references the fifth element in the nested table (of type Staff) returned
by function new_hires:
DECLARE
TYPE Staff IS TABLE OF Employee;
staffer Employee;
FUNCTION new_hires (hiredate DATE) RETURN Staff IS
BEGIN
...
END;
BEGIN
staffer := new_hires('10-NOV-96')(5);
...
END;
Records contain uniquely named fields, which can have different datatypes. Suppose
you have various data about an employee such as name, salary, and hire date. These
items are dissimilar in type but logically related. A record containing a field for each
item lets you treat the data as a logical unit. Consider the following example:
DECLARE
TYPE TimeRec IS RECORD (minutes SMALLINT, hours SMALLINT);
TYPE MeetingTyp IS RECORD (
day DATE,
7
time TimeRec, -- nested record
place VARCHAR2(20),
purpose VARCHAR2(50));
Notice that you can nest records. That is, a record can be the component of another
record.
Object types reduce complexity by breaking down a large system into logical entities.
This allows you to create software components that are modular, maintainable, and
reusable.
);
At run time, when the data structure is filled with values, you have created an instance
of an abstract bank account. You can create as many instances (called objects) as you
need. Each object has the number, balance, and status of an actual bank account.
These two environments are independent. PL/SQL might be available in the Oracle
Server but unavailable in tools, or the other way around.
In either environment, the PL/SQL accepts as input any valid PL/SQL block or
subprogram. Following shows the PL/SQL engine processing an anonymous block.
The PL/SQL executes procedural statements but sends SQL statements to the SQL
Statement Executor in the Oracle Server.
Furthermore, if the block contains no SQL statements, the engine executes the entire
block at the application site. This is useful if your application can benefit from
conditional and iterative control.
Frequently, Oracle Forms applications use SQL statements merely to test the value of
field entries or to do simple computations. By using PL/SQL instead, you can avoid
calls to the Oracle Server.
Moreover, you can use PL/SQL functions to manipulate field entries.
9
Q. How does PL/SQL result in better performance?
A. 1. Processing of SQL statements
Without PL/SQL, Oracle must process SQL statements one at a time. Each SQL
statement results in another call to Oracle and higher performance overhead. In a
networked environment, the overhead can become significant. Every time a SQL
statement is issued, it must be sent over the network, creating more traffic.
However, with PL/SQL, an entire block of statements can be sent to Oracle at one
time. This can drastically reduce communication between your application and
Oracle. Following figure shows that, if your application is database intensive, you
can use PL/SQL blocks, control structures and subprograms to group SQL
statements before sending them to Oracle for execution. e.g. to execute 10 individual
statements, 10 calls are required. But to execute a subprogram containing 10 SQL
statements, only one call is required.
Q. How is PL/SQL integrated with ORACLE? What are the advantages of this
integration?
A. Both PL/SQL and Oracle are based on SQL.
Moreover, PL/SQL supports all the SQL datatypes.
Combined with the direct access that SQL provides, these shared datatypes
integrate PL/SQL with the Oracle data dictionary.
The %TYPE and %ROWTYPE attributes further integrate PL/SQL with the data
dictionary. For example, you can use the %TYPE attribute to declare variables,
basing the declarations on the definitions of database columns. If a definition changes,
the variable declaration changes accordingly at run time. This provides
1. Data Independence
2. Reduces Maintenance Costs
3. Allows programs to adapt as the database changes to meet new business needs.
FUNDAMENTALS
There are six essentials in painting. The first is called spirit; the second, rhythm; the third,
thought; the fourth, scenery; the fifth, the brush; and the last is the ink.
*** You cannot embed spaces in lexical units except for string literals and
comments.
For example, the following line is illegal because the compound symbol
for assignment (:=) is split :-
count : = count + 1; -- illegal
Q. What is a delimiter?
A. A delimiter is a simple or compound symbol that has a special meaning to PL/SQL.
11
e.g., you use delimiters to represent arithmetic operations such as addition and
subtraction.
Simple symbols :-
Simple symbols consist of one character. e.g. :-
+ addition operator
- subtraction/negation operator
* multiplication operator
/ division operator
= relational operator
< relational operator
> relational operator
( expression or list delimiter
) expression or list delimiter
; statement terminator
% attribute indicator
, item separator
. component selector
@ remote access indicator
' character string delimiter
" quoted identifier delimiter
: host variable indicator
Compound Symbols :-
Compound symbols consist of two characters. e.g. :-
** exponentiation operator
<> relational operator
!= relational operator
~= relational operator
^= relational operator
<= relational operator
>= relational operator
:= assignment operator
=> association operator
.. range operator
|| concatenation operator
<< (beginning) label delimiter
>> (ending) label delimiter
-- single-line comment indicator
/* (beginning) multi-line comment delimiter
*/ (ending) multi-line comment delimiter
Q. What is an identifier?
A. Identifiers are used to name PL/SQL program items and units, which include
constants, variables, exceptions, cursors, cursor variables, subprograms, and
packages.
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 illegal.
*** Adjoining and trailing dollar signs, underscores, and number signs in an
identifier are legal. e.g. :-
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.
So, if the only difference between identifiers is the case of corresponding letters,
PL/SQL considers the identifiers to be the same. e.g. :-
lastname
LastName -- same as lastname
LASTNAME -- same as lastname and LastName
*** Typically, reserved words are written in upper case to promote readability.
However, like other PL/SQL identifiers, reserved words can be written in
lower or mixed case
13
Q. What is the max length of a quoted identifier?
A. The maximum length of a quoted identifier is 30 characters not counting the double
quotes.
*** Using PL/SQL reserved words as quoted identifiers is allowed but not
recommended.
Q. What is a literal?
A. 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.
*** PL/SQL considers numbers such as 12.0 and 25. to be reals even though
they have integral values.
*** PL/SQL is case sensitive within character literals. e.g., PL/SQL considers
the literals 'Z' and 'z' to be different.
*** The character literals '0' .. '9' are not equivalent to integer literals.
Character literals cannot used in arithmetic expressions.
*** PL/SQL is case sensitive within string literals. e.g., PL/SQL considers
the following literals to be different :-
'baker'
'Baker'
15
A. The PL/SQL compiler ignores comments.
Adding comments to your program promotes readability and aids
understanding.
PL/SQL supports two comment styles :-
1. Single-line :-
Single-line comments begin with a double hyphen (--) anywhere
on a line and extend to the end of the line. e.g. :-
-- begin processing
SELECT sal INTO salary FROM emp -- get current salary
WHERE empno = emp_id;
bonus := salary * 0.15; -- compute bonus amount
Notice that comments can appear within a statement at the end of a line.
2. Multi-line :-
Multi-line comments begin with a slash-asterisk (/*), end with an asterisk-
slash (*/), and can span multiple lines. e.g. :-
BEGIN
/* Compute a 15% bonus for top-rated employees. */
IF rating > 90 THEN
bonus := salary * 0.15 /* bonus is based on salary */
ELSE
bonus := 0;
END If;
...
/* The following line computes the area of a circle using pi,
which is the ratio between the circumference and diameter. */
area := pi * radius**2;
Q. What is a subtype?
A. A subtype associates a base type with a constraint and so defines a subset of values.
17
BINARY_INTEGER values require less storage than NUMBER values.
So using BINARY_INTEGER variables can boost performance.
*** Remember, you specify the maximum length of a CHAR(n) variable in bytes,
not characters. So, if a CHAR(n) variable stores multi-byte characters, its
maximum length is less than n characters.
*** You can insert any CHAR(n) value into a LONG database column because the
maximum width of a LONG column is 2147483647 bytes or 2 gigabytes.
*** You cannot retrieve a value longer than 32767 bytes from a LONG column into
a CHAR(n) variable.
19
The maximum width of a VARCHAR2 database column is 4000 bytes. Therefore,
you cannot insert VARCHAR2 values longer than 4000 bytes into a VARCHAR2
column.
*** You can insert any VARCHAR2(n) value into a LONG database column
because the maximum width of a LONG column is 2147483647 bytes.
*** You cannot retrieve a value longer than 32767 bytes from a LONG column
into a VARCHAR2(n) variable.
*** You can insert any LONG value into a LONG database column because the
maximum width of a LONG column is 2147483647 bytes.
*** You cannot retrieve a value longer than 32760 bytes from a LONG column
into a LONG variable.
*** Although the maximum length of a RAW variable is 32767 bytes, the maximum
width of a RAW database column is 255 bytes. So, you cannot insert RAW
values longer than 255 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 2147483647 bytes.
However, you cannot retrieve a value longer than 32767 bytes from a LONG
RAW column into a RAW variable.
*** You can insert any LONG RAW value into a LONG RAW database column
because the maximum width of a LONG RAW column is 2147483647 bytes.
*** You cannot retrieve a value longer than 32760 bytes from a LONG RAW
column into a LONG RAW variable.
21
WHERE clause of an UPDATE or DELETE statement to identify the latest row
fetched from a cursor.
*** The default date format is set by the Oracle initialization parameter
NLS_DATE_FORMAT. e.g., the default might be 'DD-MON-YY'.
*** If PL/SQl cannot determine which implicit conversion is needed, you get a
compilation error.
*** By default, variables are initialized to NULL. e.g., the following declarations
are equivalent :-
birthday DATE;
birthday DATE := NULL;
*** 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. e.g., the
following declaration is illegal :-
acct_id INTEGER(5) NOT NULL; -- illegal; not initialized
*** In constant declarations, the keyword CONSTANT must precede the type
specifier, as the following example shows :-
credit_limit CONSTANT REAL := 5000.00;
Q. What is elaboration?
A. The processing of a declaration by the PL/SQL compiler is called elaboration.
23
valid BOOLEAN DEFAULT FALSE;
*** A NOT NULL column constraint does not apply to variables declared
using %TYPE. In the following example, even though the database column
empno is defined as NOT NULL, you can assign a null to the variable
my_empno :-
DECLARE
my_empno emp.empno%TYPE;
...
BEGIN
my_empno := NULL; -- this works
...
END;
Q. How can you assign values to all fields in a %ROWTYPE record at once?
A. There are two ways to assign values to all fields in a record at once.
1. PL/SQL allows aggregate assignment between entire records if their declarations
refer to the same table or cursor. For example, the following assignment is legal :-
DECLARE
dept_rec1 dept%ROWTYPE;
dept_rec2 dept%ROWTYPE;
CURSOR c1 IS SELECT deptno, dname, loc FROM dept;
dept_rec3 c1%ROWTYPE;
BEGIN
...
dept_rec1 := dept_rec2;
However, because dept_rec2 is based on a table and dept_rec3 is based on a cursor,
the following assignment is illegal :-
dept_rec2 := dept_rec3; -- illegal
2. You can assign a list of column values to a record by using the SELECT or FETCH
statement, as the example below 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 dept%ROWTYPE;
...
BEGIN
SELECT deptno, dname, loc INTO dept_rec FROM dept
WHERE deptno = 30;
25
Q. Can you assign a list of column values to a %ROWTYPE record by using an
assignment statement?
A. No. e.g. the following syntax is illegal :-
record_name := (value1, value2, value3, ...); -- illegal
*** You can retrieve entire records, you cannot insert or update them.
e.g., the following statement is illegal :-
INSERT INTO dept VALUES (dept_rec); -- illegal
Q. Does PL/SQL allow you to declare a list of variables that have the same
datatype?
A. No. e.g., the following declaration is illegal :-
i, j, k SMALLINT; -- illegal
The legal version are as follows :-
i SMALLINT;
j SMALLINT;
k SMALLINT;
Q. In how many can you specify the names of PL/SQL program objects?
A. Names can be simple, qualified, remote, or both qualified and remote. e.g., you
can use the procedure name raise_salary in any of the following ways :-
raise_salary(...); -- simple
emp_actions.raise_salary(...); -- qualified
raise_salary@newyork(...); -- remote
emp_actions.raise_salary@newyork(...); -- qualified and remote
*** You cannot create synonyms for items declared within subprograms or
packages. That includes constants, variables, cursors, cursor variables,
exceptions, and packaged procedures.
27
END LOOP;
Likewise, the following SELECT statement fails because PL/SQL assumes that
emp refers to the formal parameter :-
PROCEDURE calc_bonus (emp NUMBER, bonus OUT REAL) IS
avg_sal REAL;
BEGIN
SELECT AVG(sal) INTO avg_sal FROM emp WHERE ...
In such cases, you can prefix the table name with a username,as follows :-
PROCEDURE calc_bonus (emp NUMBER, bonus OUT REAL) IS
avg_sal REAL;
BEGIN
SELECT AVG(sal) INTO avg_sal FROM scott.emp WHERE ...
However, it is better programming practice is to rename the variable or
formal parameter:
Q. Can you use a subprogram name to qualify references to local variables and
formal parameters?
A. Yes. e.g. :-
FUNCTION bonus (deptno IN NUMBER, ...) RETURN REAL IS
job CHAR(10);
BEGIN
SELECT ... WHERE deptno = bonus.deptno AND job = bonus.job;
DECLARE DECLARE
----------------. x REAL;
x REAL; | -----------------.
BEGIN | BEGIN |
... | ... |
DECLARE | DECLARE |
Outer x x REAL; | ____________ .
BEGIN | x REAL;
... | BEGIN
END; | ...
... | END;
END; | ----------------.
____________. ... |
END; |
____________.
DECLARE DECLARE
x REAL; x REAL;
BEGIN BEGIN
... ...
DECLARE DECLARE
Inner x --------------. x REAL;
x REAL; | --------------.
BEGIN | BEGIN |
... | ... |
END; | END; |
____________. __________.
END; ...
END;
*** Identifiers declared in a PL/SQL block are considered local to that block
and global to all its sub-blocks.
29
*** If a global identifier is redeclared in a sub-block, both identifiers remain in
scope. Within the sub-block, however, only the local identifier is visible
because you must use a qualified name to reference the global identifier.
Q. Can a block reference identifiers declared in other blocks at the same level?
A. No. it is because those identifiers are neither local nor global to the block. e.g. :-
DECLARE
a CHAR;
b REAL;
BEGIN
-- identifiers available here: a (CHAR), b
DECLARE
a INTEGER;
c REAL;
BEGIN
-- identifiers available here: a (INTEGER), b, c
END;
DECLARE
d REAL;
BEGIN
-- identifiers available here: a (CHAR), b, d
END;
-- identifiers available here: a (CHAR), b
END;
See that a block cannot reference identifiers declared in other blocks nested at
the same level.
*** Within the same scope, a label and a subprogram cannot have the same name.
31
wages NUMBER(7,2);
BEGIN
...
SELECT ename, sal + comm INTO last_name, wages FROM emp
WHERE empno = emp_id;
Q. What is an operand?
A. An operand is a variable, constant, literal, or function call that contributes
a value to an expression. e.g. :-
-X / 2 + 3
*** Operators with higher precedence are applied first. e.g., both of the following
expressions yield 8 because division has a higher precedence than addition :-
5 + 12 / 4
12 / 4 + 5
*** Operators with the same precedence are applied in no particular order.
Q. How can you control the order of evaluation?
A. You can use parentheses to control the order of evaluation. e.g., the following
expression yields 7, not 11, because parentheses override the default operator
precedence :-
(8 + 6) / 2
In the following example, the subtraction is done before the division because the
most deeply nested subexpression is always evaluated first :-
100 + (20 / 5 + (7 - 3))
*** When you do not use parentheses to specify the order of evaluation, operator
precedence determines the order. Compare the following 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
33
expression is equivalent to
(NOT valid) AND done
*** Usually, PL/SQL stops evaluating a logical expression as soon as the result can
be determined. This allows you to write expressions that might otherwise cause
an error. Consider the following OR expression :-
DECLARE
...
on_hand INTEGER;
on_order INTEGER;
BEGIN
..
IF (on_hand = 0) OR (on_order / on_hand < 5) THEN
...
END IF;
END;
When the value of on_hand is zero, the left operand yields TRUE, so PL/SQL
need not evaluate the right operand. If PL/SQL were to evaluate both operands
before applying the OR operator, the right operand would cause a division by
zero error.
Q. What is IN operator?
A. The IN operator tests set membership. It means "equal to any member of.
The set can contain nulls, but they are ignored.
e.g., the following statement does not delete rows in which the ename column is null :-
DELETE FROM emp WHERE ename IN (NULL, 'KING', 'FORD');
*** value NOT IN set yield FALSE if the set contains a null. e.g., instead of deleting
rows in which the ename column is not null and not 'KING', the following
statement deletes no rows :-
DELETE FROM emp WHERE ename NOT IN (NULL, 'KING');
*** If both operands have datatype CHAR, the concatenation operator returns
a CHAR value. Otherwise, it returns a VARCHAR2 value.
35
complex expressions separated by relational operators.
Often, Boolean expressions are connected by the logical operators AND, OR,
and NOT.
A Boolean expression always yields TRUE, FALSE, or NULL.
Some Guidelines :-
1. In general, do not compare real numbers for exact equality or inequality.
Real numbers are stored as approximate values. e.g., the following IF
condition might not yield TRUE :-
count := 1;
IF count = 1.0 THEN ...
*** In the example below, you might expect the sequence of statements to execute
because x and y seem unequal. But, nulls are indeterminate. Whether or not x is
equal to y is unknown. Therefore, the IF condition yields NULL and the sequence
of statements is bypassed.
x := 5;
y := NULL;
...
IF x != y THEN -- yields NULL, not TRUE
sequence_of_statements; -- not executed
END IF;
*** In this example, you might expect the sequence of statements to execute because a
and b seem equal. But, again, that is unknown, so the IF condition yields NULL and
the sequence of statements is bypassed.
a := NULL;
b := NULL;
...
IF a = b THEN -- yields NULL, not TRUE
sequence_of_statements; -- not executed
END IF;
*** Applying the logical operator NOT to a null yields NULL. Thus, the following two
statements are not always equivalent :-
IF x > y THEN | IF NOT x > y THEN
37
high := x; | high := y;
ELSE | ELSE
high := y; | high := x;
END IF; | END IF;
The sequence of statements in the ELSE clause is executed when the IF condition
yields FALSE or NULL. So, if either or both x and y are null, the first IF statement
assigns the value of y to high, but the second IF statement assigns the value of x to
high. If neither x nor y is null, both IF statements assign the same value to high.
*** The SQL group functions AVG, MIN, MAX, COUNT, SUM, STDDEV,
and VARIANCE are not built into PL/SQL. Nevertheless, you can use them
in SQL statements (but not in procedural statements).
39
Outside a handler, SQLCODE always returns zero.
Q. What is TABLE?
A. TABLE is a composite datatype in PL/SQL.
*** Future versions of PL/SQL will allow PL/SQL tables to have multiple, named
columns & composite primary keys of any type.
*** PL/SQL tables follow the usual scoping and instantiation rules.
Q. What is instantiation?
A. It is the creation of a new instance of a program object. In a package,
PL/SQL tables are instantiated when you first reference the package and
cease to exist when you exit the application or end the database session.
In a block or subprogram, they are instantiated when you enter the block or
subprogram and cease to exist when you exit the block or subprogram.
41
like syntax :-
plsql_table_name(primary_key_value)
here primary_key_value belongs to type BINARY_INTEGER. e.g., you will
reference the 3rd row in PL/SQL table ename_tab as follows :-
ename_tab(3) ...
Example 1. :-
Here you use a Cursor FOR loop to load two PL/SQL tables :-
DECLARE
TYPE EnameTabType IS TABLE OF emp.ename%TYPE
INDEX BY BINARY_INTEGER;
TYPE SalTabTyp IS TABLE OF emp.sal%TYPE
INDEX BY BINARY_INTEGER;
ename_tab EnameTabTyp;
sal_tab SalTabTyp;
i BINARY_INTEGER := 0;
...
BEGIN
-- load employee names & salaries into PL/SQL tables
FOR emprec IN (SELECT ename, sal FROM emp) LOOP
i := i + 1;
ename_tab(i) := emprec.ename;
sal_tab(i) := emprec.sal;
END LOOP;
-- process the tables
process_sals(ename_tab, sal_tab);
END;
Example 2. :-
Here you use dot notation to reference the package emp_actions :-
DECLARE
...
i BINARY_INTEGER := 0;
BEGIN
-- load employee names into a PL/SQL table
FOR emprec IN (SELECT ename FROM emp) LOOP
i := i + 1;
emp_actions.ename_tab(i) := emprec.ename;
END LOOP;
-- process the PL/SQL table
emp_actions.log_names(emp_actions.ename_tab, i);
...
END;
*** The size of a PL/SQL table is unconstrained. If you want to maintain a row
count, you must declare a variable to store the no. of rows currently in
PL/SQL table.
43
...
EXCEPTION
WHEN STORAGE_ERROR THEN
-- comes here eventually
END;
With each iteration of the loop, another row is added to the PL/SQL table. The loop
cannot complete because it contains no EXIT WHEN statement. Eventually, PL/SQL
runs out of memory and raises the predefined exception STORAGE_ERROR.
Q. How can we insert values from a PL/SQL table into a database column?
A. You must use a loop. e.g., consider the following declarations :-
DECLARE
TYPE EnameTabType IS TABLE OF CHAR(10)
INDEX BY BINARY_INTEGER;
TYPE EmpnoTabTyp IS TABLE OF NUMBER(4)
INDEX BY BINARY_INTEGER;
ename_tab EnameTabTyp;
empno_tab EmpnoTabTyp;
...
You can use the following procedure to INSERT values from PL/SQL tables
into the emp database table :-
PROCEDURE insert_emp_data
(rows BINARY_INTEGER,
empno_tab EmpnoTabTyp,
ename_tab EnameTabTyp,
...) IS
BEGIN
FOR i IN 1..rows LOOP
INSERT INTO emp (empno, ename, ...)
VALUES (empno_tab(i), ename_tab(i), ...);
END LOOP;
Q. How can we fetch values from a database column into a PL/SQL table?
A. You must use a loop. e.g. : -
PROCEDURE fetch_emp_data
(rows OUT BINARY_INTEGER,
empno_tab OUT EmpnoTabTyp,
ename_tab OUT EnameTabTyp,
...) IS
BEGIN
rows := 0;
FOR emprec in (SELECT * FROM emp) LOOP
rows := rows + 1;
empno_tab(rows) := emprec.empno;
ename_tab(rows) := emprec.ename;
...
END LOOP;
45
...
END;
*** User-defined records follow the usual scoping and instantiation rules.
Q. What is instantiation?
A. It is the creation of a new instance of a program object. In a package,
RECORDS are instantiated when you first reference the package and
cease to exist when you exit the application or end the database session.
In a block or subprogram, they are instantiated when you enter the block or
subprogram and cease to exist when you exit the block or subprogram.
47
Q. Can you assign the value of a PL/SQL expression to a specific field?
A. Yes. e.g. :-
emp_rec.ename := UPPER(emp_rec.ename);
Q. Can you assign one nested record to another if they belong to the same type?
A. Yes. e.g. :-
seminar.time := meeting.time;
Such assignments are allowed even if the containing records belong to different
49
datatypes. e.g. :-
party.time := meeting.time;
Control Structures
One ship drives east and another drives west
With the selfsame winds that blow.
'Tis the set of the sails and not the gales
Which tells us the way to go.
Q. What is a condition?
A. A condition is any variable or expression that returns a Boolean value (TRUE,
FALSE, or NULL).
Q. What is the main characteristic of the three basic control structures defined
above?
A. These have single entry and single exit point. Their proper use leads to a
well-structured program.
Q. What is conditional control IF statement?
A. Often, it is necessary to take alternative actions depending on circumstances.
The IF statement lets you execute a sequence of statements conditionally. That is,
whether the sequence is executed or not depends on the value of a condition.
Q. Explain IF-THEN.
A. It is the simplest form of IF statement. It associates a condition with a sequence of
statements enclosed by the keywords THEN and END IF (not ENDIF). e.g. :-
IF condition THEN
sequence_of_statements;
END IF;
The sequence of statements is executed only if the condition yields TRUE. If the
condition yields FALSE or NULL, the IF statement does nothing. In either
case, control passes to the next statement. e.g. :-
IF sales > quota THEN
compute_bonus(empid);
UPDATE payroll SET pay = pay + bonus WHERE empno = emp_id;
END IF;
Q. Explain IF-THEN-ELSE.
A. This form of IF statement adds the keyword ELSE followed by an alternative
sequence of statements. e.g. :-
IF condition THEN
sequence_of_statements1;
ELSE
sequence_of_statements2;
END IF;
The sequence of statements in the ELSE clause is executed only if the condition
yields FALSE or NULL. Thus, the ELSE clause ensures that a sequence of
statements is executed. In the following example, the first or second UPDATE
statement is executed when the condition is true or false, respectively :-
IF trans_type = 'CR' THEN
UPDATE accounts SET balance = balance + credit WHERE ...
ELSE
UPDATE accounts SET balance = balance - debit WHERE ...
END IF;
51
Q. Can THEN and ELSE clauses include IF statements?
A. Yes, i.e. IF statements can be nested. e.g. :-
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;
Q. Explain IF-THEN-ELSIF.
A. This form of IF statement is used to select an action from several mutually
exclusive alternatives. It uses the keyword ELSIF (not ELSEIF) to introduce
additional conditions. e.g. :-
IF condition1 THEN
sequence_of_statements1;
ELSIF condition2 THEN
sequence_of_statements2;
ELSE
sequence_of_statements3;
END IF;
If the first condition yields 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 yields
TRUE, its associated sequence of statements is executed and control passes to the next
statement. If all conditions yield FALSE or NULL, the sequence in the ELSE clause is
executed. e.g. :-
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;
2. When possible, use the ELSIF clause instead of nested IF statements. That way,
code will be easier to read and understand. Compare the following IF statements :-
IF condition1 THEN | IF condition1 THEN
statement1; | statement1;
ELSE | ELSIF condition2 THEN
IF condition2 THEN | statement2;
statement2; | ELSIF condition3 THEN
ELSE | statement3;
IF condition3 THEN | END IF;
statement3; |
END IF; |
END IF; |
END IF; |
These statements are logically equivalent, but the first statement obscures the flow of
logic, whereas the second statement reveals it.
53
2.WHILE-LOOP
3. FOR-LOOP
*** When you nest labeled loops, you can use ending label names to improve
55
readability.
Q. Can you use EXIT statement to complete any enclosing loop along with current
loop?
A. Yes, 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. e.g. :-
<<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.
Q. Explain WHILE-LOOP.
A. The WHILE-LOOP statement associates a condition with a sequence of
statements enclosed by the keywords LOOP and END LOOP. e.g. :-
WHILE condition LOOP
sequence_of_statements;
END LOOP;
Before each iteration of the loop, the condition is evaluated. If the condition yields
TRUE, the sequence of statements is executed, then control resumes at the top of the
loop. If the condition yields FALSE or NULL, the loop is bypassed and control passes
to the next statement. e.g. :-
WHILE total <= 25000 LOOP
...
SELECT sal INTO salary FROM emp WHERE ...
total := total + salary;
END LOOP;
*** Some languages have a LOOP UNTIL or REPEAT UNTIL structure, which
tests the condition at the bottom of the loop instead of at the top. Therefore,
the sequence of statements is executed at least once. PL/SQL has no such
structure.
Q. How can you build a LOOP UNTIL or REPEAT UNTIL structure in PL/SQL?
A. This is as follows :-
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. e.g. :-
done := FALSE;
WHILE NOT done LOOP
sequence_of_statements;
done := boolean_expression;
END LOOP;
A statement inside the loop must assign a new value to the Boolean variable.
Otherwise, you have an infinite loop.
*** The number of iterations through a WHILE loop is unknown until the loop
completes.
Q. Explain FOR-LOOP.
A. The number of iterations through a FOR loop is known before the loop is entered.
FOR loops iterate over a specified range of integers. The syntax is as follows :-
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.The sequence of statements is executed once for each integer in the
range. After each iteration, the loop counter is incremented. e.g. :-
FOR i IN 1..3 LOOP -- assign the values 1,2,3 to i
sequence_of_statements; -- executes three times
END LOOP;
Q. What will happen if lower bound equals the higher bound in a FOR loop?
A. If the lower bound equals the higher bound, the sequence of statements is executed
once. e.g. :-
FOR i IN 3..3 LOOP -- assign the value 3 to i
sequence_of_statements; -- executes one time
END LOOP;
57
the higher bound to the lower bound In the following example, after each iteration,
the loop counter is decremented :-
FOR i IN REVERSE 1..3 LOOP -- assign the values 3,2,1 to i
sequence_of_statements; -- executes three times
END LOOP;
Nevertheless, you write the range bounds in ascending (not descending) order.
Q. Some languages provide a STEP clause, which lets you specify a different
increment. PL/SQL has no such structure. How can you build such one?
A. This is as follows :-
FOR j IN 5..15 LOOP -- assign values 5,6,7,... to j
IF MOD(j, 5) = 0 THEN -- pass multiples of 5
sequence_of_statements; -- j has values 5,10,15
END IF;
END LOOP;
Q. Can we determine the loop range in a FOR loop dynamically at run time?
A. Yes, PL/SQL lets you determine the loop range dynamically at run time. e.g. :-
SELECT COUNT(empno) INTO emp_count FROM emp;
FOR i IN 1..emp_count LOOP
...
END LOOP;
The value of emp_count is unknown at compile time.The SELECT statement returns
the value at run time.
Q. What happens if the lower bound of a loop range evaluates to a larger integer
than the upper bound?
A. Then the sequence of statements within the loop is not executed and control passes to
the next statement. e.g :-
-- limit becomes 1
FOR i IN 2..limit LOOP
sequence_of_statements; -- executes zero times
END LOOP;
-- control passes here
59
END LOOP;
END main;
*** The same scope rules apply to nested FOR loops. Consider the example below.
Both loop counters have the same name. So, to reference the outer loop counter
from the inner loop, you must use a label and dot notation, as follows :-
<<outer>>
FOR step IN 1..25 LOOP
FOR step IN 1..10 LOOP
...
IF outer.step > 15 THEN ...
END LOOP;
END LOOP outer;
Example 2.
Here you go to a PL/SQL block farther up in a sequence of statements :-
BEGIN
...
<<update_row>>
BEGIN
UPDATE emp SET ...
...
END;
...
GOTO update_row;
...
END;
Q. What is the problem in the following example. How can you remove this
problem?
A. DECLARE
done BOOLEAN;
BEGIN
...
FOR i IN 1..50 LOOP
IF done THEN
GOTO end_loop;
END IF;
61
...
<<end_loop>> -- illegal
END LOOP; -- not an executable statement
END;
The label <<end_loop>> in the above example is illegal because it does not precede an
executable statement.
To solve it, simply add the NULL statement, as follows :-
DECLARE
done BOOLEAN;
BEGIN
...
FOR i IN 1..50 LOOP
IF done THEN
GOTO end_loop;
END IF;
...
<<end_loop>>
NULL; -- an executable statement
END LOOP;
END;
Q. Can a GOTO statement branch to an enclosing block from the current block?
A. Yes. e.g. :-
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;
Here, GOTO statement branches to the first enclosing block in which the referenced
label appears.
63
<<update_row>>
UPDATE emp SET ...
END;
Q. Can a GOTO statement branch from an exception handler into the current
block?
A. No. e.g., the following GOTO statement is illegal :-
DECLARE
...
pe_ratio REAL;
BEGIN
...
SELECT price / NVL(earnings, 0) INTO pe_ratio FROM ...
<<insert_row>>
INSERT INTO stats VALUES (pe_ratio, ...);
EXCEPTION
WHEN ZERO_DIVIDE THEN
pe_ratio := 0;
GOTO insert_row; -- illegal branch into current block
END;
*** Each clause in an IF statement must contain at least one executable statement.
The NULL statement meets this requirement. So, you can use the NULL
statement in clauses that correspond to circumstances in which no action is
taken. e.g. :-
IF rating > 90 THEN
compute_bonus(emp_id);
ELSE
NULL;
END IF;
Q. What is a stub?
A. A stub is dummy subprogram that allows you to defer the definition of a procedure
or function until you test and debug the main program.
65
Q. How does PL/SQL manipulate Oracle data?
A. PL/SQL manipulate Oracle data because it supports all SQL
1. Data manipulation commands
2. Transaction control commands
3. Functions
4. Pseudocolumns
5. Operators
Q. What is a transaction?
A. A transaction is a series of SQL data manipulation statements that does a logical
unit of work.
OR
A transaction is a series of SQL data manipulation statements that takes Oracle
database from one consistent state to another consistent state.
Q. What is a sequence?
A. A sequence is a database object that generates sequential numbers.
67
When you create a sequence, you can specify its initial value and an increment.
You specify the direction in which the query walks the tree (down from the root or
up from the branches) with the PRIOR operator.
In the START WITH clause, you specify a condition that identifies the root of the
tree.
*** PL/SQL provides a datatype also named ROWID. You can use variables of
type ROWID to store rowids in a readable format.
*** When you select or fetch a rowid into a ROWID variable, you can use the
function ROWIDTOCHAR, which converts the binary value to an 18-byte
character string.
*** The value of ROWNUM increases only when a row is retrieved. So the only
meaningful use of ROWNUM in a WHERE clause is :-
... WHERE ROWNUM < constant;
e.g. the following condition can not be met because the first nine rows are never
retrieved :-
... WHERE ROWNUM = 10;
69
Q. What is an execution plan?
A. For every SQL statement, ORACLE optimizer generates an execution plan.
It is a series of steps that ORACLE takes to execute the statement.
To form network domain names, use dot notation and follow a path through the
tree structure from leaf to root. The domain name tells the name or location of
a network host and the type of organization it is. Consider the following figure :-
Here every database object is uniquely identified by its global object name. e.g. :-
BEGIN
SELECT ename, job INTO my_ename, my_job
FROM [email protected]
WHERE empno = my_empno;
...
Q. What is Internet?
A. Internet is a worldwide system of computer networks to exchange information.
*** The set of rows returned by a query can consist of zero, one, or multiple rows,
depending on how many rows meet search criteria.
71
Q. Where can you declare a cursor?
A. You can declare a cursor in the declarative part of any PL/SQL block, subprogram,
or package.
*** For cursors declared using the FOR UPDATE clause, the OPEN statement
also locks those rows.
*** Rows in the result set are not retrieved when the OPEN statement is executed.
Rather, the FETCH statement retrieves the rows.
*** The query associated with a cursor can reference PL/SQL variables within its
scope. However, any variables in the query are evaluated only when the cursor
is opened.
73
DECLARE
CURSOR c1 (name VARCHAR2, salary NUMBER) IS SELECT ...
Any of the following statements opens the cursor :-
OPEN c1(emp_name, 3000);
OPEN c1('ATTLEY', 1500);
OPEN c1(emp_name, salary);
*** In the above example, when the identifier salary is used in the cursor declaration, it
refers to the formal parameter. But, when it is used in the OPEN statement, it refers
to the PL/SQL variable. To avoid confusion, use unique identifiers.
*** Unless you want to accept default values, each formal parameter in the cursor
declaration must have a corresponding actual parameter in the OPEN statement.
*** Formal parameters declared with a default value need not have a corresponding
actual parameter. They can simply assume their default values when the OPEN
statement is executed.
*** You can associate the actual parameters in an OPEN statement with the formal
parameters in a cursor declaration using positional or named notation.
*** The datatypes of each actual parameter and its corresponding formal parameter
must be compatible.
Q. How can you change the result/active set or values of variables in a cursor's
query?
A. First close and then reopen the cursor with the input variables set to their new values.
Q. Can we use different INTO list on separate fetches with the same cursor?
A. Yes. e.g. :-
DECLARE
CURSOR c1 IS SELECT ename FROM emp;
name1 emp.ename%TYPE;
name2 emp.ename%TYPE;
name3 emp.ename%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;
END;
Q. What happens if you fetch past the last row in the result/active set?
A. If you execute a FETCH but there are no more rows left in the active set,
the values of the target variables (i.e. fetch-list variables) are indeterminate.
*** Once a cursor is closed, you can reopen it. Any other operation on a closed
cursor raises the predefined exception INVALID_CURSOR.
75
*** If a cursor or cursor variable is not open, referencing it with %NOTFOUND
raises INVALID_CURSOR.
*** Eventually, the FETCH statement must fail to return a row. So when that
happens, no exception is raised.
*** If a cursor or cursor variable is not open, referencing it with %FOUND raises
the predefined exception INVALID_CURSOR.
*** After the first FETCH, if the result set was empty, %FOUND yields FALSE,
%NOTFOUND yields TRUE, and %ROWCOUNT yields 0.
77
Q. What are implicit cursors?
A. Oracle implicitly opens a cursor to process each SQL statement not associated with
an explicitly declared cursor. PL/SQL lets you refer to the most recent implicit cursor
as the "SQL" cursor.
Q. Can we use OPEN, FETCH & CLOSE statements to control SQL cursor?
A. No. But, you can use cursor attributes to get information about the most recently
executed SQL statement.
*** Before Oracle opens the SQL cursor automatically, the implicit cursor
attributes yield NULL.
1. If a SELECT INTO statement fails to return a row, PL/SQL raises the predefined
exception NO_DATA_FOUND whether you check %NOTFOUND on the next
line or not. e.g. :-
BEGIN
...
SELECT sal INTO my_sal FROM emp WHERE empno = my_empno;
-- might raise NO_DATA_FOUND
IF SQL%NOTFOUND THEN -- condition tested only when false
... -- this action is never taken
END IF;
EXCEPTION
...
END;
The check is useless because the IF condition is tested only when %NOTFOUND
is false. When PL/SQL raises NO_DATA_FOUND, normal execution stops and
control transfers to the exception-handling part of the block. In this case,
%NOTFOUND is useful in the OTHERS exception handler. Therefore, instead of
coding a NO_DATA_FOUND handler, find out if that exception was raised by
checking %NOTFOUND. e.g. :-
BEGIN
...
SELECT sal INTO my_sal FROM emp WHERE empno = my_empno;
-- might raise NO_DATA_FOUND
EXCEPTION
WHEN OTHERS THEN
IF SQL%NOTFOUND THEN -- check for 'no data found'
...
END IF;
...
END;
2. A SELECT INTO statement that calls a SQL group function never raises
NO_DATA_FOUND because group functions always return a value or a null.
In such cases, %NOTFOUND yields FALSE. e.g. :-
BEGIN
...
SELECT MAX(sal) INTO my_sal FROM emp WHERE deptno = my_deptno;
-- never raises NO_DATA_FOUND
IF SQL%NOTFOUND THEN -- always tested but never true
... -- this action is never taken
END IF;
79
EXCEPTION
WHEN NO_DATA_FOUND THEN ... -- never invoked
...
END;
*** 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 the query.
In such a situation, %ROWCOUNT is useful in the OTHERS exception
handler. Therefore, instead of coding a TOO_MANY_ROWS handler,
find out if the exception TOO_MANY_ROWS was raised by checking
%ROWCOUNT. e.g. :-
DECLARE
my_sal NUMBER(7,2);
my_ename CHAR(10);
BEGIN
...
SELECT sal INTO my_sal FROM emp WHERE ename = my_ename;
-- might raise TOO_MANY_ROWS
...
EXCEPTION
WHEN OTHERS THEN
IF SQL%ROWCOUNT > 0 THEN -- check for 'too many rows'
...
END IF;
...
END;
*** See in above example, the cursor specification has no SELECT statement
because the RETURN clause defines the datatype of the result value.
However, the cursor body must have a SELECT statement and the same
RETURN clause as the cursor specification.
Also, the number and datatypes of select items in the SELECT statement must
match the RETURN clause.
81
CURSOR c1 RETURN emp%ROWTYPE IS
SELECT * FROM emp WHERE deptno = 20; -- new WHERE clause
...
END emp_actions;
*** When the cursor FOR loop is entered, the cursor name cannot belong to a
cursor that was already opened by an OPEN statement or by an enclosing
cursor FOR loop.
*** You need not declare a cursor because PL/SQL lets you substitute a subquery.
e.g. :-
DECLARE
bonus REAL;
BEGIN
FOR emp_rec IN (SELECT empno, sal, comm FROM emp) LOOP
bonus := (emp_rec.sal * 0.05) + (emp_rec.comm * 0.25);
INSERT INTO bonuses VALUES (emp_rec.empno, bonus);
END LOOP;
COMMIT;
END;
83
INSERT INTO temp VALUES (high_paid, higher_comm,
'Total Wages: ' || TO_CHAR(total_wages));
COMMIT;
END;
Q. What is a session?
A. The jobs or tasks that Oracle manages are called sessions. A user session is started
when you run an application program or an Oracle tool and connect to Oracle.
Q. What is a deadlock?
A. A deadlock can occur when two or more users try to access the same schema object.
e.g., two users updating the same table might wait if each tries to update a row
currently locked by the other. Because each user is waiting for resources held by
another user, neither can continue until Oracle breaks the deadlock by signaling an
error to the last participating transaction.
The first SQL statement in your program begins a transaction. When one transaction
ends, the next SQL statement automatically begins another transaction. Thus, every
SQL statement is part of a transaction.
The COMMIT and ROLLBACK statements ensure that all database changes brought
about by SQL operations are either made permanent or undone at the same time. All
the SQL statements executed since the last commit or rollback make up the current
transaction. The SAVEPOINT statement names and marks the current point in the
processing of a transaction.
*** Until you commit the changes, other users cannot access the changed data.
They see the data as it was before you made the changes.
*** The COMMIT statement releases all row and table locks. It also erases
any savepoints marked since the last commit or rollback.
*** Just as a block can span multiple transactions, a transaction can span multiple
blocks.
85
statement fails, Oracle rolls it back automatically.
Oracle can also roll back single SQL statements to break deadlocks. Oracle signals
an error to one of the participating transactions and rolls back the current statement
in that transaction.
*** Before executing a SQL statement, Oracle must parse it. In other words, Oracle
examines the SQL statement to make sure that it follows syntax rules 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.
*** SAVEPOINT names and marks the current point in the processing of a
transaction. Used with the ROLLBACK TO statement, savepoints let you
undo parts of a transaction instead of the whole transaction.
*** When you roll back to a savepoint, any savepoints marked after that savepoint
are erased. However, the savepoint to which you roll back is not erased. e.g.,
if you mark five savepoints, then roll back to the third, only the fourth and fifth
are erased.
A simple rollback or commit erases all savepoints.
Very Important :-
If you mark a savepoint within a recursive subprogram, new instances of the
SAVEPOINT statement are executed at each level in the recursive descent.
However, you can only rollback to the most recently marked savepoint.
Q. What can happen if you neglect to commit or roll back a transaction explicitly?
A. A good programming practice is to commit or roll back every transaction explicitly.
If you neglect to commit or roll back a transaction explicitly, the host environment
determines its final state.
e.g., in the SQL*Plus environment, if your PL/SQL block does not include a
COMMIT or ROLLBACK statement, the final state of your transaction depends
on what you do after running the block. If you execute a data definition, data
control, or COMMIT statement 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.
87
This provides transaction-level read consistency. This ensures that a query sees only
changes committed before the current transaction began.
The SET TRANSACTION is written as follows :-
SET TRANSACTION READ ONLY
*** If a transaction is set 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.
If present, the FOR UPDATE clause must appear at the end of cursor declaration.
The FOR UPDATE clause identifies the rows that will be updated or deleted, then
locks each row in the result set. This is useful when you want to base an update on
the existing values in a row. In that case, you must make sure the row is not changed
by another user before the update.
The optional keyword NOWAIT tells Oracle not to wait if the table has been locked
by another user. Control is immediately returned to your program so that it can do
other work before trying again to acquire the lock. If you omit the keyword NOWAIT,
Oracle waits until the table is available. The wait has no limit unless the table is
remote, in which case the Oracle initialization parameter
DISTRIBUTED_LOCK_TIMEOUT sets a limit.
All rows are locked when you open the cursor, not as they are fetched.
The rows are unlocked when you commit or roll back the transaction.
So, 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. e.g., 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;
89
FETCH c1 INTO ...
...
UPDATE emp SET sal = new_sal WHERE CURRENT OF c1;
END LOOP;
*** Only if two different transactions try to modify the same row will one
transaction wait for the other to complete.
*** You can also use the %ROWTYPE attribute with cursors that reference the
ROWID 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;
91
Q. What is "program too large" compilation error?
A. PL/SQL was designed primarily for transaction processing. Therefore, the PL/SQL
compiler imposes a limit on program size. The limit depends on the mix of
statements in your PL/SQL program & the memory available on your platform.
Programs that exceed the limit cause a "program too large" compilation error.
2. Another solution is to break the block into two sub-blocks. Consider the
SQL*Plus script. Before the first block terminates, it inserts any data the second
block needs into a database table called temp. When the second block starts
executing, it selects the data from temp. This approximates the passing of
parameters from one procedure to another.
DECLARE
mode NUMBER;
median NUMBER;
BEGIN
...
INSERT INTO temp (col1, col2, col3)
VALUES (mode, median, 'blockA');
END;
/
DECLARE
mode NUMBER;
median NUMBER;
BEGIN
SELECT col1, col2 INTO mode, median FROM temp
WHERE col3 = 'blockA';
...
END;
/
Disadvantage :-
The above method works unless you must re-execute the first block while the
second block is still executing, or unless two or more users must run the script
concurrently.
Solution :-
To avoid these restrictions, you can embed the blocks in a host language such as
C, COBOL, or FORTRAN. That way, you can re-execute the first block using
flow-of-control statements. Also, you can store data in global host variables
instead of a database table.
Q. What is a database trigger?
A. A database trigger is a stored PL/SQL program associated with a specific
database table.
ORACLE executes (fires) the database trigger automatically whenever a given
SQL operation affects the table.
So, unlike subprograms, which must be invoked explicitly, database triggers are
invoked implicitly.
Q. How many database triggers you can associate with a given table?
A. 12
*** Database triggers fires with the privileges of the owner, not the current user.
So, the owner must have appropriate access to all objects referenced by the
trigger action.
93
/* trigger action */
IF :new.qty_on_hand < :new.reorder_point THEN
INSERT INTO pending_orders
VALUES (:new.part_no, :new.reorder_qty, SYSDATE);
END IF:
END;
Here trigger statement is UPDATE. If the trigger statement fails, it is rolled back.
The keyword AFTER specifies that database trigger fires after the update is done.
By default, a database trigger fires once per table. The FOR EACH ROW option
specifies that the trigger fires once per row. However, for the trigger to fire, the
bollean expression in the WHEN clause must evaluate to TRUE.
The prefix :new is a correlation name that refers to the newly updated column value.
Within a database trigger, you can reference :new & :old values of changing rows.
However, colon is not used in WHEN clause.
*** You can use REFERENCING clause to replace :new & :old with other
correlation names.
*** A trigger action can include calls to the built-in ORACLE procedure
raise_application_error, which lets you issue user-defined error messages.
ERROR HANDLING
There is nothing more exhilarating than to be shot at without result.
95
Q. What happens when an error occurs?
A. When an error occurs, an exception is raised. That is, normal execution stops and
control transfers to the exception-handling part of PL/SQL block or subprogram.
Q. Every Oracle error has a number. Then how is an internal exception handled?
A. Yes, every Oracle error has a number. But exceptions must be handled by name. So,
PL/SQL predefines some common Oracle errors as exceptions. e.g. PL/SQL raises
the predefined exception NO_DATA_FOUND if a SELECT INTO statement returns
no rows.
Q. How can we handle unnamed internal exceptions i.e. Oracle errors that have no
predefined names?
A. 1. Use OTHERS handler with error-reporting functions SQLCODE & SQLERRM.
These error-reporting functions return the ORACLE error code and message text.
2. Use the pragma EXCEPTION_INIT to associate exception names with ORACLE
error numbers.
97
tries to convert 'HALL' to a number :-
INSERT INTO emp (empno, ename, deptno) VALUES ('HALL', 7888, 20);
Q. Does a SELECT INTO statement that calls a group function will raise exception
NO_DATA_FOUND?
A. No. It is because SQL group functions such as AVG or SUM always return a value or
a null.
*** The FETCH statement is expected to return no rows eventually, so when that
happens, no exception is raised.
Q. Are, exceptions similar to variables. If yes, then what are the key differences
between the two?
A. Yes, exception and variable declarations are similar. The same scope rules apply to
variables and exceptions. However,
1. An exception is an error condition, not a data item.
2. Unlike variables, exceptions cannot appear in assignment statements or SQL
99
statements.
Q. What is a pragma?
A. A pragma is a compiler directive. Pragmas (also called pseudoinstructions) are
processed at compile time, not at run time. They do not affect the meaning of a
program. They simply convey information to the compiler.
10
1
Q. How are exceptions raised?
A. Predefined internal exceptions are raised implicitly by the runtime system.
Also, user-defined exceptions that you have associated with an Oracle error
number using EXCEPTION_INIT are also raised implicitly by the runtime system.
However, other user-defined exceptions must be raised explicitly by RAISE
statements.
*** However, exceptions cannot propagate across remote procedure calls (RPCs).
Therefore, a PL/SQL block cannot catch an exception raised by a remote
subprogram.
RULE 3.
If no exception handler is found for a raised exception then a runtime
unhandeled exception is returned to the host environment.
BEGIN
...
BEGIN
IF x = 1 THEN
RAISE A;
ELSIF x = 2 THEN
RAISE B;
ELSE
RAISE C; -- exception C is raised
END IF;
...
EXCEPTION
WHEN A THEN -- exception C is not handeled here(locally)
so propagates to the enclosing block
...
10
3
END;
...
EXCEPTION
WHEN B THEN -- exception C is not handled here, so a runtime
unhandeled exception is returned to the host
environment.
...
END;
Q. Can an exception propagate beyond its scope i.e. beyond the block in which
it was declared?
A. Yes, e.g.
BEGIN
...
DECLARE ---------- sub-block begins
past_due EXCEPTION;
BEGIN
...
IF ... THEN
RAISE past_due;
END IF;
END; ------------- sub-block ends
EXCEPTION
...
WHEN OTHERS THEN
ROLLBACK;
END;
Because the block in which it was declared has no handler for the exception named
past_due, it propagates to the enclosing block. But, according to the scope rules,
enclosing blocks cannot reference exceptions declared in a sub-block. So, only
an OTHERS handler can catch the exception.
*** RULE 4.
Enclosing blocks cannot reference exceptions declared in a sub-block.
Only an OTHERS handler can catch the exception in this case.
Q. Does control return to the point where the exception was raised, after the
successful handling of raised exception?
A. No, control does not return to where the exception was raised. In other words, you
cannot resume processing where you left off.
10
5
END;
*** Use of the OTHERS handler guarantees that no exception will go unhandled.
*** You can have any number of exception handlers, and each handler can
associate a list of exceptions with a sequence of statements.
*** When an exception is raised inside a cursor FOR loop, the cursor is closed
implicitly before the handler is invoked. Therefore, the values of explicit
cursor attributes are not available in the handler.
*** In above case, handlers in the current block cannot catch the raised exception.
It is because an exception raised in a declaration propagates immediately to
the enclosing block.
*** Only one exception at a time can be active in the exception-handling part of a
block or subprogram.
Q. Can exceptions be raised in an exception handler?
A. Yes. An exception raised inside a handler propagates immediately to the enclosing
block, which is searched to find a handler for the newly raised exception. From there
on, the exception propagates normally. e.g.
EXCEPTION
WHEN INVALID_NUMBER THEN
INSERT INTO ... -- might raise DUP_VAL_ON_INDEX
WHEN DUP_VAL_ON_INDEX THEN ... -- cannot catch the exception
Q. Can a GOTO statement branch from an exception handler into the current
block?
A. No. e.g.
DECLARE
pe_ratio NUMBER(3,1);
BEGIN
DELETE FROM stats WHERE symbol = 'XYZ';
SELECT price / NVL(earnings, 0) INTO pe_ratio FROM stocks
WHERE symbol = 'XYZ';
<<my_label>>
INSERT INTO stats (symbol, ratio) VALUES ('XYZ', pe_ratio);
EXCEPTION
WHEN ZERO_DIVIDE THEN
pe_ratio := 0;
GOTO my_label; -- illegal branch into current block
10
7
A. For user-defined exceptions, SQLCODE returns +1 and SQLERRM returns the
message
User-Defined Exception
*** The maximum length of an Oracle error message is 512 characters including
the error code, nested messages, and message inserts such as table and
column names.
*** Make sure you pass negative error numbers to SQLERRM. In the following
example SQLERRM gives unwanted results as it is passed +ve rather than -ve
numbers :-
DECLARE
...
err_msg VARCHAR2(100);
BEGIN
...
/* Get all Oracle error messages. */
FOR err_num IN 1..9999 LOOP
err_msg := SQLERRM(err_num); -- wrong; should be -err_num
INSERT INTO errors VALUES (err_msg);
END LOOP;
END;
10
9
by the block or subprogram is undone.
11
1
BEGIN
SELECT ...
SELECT ...
SELECT ...
...
EXCEPTION
WHEN NO_DATA_FOUND THEN ...
-- Which SELECT statement caused the error?
END;
Normally, this is not a problem. But, if the need arises, you can use a locator variable
to track statement execution, as follows :-
DECLARE
stmt INTEGER := 1; -- designates 1st SELECT statement
BEGIN
SELECT ...
stmt :=2; -- designates 2nd SELECT statement
SELECT ...
stmt := 3; -- designates 3rd SELECT statement
SELECT ...
...
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO errors VALUES ('Error in statement ' || stmt);
...
END;
SUBPROGRAMS
Civilization advances by extending the number of important operations that we can perform
without thinking about them.
The executable part contains statements that assign values, control execution, and
manipulate ORACLE data.
The exception-handling part contains exception handlers, which deal with exception
raised during execution.
DECLARE
PROCEDURE award_bonus (...) IS -- misplaced; must come last
BEGIN ... END;
rating NUMBER;
CURSOR c1 IS SELECT * FROM emp;
Q. What should be done to make a subprogram available for general use by all
tools?
A. Store it in an ORACLE database.
11
3
A. Subprograms provide modularity; that is, they let you break a program down into
manageable, well-defined logic modules. This supports top-down design and the
stepwise refinement approach to problem solving.
Dummy subprograms (stubs) allow you to defer the definition of procedures and
functions until you test and debug the main program.
The declarative part contains local declarations, which are placed between the
keywords IS and BEGIN. The keyword DECLARE, which introduces declarations in
an anonymous PL/SQL block, is not used.
The executable part contains statements, which are placed between the keywords
BEGIN and EXCEPTION (or END). At least one statement must appear in the
executable part of a procedure. The NULL statement meets this requirement.
The exception-handling part contains exception handlers, which are placed between
the keywords EXCEPTION and END.
11
5
BEGIN
executable statements
[EXCEPTION
exception handlers]
END [name];
The declarative part contains local declarations, which are placed between the
keywords 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 the executable part of a function.
The exception-handling part contains exception handlers, which are placed between
the keywords EXCEPTION and END.
*** The function identifier acts like a variable whose value depends on the parameters
passed to it.
DECLARE
empnum INTEGER;
...
FUNCTION bonus (emp_id INTEGER) RETURN REAL IS
BEGIN ... END bonus;
BEGIN
...
INSERT INTO payroll
VALUES(empnum, ..., bonus(empnum)); -- illegal call
END;
*** Do not confuse the RETURN statement with the RETURN clause, which
specifies the datatype of the result value in a function specification.
11
7
Q. Can a subprogram contain several RETURN statements?
A. Yes, a subprogram can contain several RETURN statements, none of which need be
the last lexical statement. Executing any of them completes the subprogram
imediately.
DECLARE
PROCEDURE calc_rating ( ... ); -- forward declaration
...
/* Define subprograms in alphabetical order. */
PROCEDURE award_bonus ( ... ) IS
BEGIN
calc_rating( ... );
...
END;
PROCEDURE calc_rating ( ... ) IS
BEGIN
...
END;
...
*** Although the formal parameter list appears in the forward declaration, it must
also appear in the subprogram body.
11
9
necessary. e.g. the following call to raise_salary is legal :-
raise_salary(emp_num, '2500');
The actual parameter and its corresponding formal parameter must have compatible
datatypes. For instance, PL/SQL cannot convert between the DATE and REAL
datatypes. Also, the result value must be convertible to the new datatype. e.g. The
following procedure call raises the predefined exception VALUE_ERROR because
PL/SQL cannot convert the second actual parameter to a number : -
raise_salary(emp_num, '$2500'); -- note the dollar sign
Q. In how many ways you can indicate the association between an actual and
a formal parameter?
A. You can indicate the association between an actual and formal parameter
1. By position
2. By name.
In othe words, when calling a subprogram, you can write the actual parameters
using either positional or named notation.
*** Positional notation must precede named notation. The reverse is not allowed.
For example, the following procedure call is illegal:
credit(acctno => acct, amt);
Q. Why should the use of OUT & IN OUT modes be avoided with functions?
A. The purpose of a function is to take zero or more arguments (actual parameters)
and return a single value. 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.
Q. Explain IN mode?
A. An IN parameter lets you pass values to the subprogram being called.
Inside the subprogram, an IN parameter acts like a constant. Therefore,
it cannot be assigned a value. e.g. the following assignment statement causes a
compilation error :-
12
1
SELECT sal * 0.10, hiredate INTO bonus, hire_date FROM emp
WHERE empno = emp_id;
IF MONTHS_BETWEEN(SYSDATE, hire_date) > 60 THEN
bonus := bonus + 500; -- causes syntax error
END IF;
Q. Can an OUT actual parameter have a value before the subprogram is called?
A. Yes, An OUT actual parameter can(but need not) have a value before the subprogram
is called. However, the value is lost when you call the subprogram.
PROCEDURE create_dept (
new_dname CHAR DEFAULT 'TEMP',
new_loc CHAR DEFAULT 'TEMP') IS
BEGIN
INSERT INTO dept
VALUES (deptno_seq.NEXTVAL, new_dname, new_loc);
If an actual parameter is not passed, the default value of its corresponding formal
parameter is used. Consider the following calls to create_dept :-
create_dept;
create_dept('MARKETING');
create_dept('MARKETING', 'NEW YORK');
The first call passes no actual parameters, so both default values are used.
The second call passes one actual parameter, so the default value for new_loc is used
The third call passes two actual parameters, so neither default value is used.
Usually, you use positional notation to override the default values of formal
parameters.
IN OUT IN OUT
the default must be specified must be specified
12
3
passes values to returns values to passes initial values
a subprogram the caller to a subprogram;
returns updated
values to the caller
*** Passing large composite types by value is inefficient. So, in most cases PL/SQL
passes composite types by reference, which saves time.
Problem of aliasing also occurs when the same actual parameters appears twice in a
procedure call. Unless both formal parameters are IN parameters, the result is
indeterminate because it depends on the methods of parameter passing chosen by the
compiler. e.g.
DECLARE
str CHAR(10);
PROCEDURE reverse (in_str CHAR, out_str OUT CHAR) IS
...
BEGIN
-- reverse order of characters in string
...
/* At this point, whether the value of in_str is 'ABCD' or
'DCBA' depends on the methods of parameter passing
used by the PL/SQL compiler. */
END reverse;
...
BEGIN
str := 'ABCD';
reverse(str, str); -- indeterminate;
...
END:
12
5
Q. Can you overload standalone subprograms?
A. No, you cannot overload standalone subprograms.
Q. Can you overload two subprograms if their formal parameters differ only in
name or parameter mode?
A. No. e.g. you cannot overload the following two procedures :-
PROCEDURE reconcile (acctno IN INTEGER) IS
BEGIN
...
END;
Q. Can you overload two subprograms if their formal parameters differ only in
datatype and the different datatypes are in the same family?
A. No. e.g. you cannot overload the following procedures because the datatypes
INTEGER and REAL are in the same family :-
PROCEDURE charge_back (amount INTEGER) IS
BEGIN
...
END;
Q. Can you overload two functions that differ only in return type (the datatype of
the result value) even if the types are in different families?
A. No. e.g. you cannot overload the following functions :-
FUNCTION acct_ok (acct_id INTEGER) RETURN BOOLEAN IS
BEGIN
...
END;
Here you are calling the enclosing procedure swap from within the function .But neither
declaration of swap within the current scope matches the procedure call. Therefore, the
compiler will generate an error.
12
7
IF n < 0 THEN
RETURN -1;
ELSE
RETURN 1;
END IF;
END;
BEGIN
...
x := SIGN(0); -- assigns 1 to x
END;
...
x := SIGN(0); -- assigns 0 to x
END;
Inside the sub-block, PL/SQL uses your function definition, not the built-in definition.
To call the built-in function from inside the sub-block, you must use dot notation, as
follows:
x := STANDARD.SIGN(0); -- assigns 0 to x
Q. What is Recursion?
A. Recursion means self-reference.
*** 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.
Q. Can you use iteration in place of recursion? If yes, then where is recursion
appropriate?
A. Yes, we can use iteration in place of recursion. But recursion is appropriate when
the problem can be broken down into simpler versions of itself.
*** The recursive version is more elegant than the iterative version.
However, the iterative version is more efficient. It runs faster and uses less
storage. That is because each recursive call requires additional time and memory.
As the number of recursive calls gets larger, so does the difference in efficiency.
Still, if you expect the number of recursive calls to be small, you might choose the
recursive version for its readability.
12
9
BEGIN
IF n = 0 THEN
RETURN FALSE;
ELSE
RETURN even(n - 1); -- mutually recursive call
END IF;
END odd;
*** The syntax of calling stored subprograms is the same as in case of calling
subprograms of a package, except that you don't use dot notation here.
Q. What are the restrictions regarding the use of SQL and PL/SQL statements
in a stand-alone or packaged subprogram?
A. The body of a stand-alone or packaged subprogram can contain any SQL oe PL/SQL
statement.
*** However, subprograms participating in a distributed transaction, database
triggers, and SQL*Forms applications cannot call stored subprograms that
contain a COMMIT, ROLLBACK or SAVEPOINT statement.
13
1
DELETE FROM emp WHERE empno = emp_id;
END;
*** Generally, tools (such as Oracle Forms) that incorporate the PL/SQL engine
can store subprograms locally for later, strictly local execution. However, to
become available for general use by all tools, subprograms must be stored in
an Oracle database.
Q. What is a package?
A. A Package is a database object. It groups logically related PL/SQL types, objects &
subprograms.
The body fully defines cursors & subprograms and so implements the specification.
13
3
-- public type & object declarations
-- subprogram specifications
END [name];
As you can see specification holds Public declarations, which are visible to your
application. The body holds implementation details & private declarations, which
are hidden from your application.
Also, as your application refers to the specification, you can change the body
(implementation) without having to recompile calling programs.
Further, unlike subprograms, packages stop Cascading Dependencies and thus avoid
unnecessary recompiling. In other words, if you change the definition of a stand-alone
function, ORACLE must recompile all stored subprograms that call the function.
However, if you change the definition of a packaged function, ORACLE need not
recompile the calling subprograms because they do not depend on the package body.
*** Packaged public variables & cursors persist for the duration of a session.
So, they can be shared by all procedures that execute in the environment
Such packages let you define Global Variables that persist throughout a session and
can be used by subprograms & triggers.
package_name.type_name;
package_name.object_name;
package_name.subprogram_name;
13
5
The scope of these declarations is local to the package body. Therefore, the
declared types & objects are inaccessible except from within the package body.
Further, unlike a package specification, the declarative part of a package body
can contain subprogram bodies.
Declare variables & cursors in a package only when you want them to persist
throughout a session.
*** Objects that call or reference global package depend only on the package
specification. Therefore, you can redefine objects in the package body(which
causes the body to be recompiled) without causing ORACLE to invalidate their
dependent objects.
Package STANDARD declares the built-in function named ABS which returns the
absolute value of its arguments.
13
7
If you redeclare ABS in a PL/SQL program, your local declaration overrides the
global declaration. However, you can still call the built-in function by using dot
notation :- STANDARD.ABS
Q. What is raise_application_error?
A. This is a packaged procedure declared and defined in package DBMS_STANDARD
It lets you issue user-defined messages. The syntax is :-
raise_application_error(error_number,error_message);
The calling application gets a PL/SQL exception. This exception can be processed using
the error-reporting functions SQLCODE & SQLERRM in an OTHERS handler.
Furtheremore, it can use EXCEPTION_INIT to map specific error numbers returned by
raise_application_error to exceptions of its own. e.g.
13
9
SQL> EXECUTE emp_actions.hire_employee('JIT', 'ENGG', ...);
Remote Access :-
You can use the following syntax to call packaged subprograms
dtored in a remote ORACLE database :-
e.g. below you are calling the packaged procedure hire_employee in the newyork
database :-
emp_actions.hire_employee@newyork(name, title, ...);
EXECUTION ENVIRONMENT
Every tool carries with it the spirit by which it was created.
SQL*Plus expects you to input an unlabeled PL/SQL block. Hence, you can not start
with a block label.
You can input the rest of the PL/SQL block line by line. Ending the block with a
period(.) on a line by itself stores the block in the SQL buffer.
If you want to edit the file, you can use the SQL*Plus editor. After editing the file, you
can save it again as follows :-
SQL> SAVE <filename> REPLACE
Once it is stored in the SQL buffer, you can run the PL/SQL block again as follows :-
SQL> RUN
or
SQL /
When the block is finished running, you are returned to the SQL*Plus prompt. The
SQL buffer is not cleared until you start inputting the next SQL statement or PL/SQL
block.
The two CLEAR statements get rid of any settings left over from a previous report.
14
1
The COLUMN statement changes the ENAME column heading to NAME. The
TTITLE statement specifies a title that appears at the top of each page in the report.
The slash following the END executes the PL/SQL block.
*** In such a case a slash (/) must follow every PL/SQL block.
Once it is stored in the SQL buffer, you can run the script using the RUN command
or a slash (/). If you prefer, you can load and run the script in one step, as
follows :-
SQL> START <filename>
Your PL/SQL block can take advantage of the SQL*Plus substitution variable feature.
before running a script, SQL*Plus prompts for the value of any variable prefixed with
ampersand(&). e.g. :-
BEGIN
...
FOR i IN 1..&num LOOP
...
END LOOP;
END;
This call is equivalent to the following call issued from an anonymous PL/SQL block
:-
SQL> BEGIN create_dept('ADVERTISING', 'NEW YORK'); END;
In the following example you use the database link newyork to call the remote stored
procedure raise_salary :-
SQL> EXECUTE raise_salary@newyork(7499, 1500);
You can create synonyms to provide location transparency for remote stand-alone
procedures.
Such programs & languages are called host programs and host languages,
respectively.
After writing a program, you precompile the source file. The precompiler checks
the program for syntax errors, then generates a modified source file, which can be
compiled, linked, and executed in the usual way.
*** The keyword END-EXEC must be followed with the host-language statement
terminator.
The value of an input host variable is set by the host program and referenced
by ORACLE. Conversly, the value of an output host variable is set by
ORACLE and referenced by the host program.
You declare host variables in the program Declare Section using regular host
language syntax. Inside a PL/SQL block, the scope of host variable is global.
All references to host variables in a PL/SQL block must be prefixed with a colon.
That way, the precompiler can tell host variables from PL/SQL variables and
database objects.
*** When necessary, ORACLE converts between its internal datatypes and
standard host-language datatypes.
14
3
You use indicator variables to assign nulls to input host variables and to detect nulls
or truncated values in output host variables.
For input host variables, the values your program can assign to an indicator variable
have the following meanings :
-1 ORACLE will ignore the value of the host variable and assign a null to
the database column.
>= 0 ORACLE will assign the value of the host variable to the database
column.
For output host variables, the values ORACLE can assign to an indicator variable
have the following meanings :
-1 The database column contains a null, so the vale of the host variable is
indeterminate.
0 ORACLE assigned an intact column value to the host variable
>0 ORACLE assigned a truncated column value to the host variable. The
integer returned by the indicator variable is the original length of the
column value.
An indicator variable must be defined in the Declare Section as a 2-byte integer and,
in SQL statements, must be pefixed with a colon and appended to its host variable.
A host language needs indicator variables because it cannot manipulate nulls. PL/SQL
meets this need by allowing an embedded PL/SQL block to accept nulls from the host
program and return nulls or truncated values to it. In the following Pro*C example, the
PL/SQL block uses an indicator variable to return a null status code to the host
program :-
EXEC SQL EXECUTE
BEGIN
SELECT ename, comm
INTO :emp_name, :commission:comm_ind
FROM emp
WHERE empno = :emp_number;
...
END;
END-EXEC;
commission_out = commission;
if comm_id = -1
/*if the value returned by an indicator variable is -1, the value of its output host
variable is null*/
printf("null");
else
printf("not null");
Inside a PL/SQL block, an indicator variable must be prefixed with a colon and
appended to its host variable.
You cannot refer to an indicator variable by itself. Furthermore, if you refer to a host
variable with its indicator variable, you must always refer to it that way in the same
block. In the next example, because the host variable appears with its indicator
variable in the SELECT statement, it must also appear that way in the IF statement :-
EXEC SQL EXECUTE
BEGIN
...
SELECT ename, job
INTO :ename_host, :job_host:job_ind
FROM emp
WHERE empno = :empno_host;
...
IF :job_host:job_ind IS NULL THEN
RAISE status_unknown;
END IF:
...
END;
END-EXEC;
Although you cannot refer directly to indicator variables inside a PL/SQL block,
PL/SQL checks their values upon entering the block and sets their values correctly
upon exiting the block.
Nulls
Upon entering a block, if an indicator variable has a value of -1, PL/SQL assigns
a null to the host variable. Upon exiting the block, if a host variable is null, PL/SQL
assigns a value of -1 to the indicator variable. In the following example, the exception
name_missing is raised if the indicator variable ename_ind had a vale of -1 before the
PL/SQL block was entered :-
EXEC SQL EXECUTE
BEGIN
...
IF :ename_host:ename_ind IS NULL THEN
RAISE name_missing;
END IF;
...
EXCEPTION
WHEN name_missing THEN
...
END;
END-EXEC;
Truncated Values
PL/SQL does not raise an exception when a truncated string value
is assigned to a host variable. However, if you use an indicator variable, PL/SQL sets
14
5
it to the original length of the string. In the following example, the host program will
be able to tell, by checking the value of ename_ind, if a truncated value was assigned
to ename_host :-
EXEC SQL EXECUTE
DECLARE
new_ename CHAR(10);
...
BEGIN
...
:ename_host:ename_ind := new_ename;
...
END;
END-EXEC;
To get the length of a VARCHAR, simply refer to its length field. You need not use a
string function or character-counting algorithm.
If you use DECLARE TABLE to define a table that already exists, the precompiler
uses your definition, ignoring the one in the ORACLE data dictionary. Note that you
cannot use the DECLARE TABLE statement inside a PL/SQL block.
The precompiler gets information needed for the semantic check by using embedded
DECLARE TABLE statements or, if you specify the USERID option on the command
line, by connecting to ORACLE and accessing the data dictionary. You need not
connect to ORACLE if every database table referenced in a SQL statement or PL/SQL
block is defined by a DECLARE TABLE statement.
14
7
you can use the ARRAYLEN statement to specify a smaller array dimension.
Furthermore, you can use a procedure call to assign all the values in a host array to
rows in a PL/SQL table. Given that array subscript range is m..n, the corresponding
PL/SQL table index range is always 1..n-m+1. For example, if the array subscript
range is 5..10, the corresponding PL/SQL table index range is 1..(10-5+1) or 1..6
In the Pro*C example below, you pass a host array named salary to a PL/SQL block,
which uses the host array in a function call. The function's formal parameters include a
PL/SQL table named num_tab. The function call assigns all the values in the actual
parameter salary to rows in the formal parameter num_tab.
#include <stdio.h>
main()
{
/* Declare host array */
EXEC SQL BEGIN DECLARE SECTION;
...
float salary[100];
EXEC SQL END DECLARE SECTION;
You can also use a procedure call to assign all row values in a PL/SQL table to
corresponding elements in a host array.
Following table shows the legal conversions between row values in a PL/SQL table
and elements in a host array.
HOST ARRAY
PL/SQL -----------------------------------------------------------------------------------------------------------------
TABLE | CHAR | DATE | LONG | LONG RAW | NUMBER | RAW | ROWID | VARCHAR2 |
-----------------------------------------------------------------------------------------------------------------
CHAR | Yes |
-----------------------------------------------------------------------------------------------------------------
DATE | Yes |
-----------------------------------------------------------------------------------------------------------------
LONG | Yes Yes Yes Yes |
-----------------------------------------------------------------------------------------------------------------
LONG | Yes Yes Yes Yes |
RAW | |
-----------------------------------------------------------------------------------------------------------------
NUM. | Yes |
-----------------------------------------------------------------------------------------------------------------
RAW | Yes Yes Yes Yes |
-----------------------------------------------------------------------------------------------------------------
ROWID | Yes |
-----------------------------------------------------------------------------------------------------------------
VARC2 | Yes Yes Yes Yes |
-----------------------------------------------------------------------------------------------------------------
*** The ORACLE Precompilers do not check your usage of host arrays. For
instance, no index range-checking is done.
Let us repeat the last example, but use ARRAYLEN to override the default
dimension of host array :-
#include <stdio.h>
main()
{
/* Declare host array */
EXEC SQL BEGIN DECLARE SECTION;
...
float salary[100];
int my_dim;
EXEC SQL ARRAYLEN salary (my_dim);
EXEC SQL END DECLARE SECTION;
14
9
...
/* Set smaller host array dimension */
my_dim = 25;
Only 25 array elements are passed to the PL/SQL block because ARRAYLEN
downsizes the host array from 100 to 25 elements. As s result, when the PL/SQL
block is sent to ORACLE for execution, a much smaller host array is sent along.
This saves time and, in a networked environment, reduces network traffic.
The following Pro*C example shows how values are assigned to host array elements
and how individual array elements are referenced :
#include <stdio.h>
...
main()
{
/* Declare host and indicator arrays */
EXEC SQL BEGIN DECLARE SECTION;
float sal[100];
float comm[100];
short icomm[100];
int i =50;
EXEC SQL END DECLARE SECTION;
The ORACLE Precompilers treat a PL/SQL block like a single SQL statement.
So, like a SQL statement, a PL/SQL block can be stored in a string host variable
for processing by dynamic SQL commands.
However, you cannot use single-line comments in a PL/SQL block that will be
processed dynamically. Instead, use multiline comments.
Mentod 1
If your PL/SQL block contains no host variables, you can use Method1 to
execte the PL/SQL string in the usual way. In the following Pro*C example, you
prompt the user for a PL/SQL block, store it in a string variable named user_block,
then execute it :-
15
1
main()
{
printf("\nEnter a PL/SQL block: ");
scanf("%s", user_block);
EXEC SQL EXECUTE IMMEDIATE :user_block;
...
When you store a PL/SQL block in a string host variable, omit the keywords EXEC
SQL EXECUTE, the keyword END-EXEC, and the statement terminator.
Method 2
If your PL/SQL block contains a known number of input and output host
variables, you can use dynamic SQL method 2 to PREPARE and EXECUTE the
PL/SQL string in the usual way. In the Pro*C example below, the PL/SQL block
uses one host variable named my_empno. The program prompts the user for a
PL/SQL block, stores it in a string host variable named user_block, then prepares
and executes the block :-
main()
{
printf("\nEnter a PL/SQL block: ");
scanf("%s", user_block);
EXEC SQL PREPARE my_block FROM :user_block;
EXEC SQL EXECUTE my_block USING :my_empno;
...
Note that my_block is an identifier used by the precompiler, not a host or program
variable.
*** The precompiler treats all PL/SQL host variables as input host variables
whether they serve as input or output host variables(or both) inside th PL/SQL
block. So, you must put all host variables in the USING clause.
When the PL/SQL string is EXECUTEd, host variables in the USING clause replace
corresponding placeholders in the PREPAREd string. Although the precompiler treats
all PL/SQL host variables as input host variables, values are assigned correctly. Input
(program) values as assigned to input host variables, and output(column) values are
assigned to output host variables.
Method 4
If your PL/SQL block contains an unknown number of input or output host
variables, you must use this method. To do so, you set up a bind descriptor for all the
input and output host variables. Executing the DESCRIBE BIND VARIABLES
statement stores information about input and output host variables in the bind
descriptor.
However, you can mimic dynamic SQL by using the DECODE function. In the
following example, the data returned depends on the value of my_column :-
DECLARE
my_column CHAR(10);
my_data emp.ename%TYPE;
BEGIN
...
my_column := 'hiredate';
...
SELECT DECODE(my_column, 'ename', ename,
'hiredate', TO_CHAR(hiredate, 'ddmmyy'), 'empno', empno)
INTO my_data FROM emp WHERE ...;
END;
The value that DECODE returns is always forced to the datatype of the first result
expression. In this example, the first result expression is ename, which belongs to
type CHAR, so the returned value is forced to type CHAR. Thus, my_data is
correctly declared as emp.ename%TYPE.
You can use this technique in many environments. For example, it works in
SQL*Forms, SQL*Menu, and SQL*Plus.
15
3
Notice that the actual parameters number, name, and location are host variables.
LANGUAGE ELEMENTS
Grammar, which knows how to control even kings.
ERROR MESSAGES
We often discover what will do, by finding out what will not do.
15
5
messages. These embedded variables are represented by name, num & str
respectively. e.g., the error message listed as :-
PLS-0038: undefined column 'name' in subquery
might actually appear as :-
PLS-0038: undefined column 'AMPNO' in subquery
CHAR vs VARCHAR2
VARCHAR2 Variable :-
When you assign a character value to a VARCHAR2
variable, if the value is shorter than the declared length of the variable, PL/SQL
neither blank-pads the value nor strips trailing blanks. Character values are
assigned intact, so no information is lost. If the character value is longer than the
declared length of the VARCHAR2 variable, PL/SQL aborts the assignment and
raises VALUE_ERROR. PL/SQL neither truncates the value nor tries to trim trailing
blanks.
ANSI/ISO SQL requires that two character values being compared have equal
lengths.
Comparison of two CHAR Variables :-
If both values in comparison belong to type
CHAR, blank-padding semantics are used. That is, before comparing character
values of unequal length, PL/SQL blank-pads the shorter value to the length of the
longer value. e.g., consider the following declarations :-
name1 CHAR(4) := 'BELL';
name2 CHAR(6) := 'BELL'; -- note trailing blanks
the following IF condition is TRUE :-
IF (name1 = name2) THEN ...
15
7
Comparison of VARCHAR2 Variables or a CHAR and VARCHAR2 Variable :-
If either or both value in a comparison belong to type VARCHAR2, non-blank-
padding semantics are used. That is, when comparing character values of unequal
length, PL/SQL makes no adjustments and uses the exact lengths. e.g. consider the
following declarations :-
name1 VARCHAR2(10) := 'DOW';
name2 CHAR(10) := 'DOW '; -- note trailing blanks
the following IF condition is FALSE :-
IF (name1 = name2) THEN ...
In the ORACLE Precompiler environment, you specify the runtime option DBMS
on the command line as follows :-
... DBMS=V6 ...
The default value is NATIVE, which specifies the version of ORACLE resident on
your system, which must be version 6 or later.
*** When INSERTing character values, you can ensure that no trailing blanks
are stored by using the RTRIM function, which trims trailing blanks. e.g. :-
my_empno := 7471;
my_ename := 'LEE ' -- note trailing blanks;
...
INSERT INTO emp
VALUES (my_empno, RTRIM(my_ename, ...); -- inserts 'LEE'
15
9