You are on page 1of 26

1.

2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.

EXECUTE IMMEDIATE
user_objects
Trapping Exceptions
Composite Data Types
PL/SQL records:
PL/SQL collections:
INDEX BY Tables or Associative Arrays
Nested Tables
VARRAY
Parameter Passing
Overloading Subprograms
Using Forward Declarations
autonomous transactions
Returning Clause
Bulk Binding (FOR ALL) Bulk Collect
Triggers

event VARCHAR2(15):='Father''s day'; event := q' [Mother's day]';

SQL Statements in PL/SQL


In a PL/SQL block you make use of SQL statements to retrieve and modify data from the database table. PL/SQL
supports data manipulation language (DML) and transaction control commands. You can use DML commands to
modify the data in a database table. However, remember the following points while using DML statements and
transaction control commands in PL/SQL blocks:
The keyword END signals the end of a PL/SQL block, not the end of a transaction. Just as a block can span
multiple transactions, a transaction can span multiple blocks.
EXECUTE IMMEDIATE
PL/SQL does not directly support data definition language (DDL) statements, such as CREATETABLE,
ALTERTABLE, or DROPTABLE. PL/SQL supports early binding due to which the compilation time is greater than
the execution time. If applications have to create database objects at run time by passing values, then early binding
cannot happen in such cases. DDL statements cannot be directly executed. These statements are dynamic SQL
statements. Dynamic SQL statements are built as character strings at run time and can contain placeholders for
parameters. Therefore, you can use dynamic SQL to execute your DDL statements in PL/SQL. Use EXECUTE
IMMEDIATE statement which takes the SQL statement as an argument to execute your DDL statement. The
EXECUTE IMMEDIATE statement parses and executes a dynamic SQL statement.

Consider the following example:


BEGIN
CREATE TABLE My_emp_table AS SELECT * FROM employees;
END;
/
The example uses a DDL statement directly in the block. When you execute the block, you will see the following
error:
create table My_table as select * from table_name;
* ERROR at line 5: ORA-06550: line 5, column 1: PLS-00103: Encountered the
symbol "CREATE" when expecting one of the following:
Use the EXECUTE IMMEDIATE statement to avoid the error:
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE My_emp_table AS SELECT * FROM employees';
END;
/
PL/SQL does not support data control language (DCL) statements, such as GRANTor REVOKE. You can use
EXECUTE IMMEDIATE statement to execute them.

Note:When you create any object such as table, procedure, function, and so on, the entries are made to the
user_objects table. When the code in the slide is executed successfully, you can check the user_objects
table by issuing the following command:
SELECT object_name,object_type FROM user_objects;

Note:You can use the DESCRIBE command to check the arguments and return type of the function. Example:
DESCRIBE check_sal;
Guidelines for Trapping Exceptions
Begin the exception-handling section of the block with the EXCEPTION keyword.
Define several exception handlers, each with its own set of actions, for the block.
When an exception occurs, PL/SQL processes only one handler before leaving the block.
Place the OTHERS clause after all other exception-handling clauses.
You can have only one OTHERS clause.
Exceptions cannot appear in assignment statements or SQL statements.
Trapping Non-Predefined Oracle Server Errors
Non-predefined exceptions are similar to predefined exceptions; however, they are not defined as PL/SQL
exceptions in the Oracle server. They are standard Oracle errors. You can create exceptions with standard Oracle
errors using the PRAGMA EXCEPTION_INIT function. Such exceptions are called non-predefined exceptions.
You can trap a non-predefined Oracle server error by declaring it first. The declared exception is raised implicitly. In
PL/SQL, the PRAGMA EXCEPTION_INIT tells the compiler to associate an exception name with an Oracle error
number. That allows you to refer to any internal exception by name and to write a specific handler for it.
Note: PRAGMA(also called pseudo instructions) is the keyword that signifies that the statement is a compiler
directive, which is not processed when the PL/SQL block is executed. Rather, it directs the PL/SQL compiler to
interpret all occurrences of the exception name within the block as the associated Oracle server error number

Note:Use the RAISE statement by itself within an exception handler to re-raise the same
exception and propagate it back to the calling environment.

The RAISE_APPLICATION_ERROR Procedure


Use the RAISE_APPLICATION_ERROR procedure to communicate a predefined exception interactively by
returning a nonstandard error code and error message. With RAISE_APPLICATION_ERROR, you can report errors
to your application and avoid returning unhandled exceptions

Here is another example of using the RAISE_APPLICATION_ERROR procedure:


DECLAREe_name EXCEPTION;
PRAGMA EXCEPTION_INIT (e_name, -20999);BEGIN...DELETE FROM employeesWHERE
last_name = 'Higgins';
IF SQL%NOTFOUND THEN RAISE_APPLICATION_ERROR(-20999,'This is not avalid last
name');END IF;EXCEPTION WHEN e_name THEN--handle the error...END;
/

Composite Data Types


You have learned that variables of scalar data type can hold only one value whereas a variable of composite data type
can hold multiple values of scalar data type or composite data type. There are two types of composite data types:
PL/SQL records: PL/SQL records are used to treat related but dissimilar data as a logical unit. A PL/SQL record
can have variables of different types. For example, you can define a record to hold employee details. This involves
storing employee number as NUMBER, first name and last name as VARCHAR2, and so on. By creating a record to
store employee details, you are creating a logical collective unit. This makes data access and manipulation easier.
PL/SQL collections: Collections are used to treat data as a single unit. Collections are of three types:
-INDEX BY tables or associative arrays
-Nested table
-VARRAY
Use PL/SQL records when you want to store values of different data types that are logically related. If you create a
record to hold employee details, identify that all the values stored are related because they provide information about
a particular employee.
Use PL/SQL collections when you want to store values of the same data type. Note that this data type can also be of
the composite type such as records. You can define a collection to hold the first names of all the employees. You may
have stored n names in the collection; however, name 1 is not related to name 2. The relation between these names is
only that they are employee names.
PL/SQL Records A record is a group of related data items stored in fields, each with its own name and data type.
Each record defined can have as many fields as necessary.
Records can be assigned initial values and can be defined as NOTNULL.

Fields without initial values are initialized to NULL.


The DEFAULT keyword can also be used when defining fields.
You can define RECORD t ypes and declare user-defined records in the declarative part of any block, subprogram,
or package.
You can declare and reference nested records. One record can be the component of another record.
DECLARE TYPE emp_record_type IS RECORD(
employee_idNUMBER(6) NOT NULL := 100,
last_name employees.last_name%TYPE,
job_id employees.job_id%TYPE);
emp_record emp_record_type

INDEX BY Tables or Associative Arrays


INDEX BY tables are composite types (collections) and are user defined. INDEX BY tables can store data using a
primary key value as the index, where the key values are not sequential. INDEX BY tables are sets of key-value
pairs and hence you can imagine data stored in two columns though the key and value pairs are not exactly stored in
columns. INDEX BY tables have only two columns:
A column of type integer or string, which acts as the primary key. The key can be numeric, either
BINARY_INTEGER or PLS_INTEGER. BINARY_INTEGER and PLS_INTEGER require less storage
than NUMBER. They are used to represent mathematical integers compactly and implement arithmetic
operations by using machine arithmetic. Arithmetic operations on these data types are faster than NUMBER
arithmetic. The key can also be of type VARCHAR2or one of its subtypes. The examples in this course use
PLS_INTEGER as data type for the key column.
A column of scalar or record data type to hold values. If the column is of scalar type, it can hold only one
value. If the column is of record type, then it can hold multiple values. The INDEX BY tables are
unconstrained for size. However, the key in the PLS_INTEGER column is restricted to the maximum value
that a PLS_INTEGER can hold. Note that the keys can be both positive and negative. The keys in INDEX
BY tables are not in sequence

INDEX BY Table of Records


At any given point in time, an INDEX BY table declared as a table of scalar data type can store the details of only
one column in a database table. There is often a need to store all the columns retrieved by a query. The INDEX BY
table of records offers a solution to this. Because only one table definition is needed to hold information about all the
fields of a database table, the table of records greatly increases the functionality of INDEX BY tables.

Nested Tables
The functionality of nested tables is similar to INDEX BY tables; however, there
are differences in the nested table implementation. The nested table is a valid
data type in a schema-level table but an INDEX BY table is not. The key type for
nested tables is not PLS_INTEGER. The key cannot be a negative value unlike in
the INDEX BY table. Though we are referring to the first column as key, there is
no key in case of nested tables. There is a column with numbers in sequence which
is considered as the key column. Elements can be deleted from anywhere in a
nested table leaving a sparse table with non sequential keys. The rows of a
nested table are not in any particular order. When you retrieve values from a
nested table, the rows are given consecutive subscripts starting from 1. Nested
tables can be stored in the database unlike INDEX BY tables.
Syntax:
TYPEtype_nameISTABLEOF
{column_type|variable%TYPE
| table.column%TYPE} [NOT NULL]

| table.%ROWTYPE
In Oracle Database 10g, nested tables can be compared for equality. You can check
if an
element exists in the nested table and also if the nested table is a subset of
another.

If you do not initialize an INDEX BY table, then it is empty. If you do not initialize a nested
table, then it is automatically initialized to NULL. You can initialize the offices nested table
by using a constructor as shown below:
offices := location_type('Bombay', 'Tokyo','Singapore', 'Oxford');
Complete example:
SET SERVER OUTPUT ON DECLARE TYPE
location_type IS TABLE OF locations.city%TYPE;
offices location_type;
table_count NUMBER;BEGINoffices := location_type('Bombay',
'Tokyo','Singapore','Oxford');
table_count := offices.count();
FOR i in 1..table_count LOOP
DBMS_OUTPUT.PUT_LINE(offices(i));
END LOOP;
END;

VARRAY
Variable-size arrays (VARRAY)are similar to PL/SQL tables except that a VARRAY is constrained in size. VARRAY
is valid in a schema-level table. Items of VARRAY type are called VARRAYS. VARRAYS have a fixed upper
bound. You have to specify the upper bound when you are declaring them. This is similar to arrays in the C language.
The maximum size of a VARRAY is 2 gigabytes (GB) as in nested tables. The distinction between nested table and
VARRAY is the physical storage mode. The elements of a VARRAY are stored contiguously in memory and not in
the database. One can create a VARRAY type in the database by using SQL.
Example:
TYPE location_type IS VARRAY(3) OF locations.city%TYPE;
offices location_type;
The size of a VARRAY is restricted to 3. You can initialize a VARRAY by using constructors. If you try to initialize
the VARRAY with more than three elements, then a Subscript outside of limit error message is displayed.

Note: You cannot assign default values to OUT and IN OUT parameters.

Overloading Subprograms
The overloading feature in PL/SQL enables you to develop two or more packaged subprograms with the same name.
Overloading is useful when you want a subprogram to accept similar sets of parameters that have different data
types. For example, the TO_CHAR function has more than one way to be called, enabling you to convert a number
or a date to a character string.PL/SQL allows overloading of package subprogram names and object type methods.
The key rule is that you can use the same name for different subprograms as long as their formal parameters differ in
number, order, or data type family.
Consider using overloading when:
Processing rules for two or more subprograms are similar, but the type or number of parameters used varies
Providing alternative ways for finding different data with varying search criteria. For example, you may want to
find employees by their employee ID and also provide a way to find employees by their last name. The logic is
intrinsically the same, but the parameters or search criteria differ.
Extending functionality when you do not want to replace existing code
Note:

Restrictions
You cannot overload:
Two subprograms if their formal parameters differ only in data type and the different data types are in the same
family (NUMBER and DECIMAL belong to the same family.)

Two subprograms if their formal parameters differ only in subtype and the different subtypes are based on types in
the same family (VARCHAR and STRING are PL/SQL subtypes of VARCHAR2.)
Two functions that differ only in return type, even if the types are in different families You get a run-time error
when you overload subprograms with the preceding features

Using Forward Declarations


As previously mentioned, PL/SQL enables you to create a special subprogram declaration called a forward
declaration. A forward declaration may be required for private subprograms in the package body, and consists of the
subprogram specification terminated by a semicolon. Forward declarations help to:
Define subprograms in logical or alphabetical order
Define mutually recursive subprograms. Mutually recursive programs are programs that call each other directly or
indirectly.
Group and logically organize subprograms in a package body When creating a forward declaration:
The formal parameters must appear in both the forward declaration and the subprogram body
The subprogram body can appear anywhere after the forward declaration, but both must appear in the same program
unit Forward Declarations and Packages Typically, the subprogram specifications go in the package specification,
and the subprogram bodies go in the package body. The public subprogram declarations in the package specification
do not require forward declarations.

Since Oracle8i, the autonomous transactions were added to make it possible to create an independent transaction. An
autonomous transaction (AT) is an independent transaction started by another main transaction (MT). The slide
depicts the behavior of an AT:
1.The main transaction begins.
2.A proc2procedure is called to start the autonomous transaction.
3.The main transaction is suspended.
4.The autonomous transactional operation begins.
5.The autonomous transaction ends with a commit or roll back operation.
6.The main transaction is resumed.

7.The main transaction ends.

The INSERT, UPDATE, and DELETE statements can include a RETURNING clause, which returns
column values from the affected row into PL/SQL variables or host variables. This eliminates
the need to SELECT the row after an INSERT or UPDATE, or before a DELETE.

Binding Use bulk binds to improve the performance of:


DML statements that reference collection elements
SELECT statements that reference collection elements
Cursor FOR loops that reference collections and the RETURNING INTO clause
Keywords to Support Bulk Binding
The FORALL keyword instructs the PL/SQL engine to bulk bind input collections before sending them to the SQL
engine. Although the FORALL statement contains an iteration scheme, it is not a FOR loop.
The BULKCOLLECT keyword instructs the SQL engine to bulk bind output collections, before returning them to the
PL/SQL engine. This allows you to bind locations into which SQL can return the retrieved values in bulk. Thus, you
can use these keywords in the SELECT INTO, FETCH INTO, and RETURNING INTO clauses.
The SAVE EXCEPTIONS keyword is optional. However, if some of the DML operations succeed and some fail,
you will want to track or report on those that fail. Using the SAVE EXCEPTIONS keyword causes failed operations
to be stored in a cursor attribute called %BULK_EXCEPTIONS, which is a collection of records indicating the bulk
DML iteration number and corresponding error code.

To manage exceptions and have the bulk bind complete despite errors, add the SAVE EXCEPTIONS keyword to
your FORALL statement after the bounds, before the DML statement. Use the new cursor attribute
%BULK_EXCEPTIONS, which stores a collection of records with two fields:
%BULK_EXCEPTIONS(i).ERROR_INDEX holds the "iteration" of the statement during which the exception
was raised.
%BULK_EXCEPTIONS(i).ERROR_CODE holds the corresponding error code.
Values stored in %BULK_EXCEPTIONS refer to the most recently executed FORALL statement. Its subscripts
range from 1 to %BULK_EXCEPTIONS.COUNT.
FORALL: Example (continued)
An Additional Cursor Attribute for DML Operations
Another cursor attribute added to support bulk operations is %BULK_ROWCOUNT. The %BULK_ROWCOUNT
attribute is a composite structure designed for use with the FORALL statement. This attribute acts like an
index-by table. Its ith element stores the number of rows processed by the ith execution of an UPDATE or
DELETE statement. If the ith execution affects no rows, then %BULK_ROWCOUNT(i)returns zero.
For example:
CREATE TABLE num_table (n NUMBER);
DECLARE
TYPE NumList IS TABLE OF NUMBERINDEX BY BINARY_INTEGER;
nums NumList;
BEGIN
nums(1) := 1;
nums(2) := 3;
nums(3) := 5;
nums(4) := 7;
nums(5) := 11;
FORALL i IN nums.FIRST .. nums.LAST
INSERT INTO num_table (n) VALUES (nums(i));

FOR i IN nums.FIRST .. nums.LAST


LOOP
dbms_output.put_line('Inserted ' ||
SQL%BULK_ROWCOUNT(i) || ' row(s)'
|| ' on iteration ' || i);
END LOOP;
END;
/
DROP TABLE num_table;
The following results are produced by this example:
Inserted 1 row(s) on iteration 1
Inserted 1 row(s) on iteration 2
Inserted 1 row(s) on iteration 3
Inserted 1 row(s) on iteration 4
Inserted 1 row(s) on iteration 5
PL/SQL procedure successfully completed.

Using BULKCOLLECTINTO with Cursors


In Oracle Database 10g, when using cursors in PL/SQL, you can use a form of the FETCH statement that supports
the bulk collection syntax shown in the example in the slide.
This example shows how BULK COLLECT INTO can be used with cursors.
You can also add a LIMIT clause to control the number of rows fetched in each operation. The code example in the
slide could be modified as follows:
CREATE PROCEDURE get_departments(loc NUMBER,nrows NUMBER) IS
CURSOR dept_csr IS SELECT * FROM departments
WHERE location_id = loc;
TYPE dept_tabtype IS TABLE OF dept_csr%ROWTYPE;
depts dept_tabtype;
BEGIN
OPEN dept_csr;
FETCH dept_csr BULK COLLECT INTO depts LIMIT nrows;
CLOSE dept_csr;
DBMS_OUTPUT.PUT_LINE(depts.COUNT||' rows read');
END;

Using BULKCOLLECT INTO with a RETURNING Clause


Bulk binds can be used to improve the performance of FOR loops that reference collections and return DML. If you
have, or plan to have, PL/SQL code that does this, then you can use the FORALL keyword along with the
RETURNING and BULK COLLECT INTO keywords to improve performance.
In the example shown in the slide, the salary information is retrieved from the EMPLOYEES table and collected
into the new_sals array.
The new_sals collection is returned in bulk to the PL/SQL engine.
The example in the slide shows an incomplete FOR loop that is used to iterate through the new salary data received
from the UPDATE operation, and process the results.
TRIGGERS
Note: If multiple triggers are defined for a table, then the order in which multiple triggers of the same type fire is
arbitrary. To ensure that triggers of the same type are fired in a particular order, consolidate the triggers into one
trigger that calls separate procedures in the desired order.

Creating a DML Statement Trigger


In this example, the database trigger SECURE_EMP is a BEFORE statement trigger that prevents the INSERT
operation from succeeding if the business condition is violated. In this case, the trigger restricts inserts into the
EMPLOYEES table during certain business hours, Monday through Friday.
If a user attempts to insert a row into the EMPLOYEES table on Saturday, then the user sees an error message, the
trigger fails, and the triggering statement is rolled back. Remember that the RAISE_APPLICATION_ERROR is a
server-side built-in procedure that returns an error to the user and causes the PL/SQL block to fail.
When a database trigger fails, the triggering statement is automatically rolled back by the Oracle server

Combining Triggering Events

You can combine several triggering events into one by taking advantage of the special conditional predicates
INSERTING, UPDATING, and DELETING within the trigger body.
Example
Create one trigger to restrict all data manipulation events on the EMPLOYEES table to certain business hours,
Monday through Friday.

Creating a DML Row Trigger


You can create a BEFORE row trigger in order to prevent the triggering operation from succeeding if a certain
condition is violated.
In the example, a trigger is created to allow certain employees to be able to earn a salary of more than 15,000.
Suppose that a user attempts to execute the following UPDATE statement:
UPDATE employees
SET salary = 15500
WHERE last_name = 'Russell';
The trigger raises the following exception:
UPDATE EMPLOYEES
*
ERROR at line 1:ORA-20202: Employee cannot earn more than $15,000.
ORA-06512: at "PLSQL.RESTRICT_SALARY", line 5
ORA-04088: error during execution of trigger "PLSQL.RESTRICT_SALARY"

INSTEAD OF Triggers
Use INSTEAD OF triggers to modify data in which the DML statement has been issued against an inherently
nonupdatable view. These triggers are called INSTEAD OF triggers because, unlike other triggers, the Oracle
server fires the trigger instead of executing the triggering statement. This trigger is used to perform an INSERT,
UPDATE, or DELETE operation directly on the underlying tables. You can write INSERT, UPDATE, or DELETE
statements against a view, and the INSTEADOF trigger works invisibly in the background to make the right actions
take place. A view cannot be modified by normal DML statements if the view query contains set operators, group
functions, clauses such as GROUP BY, CONNECT BY, START, the DISTINCT operator, or joins. For example, if a
view consists of more than one table, an insert to the view may entail an insertion into one table and an update to
another. So, you write an INSTEADOF trigger that fires when you write an insert against the view. Instead of the
original insertion, the trigger body executes, which results in an insertion of data into one table and an update to
another table.
Note: If a view is inherently updatable and has INSTEADOF triggers, then the triggers take precedence.
INSTEADOF triggers are row triggers. The CHECK option for views is not enforced when insertions or updates to
the view are performed by using INSTEADOF triggers. The INSTEADOF trigger body must enforce the check.

CREATE OR REPLACE TRIGGER new_emp_dept


INSTEAD OF INSERT OR UPDATE OR DELETE ON emp_details
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO new_emps
VALUES (:NEW.employee_id, :NEW.last_name,:NEW.salary,
:NEW.department_id);
UPDATE new_depts
SET dept_sal = dept_sal + :NEW.salary
WHERE department_id = :NEW.department_id;
ELSIF DELETING THEN
DELETE FROM new_emps
WHERE employee_id = :OLD.employee_id;
UPDATE new_depts
SET dept_sal = dept_sal -:OLD.salary
WHERE department_id = :OLD.department_id;
ELSIF UPDATING ('salary') THEN
UPDATE new_emps
SET salary = :NEW.salary
WHERE employee_id = :OLD.employee_id;
UPDATE new_depts
SET dept_sal = dept_sal +
(:NEW.salary -:OLD.salary)
WHERE department_id = :OLD.department_id;
ELSIF UPDATING ('department_id') THEN
UPDATE new_emps
SET department_id = :NEW.department_id
WHERE employee_id = :OLD.employee_id;
UPDATE new_depts

END IF;
END;
/

SET dept_sal = dept_sal -:OLD.salary


WHERE department_id = :OLD.department_id;
UPDATE new_depts
SET dept_sal = dept_sal + :NEW.salary
WHERE department_id = :NEW.department_id;

Comparison of Database Triggers and Stored Procedures


There are differences between database triggers and stored procedures:

Triggers are fully compiled when the CREATE TRIGGER command is issued and the executable code is stored in
the data dictionary.
Note:If errors occur during the compilation of a trigger, the trigger is still created.

Obteniendo planes de ejecucin de Oracle SQL


Para afinar sentencias SQL necesitas obtener su plan de ejecucin, esto es, informacin sobre como la base de
datos Oracle va a procesar tu sentencia SQL para obtener los resultados de la mejor manera posible. Este es un
tema importante y muy complejo, y este mensaje es slo para mostrar cmo obtener planes de ejecucin, no
para cmo analizarlos.
La manera mas fcil para obtener un plan de ejecucin es usar la sentencia EXPLAIN PLAN FOR y el script
utlxpls.sql:
SQL> set linesize 130
SQL> set pagesize 9999
SQL> EXPLAIN PLAN FOR
select OBJECT_NAME from dba_objects where OWNER='SYS' order by OBJECT_NAME;
Explained.
SQL> @$ORACLE_HOME/rdbms/admin/utlxpls.sql

Como se mencion arriba, usar EXPLAIN PLAN FOR es la manera mas fcil para tener una idea sobre el plan
de ejecucin de una sentencia SQL sin tener que ejecutarla, pero podra no ser el mismo plan usado para
ejecutarla porque la seleccin del plan tiene lugar al momento de la ejecucin y depende de muchos factores
como carga, exactitud de las estadsticas y hints por nombrar algunos factores. De hecho, podras estar ms
interesado en saber el plan de ejecucin de un SQL corriendo ms que en el plan de ejecucin de una sentencia
SQL no ejecutada an; si ese es el caso puedes obtenerlo de esta forma:
SQL>
SQL>
SQL>
SQL>

column
column
column
column

SID format a6
USERNAME format a10
PROGRAM format a50
EVENT format a30

SQL> select to_char(s.sid) AS sid, s.username, s.status, s.program, s.event


FROM v$session s JOIN v$process p ON (p.addr = s.paddr)
WHERE s.username = 'SYS' ORDER BY 1;