45
About PL/SQL Procedural Language/SQL (PL/SQL) is Oracle Corporation’s procedural language extension to SQL, the standard data access language for relational databases. PL/SQL offers modern software engineering features such as data encapsulation, exception handling, information hiding, object orientation, and brings state-of-the-art programming to the Oracle Server and toolset PL/SQL Environment Blocks of PL/SQL are passed to and processed by a PL/SQL engine, which may reside within the tool or within the Oracle server. When you submit PL/SQL blocks from a Oracle precompiler such as Pro*C or Pro*Cobol program, userexit, iSQL*Plus, or Server Manager, the PL/SQL engine in the Oracle Server processes them. It separates the SQL statements and sends them individually to the SQL statements executor. Benefits PL/SQL plays a central role in both the Oracle server (through stored procedures, stored functions, database triggers, and packages) and Oracle development tools (through Oracle Developer component triggers). PL/SQL can be used to group SQL statements together within a single block and to send the entire block to the server in a single call, thereby reducing networking traffic Break down a complex problem into a set of manageable, well-defined, logical modules and implement the modules with blocks. Prtable-SQL. In other words, PL/SQL programs can run anywhere the Oracle server can run; you do not need to tailor them to each new environment. Declare variables, cursors, constants, and exceptions and then use them in SQL and procedural statements Procedural Language Control Structures allow you to do the following: • Execute a sequence of statements conditionally • Execute a sequence of statements iteratively in a loop The Error handling functionality in PL/SQL allows you to do the following: • Process Oracle server errors with exception-handling routines • Declare user-defined error conditions and process them with exception- handling routines • Easy maintenance that enables you to modify: – Routines online without interfering with other users – One routine to affect multiple applications – One routine to eliminate duplicate testing • Improved data security and integrity by doing the following:

plsql

Embed Size (px)

DESCRIPTION

plsql

Citation preview

Page 1: plsql

About PL/SQLProcedural Language/SQL (PL/SQL) is Oracle Corporation’s procedural language extension to SQL, thestandard data access language for relational databases. PL/SQL offers modern software engineeringfeatures such as data encapsulation, exception handling, information hiding, object orientation, andbrings state-of-the-art programming to the Oracle Server and toolset

PL/SQL EnvironmentBlocks of PL/SQL are passed to and processed by a PL/SQL engine, which mayreside within the tool or within the Oracle server.When you submit PL/SQL blocks from a Oracle precompiler such as Pro*C or Pro*Cobol program, userexit,iSQL*Plus, or Server Manager, the PL/SQL engine in the Oracle Server processes them. It separatesthe SQL statements and sends them individually to the SQL statements executor.

BenefitsPL/SQL plays a central role in both the Oracle server (through stored procedures, stored functions,database triggers, and packages) and Oracle development tools (through Oracle Developer componenttriggers).PL/SQL can be used to group SQL statements together within a single block and to send theentire block to the server in a single call, thereby reducing networking trafficBreak down a complex problem into a set of manageable, well-defined, logical modules andimplement the modules with blocks.Prtable-SQL. In other words,PL/SQL programs can run anywhere the Oracle server can run; you do not need to tailor them toeach new environment.Declare variables, cursors, constants, and exceptions and then use them in SQL and proceduralstatementsProcedural Language Control Structures allow you to do the following:• Execute a sequence of statements conditionally• Execute a sequence of statements iteratively in a loopThe Error handling functionality in PL/SQL allows you to do the following:• Process Oracle server errors with exception-handling routines• Declare user-defined error conditions and process them with exception-handling routines

• Easy maintenance that enables you to modify:– Routines online without interfering with other users– One routine to affect multiple applications– One routine to eliminate duplicate testing• Improved data security and integrity by doing the following:– Control indirect access to database objects from nonprivileged users with securityprivileges– Ensure that related actions are performed together, or not at all, by funneling activity forrelated tables through a single path• Improved performance that allows you to do the following:– Avoid reparsing for multiple users by exploiting the shared SQL area– Avoid PL/SQL parsing at run time by parsing at compile time– Reduce the number of calls to the database and decrease network traffic by bundlingcommands• Improved code clarity: Using appropriate identifier names to describe the action of the routinesreduces the need for comments and enhances the clarity of the code.

programs can be divided into logical blocks. A

Page 2: plsql

PL/SQL block consists of up to three sections: declarative (optional), executable (required), andexception handling (optional). The following table describes the three sections:Section Description InclusionDeclarative Contains all variables, constants, cursors, anduser-defined exceptions that are referenced in theexecutable and declarative sections OptionalExecutable Contains SQL statements to manipulate data inthe database and PL/SQL statements tomanipulate data in the block MandatoryExceptionhandlingSpecifies the actions to perform when errors andabnormal conditions arise in the executablesection Optional

Section keywords DECLARE, BEGIN, and EXCEPTION are not followed by semicolons.• END and all other PL/SQL statements require a semicolon to terminate the statement.

Variables can be used for:• Temporary storage of data• Manipulation of stored values• Reusability• Ease of maintenance

PL/SQL variables:– Scalar– Composite– Reference– LOB (large objects)

Non-PL/SQL variables: Bind and host variables

iSQL*Plus host (or “bind”) variables can be usedto pass run time values out of the PL/SQL blockback to the iSQL*Plus environment.

identifier [CONSTANT] datatype [NOT NULL][:= | DEFAULT expr];

DECLAREv_hiredate DATE;v_deptno NUMBER(2) NOT NULL := 10;v_location VARCHAR2(13) := 'Atlanta';c_comm CONSTANT NUMBER := 1400;

Two variables can have the same name, provided theyare in different blocks.

CHAR [(maximum_length)]• VARCHAR2 (maximum_length)• LONG• LONG RAW• NUMBER [(precision, scale)]• BINARY_INTEGER• PLS_INTEGER• BOOLEAN TRUE, FALSE, or NULL.

Rather than hard coding the data type and precision of a variable, you can use the %TYPE attribute to

Page 3: plsql

declare a variable according to another previously declared variable or database column. The %TYPEattribute is most often used when the value stored in the variable will be derived from a table in the database. A NOT NULL database column constraint does not apply to variables that are declared using %TYPE.

v_name employees.last_name%TYPE;v_balance NUMBER(7,2);v_min_balance v_balance%TYPE := 10;

TheCLOB (character large object) data type is used to store large blocks of single-byte characterdata in the database in line (inside the row) or out of line (outside the row).• TheBLOB (binary large object) data type is used to store large binary objects in the database inline (inside the row) or out of line (outside the row).• TheBFILE (binary file) data type is used to store large binary objects in operating system filesoutside the database.• TheNCLOB (national language character large object) data type is used to store large blocks ofsingle-byte or fixed-width multibyte NCHAR unicode data in the database, in line or out of line.

Bind variables can be used to passrun-time values, either number or character, into or out of one or more PL/SQL programs.

VARIABLE RESULT NUMBERBEGINSELECT (SALARY*12) + NVL(COMMISSION_PCT,0) INTO :RESULTFROM employees WHERE employee_id = 144;END;/PRINT RESULTuse the PRINTcommand. However, PRINT cannot be used inside a PL/SQL block because it is an iSQL*PlusHost variablesare also called bind variables.

If verify is on, SQL*Plus prints two lines for each substituted variable (see set define.

VARIABLE g_monthly_sal NUMBERDEFINE p_annual_sal = 50000SET VERIFY OFFDECLAREv_sal NUMBER(9,2) := &p_annual_sal;BEGIN:g_monthly_sal := v_sal/12;END;/PRINT g_monthly_sal

SET SERVEROUTPUT ONDEFINE p_annual_sal = 60000DECLAREv_sal NUMBER(9,2) := &p_annual_sal;BEGINv_sal := v_sal/12;DBMS_OUTPUT.PUT_LINE ('The monthly salary is ' ||TO_CHAR(v_sal));END;

Page 4: plsql

Variables declared in an external environmentsuch as iSQL*Plus are called host variables.

Comment-Prefix single-line comments with two dashes (--).• Place multiple-line comments between the symbols/* and */.

CHR is the SQL function that converts an ASCII code to its corresponding character; 10 is the code fora line feed.

IF x > y THENv_max := x;ELSEv_max := y;END IF;

v_salary NUMBER(8,2) := &p_salary;Enter value for p_salary: 4000old 2: v_salary NUMBER(8,2) := &p_salary;new 2: v_salary NUMBER(8,2) := 4000;

Control a transaction with the COMMIT, ROLLBACK,or SAVEPOINT command.

• PL/SQL does not directly support data definition language (DDL) statements, such as CREATETABLE, ALTER TABLE, or DROP TABLE.• PL/SQL does not support data control language (DCL) statements, such as GRANT or REVOKE.

SELECT department_id, location_idINTO v_deptno, v_location_idFROM departmentsQueries Must Return One and Only One RowNO_DATA_FOUND and TOO_MANY_ROWS

DML• TheINSERT statement adds new rows of data to the table.• TheUPDATE statement modifies existing rows in the table.• TheDELETE statement removes unwanted rows from the table.• TheMERGE statement selects rows from one table to update or insert into another table. Thedecision whether to update or insert into the target table is based on a condition in the ON clause.

INSERT INTO employees(employee_id, first_name, last_name, email,hire_date, job_id, salary)VALUES(employees_seq.NEXTVAL, 'Ruth', 'Cores', 'RCORES',sysdate, 'AD_ASST', 4000);

UPDATE employeesSET salary = salary + v_sal_increaseWHERE job_id = 'ST_CLERK';

DELETE FROM employeesWHERE department_id = v_deptno;

MERGE INTO copy_emp c

Page 5: plsql

USING employees eON (e.employee_id = v_empno)WHEN MATCHED THENUPDATE SETc.first_name = e.first_name,c.last_name = e.last_name,c.email = e.email,. . .WHEN NOT MATCHED THENINSERT VALUES(e.employee_id, e.first_name, e.last_name,. . .,e.department_id);

A cursor is a private SQL work area.Whenever you issue a SQL statement, the Oracle server opens an area of memory in which thecommand is parsed and executed. This area is called a cursor.When the executable part of a block issues a SQL statement, PL/SQL creates an implicit cursor, whichPL/SQL manages automatically. The programmer explicitly declares and names an explicit cursor.There are four attributes available in PL/SQL that can be applied to cursors.• There are two types of cursors:– Implicit cursors– Explicit cursors• The Oracle server uses implicit cursors to parseand execute your SQL statements.• Explicit cursors are explicitly declared by theprogrammer.

You can use the attributes SQL%ROWCOUNT, SQL%FOUND, SQL%NOTFOUND, and SQL%ISOPEN in the exception section%BULK_ROWCOUNT, designed for use with the FORALLstatement. that is not covered in this course.

:rows_deleted := (SQL%ROWCOUNT ||' row deleted.');END;/PRINT rows_deleted

SAVEPOINT savepoint_name;ROLLBACK [WORK];ROLLBACK [WORK] TO [SAVEPOINT] savepoint_name;

IF condition THENstatements;[ELSIF condition THENstatements;][ELSEstatements;]END IF;CASE selectorWHEN expression1 THEN result1WHEN expression2 THEN result2...WHEN expressionN THEN resultN[ELSE resultN+1;]END;

v_appraisal :=

Page 6: plsql

CASE v_gradeWHEN 'A' THEN 'Excellent'WHEN 'B' THEN 'Very Good'WHEN 'C' THEN 'Good'ELSE 'No such grade'END;

There are three loop types:• Basic loop that perform repetitive actions without overall conditions• FOR loops that perform iterative control of actions based on a count• WHILE loops that perform iterative control of actions based on a condition

LOOPINSERT INTO locations(location_id, city, country_id)VALUES((v_location_id + v_counter),v_city, v_country_id);v_counter := v_counter + 1;EXIT WHEN v_counter > 3;END LOOP;

WHILE v_counter <= 3 LOOPINSERT INTO locations(location_id, city, country_id)VALUES((v_location_id + v_counter), v_city, v_country_id);v_counter := v_counter + 1;END LOOP;

FOR i IN 1..3 LOOPINSERT INTO locations(location_id, city, country_id)VALUES((v_location_id + i), v_city, v_country_id );END LOOP;

A conditional control construct checks for the validity of a condition and performs a corresponding action accordingly. You use the IF construct to perform a conditional execution of statements.An iterative control construct executes a sequence of statements repeatedly, as long as a specifiedcondition holds TRUE. You use the various loop constructs to perform iterative operations.

Use the basic loop when the statements inside theloop must execute at least once.• Use the WHILE loop if the condition has to beevaluated at the start of each iteration.• Use a FOR loop if the number of iterations is known.

Composite Data TypeAre of two types:– PL/SQL RECORDs– PL/SQL Collections

– INDEX BY Table– Nested Table– VARRAY

• Contain internal components• Are reusable

A record is a group of related data items stored as fields, each with its own name and data type. A

Page 7: plsql

table contains a column and a primary key to give you array-like access to rows.

Record-suppose you have different kinds of data about an employee, such as name, salary, hire date,and so on. This data is dissimilar in type but logically related. A record that contains such fields as the name, salary, and hire date of an employee allows you to treat the data as a logical unit.TYPE emp_record_type IS RECORD(last_name VARCHAR2(25),job_id VARCHAR2(10),salary NUMBER(8,2));emp_record emp_record_type;a record is declared using %ROWTYPE as a data type specifier.DECLAREemp_record employees%ROWTYPE;

Index By table– A primary key of data type BINARY_INTEGER that indexes the INDEX BY table– A column of a scalar or record data type, which stores the INDEX BY table elements

TYPE ename_table_type IS TABLE OFemployees.last_name%TYPEINDEX BY BINARY_INTEGER;

The magnitude range of a BINARY_INTEGER is -2147483647 ... 2147483647, so the primary keyvalue can be negative. Indexing does not need to start with 1.

The following methods make INDEX BY tableseasier to use:– NEXT– TRIM– DELETE– EXISTS– COUNT– FIRST and LAST– PRIOR

Nested TablesWithin the database, nested tables can be considered one-column database tables.However, nested tables differ from arrays because arrays have a fixed upper bound, but nested tables have an extensible upper bound. Thus, the size of a nested table can be extended

VARRAYS??????????????????Items of the VARRAY type are called VARRAYS. They allow you to associate a single identifier withan entire collection. This association enables you manipulate the collection as a whole and to referenceindividual elements easily. To reference an element, you use standard subscripting syntax; forexample, GRADES(3) references the third element in VARRAY grades. A VARRAY has a maximumsize, which you must specify in its type definition. Its index has a fixed lower bound of 1 and anextensible upper bound. For example, the current upper bound for VARRAY grades is 7, but you canextend it to 8, 9, or 10. Thus a VARRAY can contain a varying number of elements, from zero (whenempty) to the maximum specified in its type definition.

Page 8: plsql

Explicit CursorYou use the OPEN, FETCH, and CLOSE statements to control a cursor.The OPEN statement executes the query associated with the cursor, identifies the result set, andpositions the cursor before the first row.The FETCH statement retrieves the current row and advances the cursor to the next row until eitherthere are no more rows or until the specified condition is met.Close the cursor when the last row has been processed. The CLOSE statement disables the cursor.DECLARECURSOR emp_cursor ISSELECT employee_id, last_nameFROM employees;emp_record emp_cursor%ROWTYPE;

OPEN emp_cursorFOR i IN 1..10 LOOPFETCH emp_cursor INTO v_empno, v_ename;...END LOOP;CLOSE emp_cursor;END;

Parameter to CursorDECLARECURSOR emp_cursor(p_deptno NUMBER, p_job VARCHAR2) ISSELECT employee_id, last_nameFROM employeesWHERE department_id = p_deptnoAND job_id = p_job;BEGINOPEN emp_cursor (80, 'SA_REP');. . .CLOSE emp_cursor;OPEN emp_cursor (60, 'IT_PROG');. . .END;

The FOR UPDATE ClauseYou may want to lock rows before you update or delete rows. Add the FOR UPDATE clause in thecursor query to lock the affected rows when the cursor is opened. Because the Oracle Server releaseslocks at the end of the transaction, you should not commit across fetches from an explicit cursor ifFOR UPDATE is used.NOWAIT returns an Oracle error if the rows are locked by another session

The SELECT ... FOR UPDATE statement identifies the rows that will be updated or deleted, thenlocks each row in the result set. This is useful when you want to base an update on the existing valuesin a row. In that case, you must make sure the row is not changed by another user before the update.The optional NOWAIT keyword tells Oracle not to wait if requested rows have been locked by anotheruser. Control is immediately returned to your program so that it can do other work before trying againto acquire the lock. If you omit the NOWAIT keyword , Oracle waits until the rows are available.

Page 9: plsql

CURSOR sal_cursor ISSELECT e.department_id, employee_id, last_name, salaryFROM employees e, departments dWHERE d.department_id = e.department_idand d.department_id = 60FOR UPDATE OF salary NOWAIT;BEGINFOR emp_record IN sal_cursorLOOPIF emp_record.salary < 5000 THENUPDATE employeesSET salary = emp_record.salary * 1.10WHERE CURRENT OF sal_cursor;END IF;END LOOP;

If you do a COMMIT before closing the cursor, the cursor will close.

The WHERE CURRENT OF ClauseWhen referencing the current row from an explicit cursor, use the WHERE CURRENT OF clause. Thisallows you to apply updates and deletes to the row currently being addressed, without the need toexplicitly reference the ROWID. You must include the FOR UPDATE clause in the cursor query so thatthe rows are locked on OPEN.

Cursors with SubqueriesA subquery is a query (usually enclosed by parentheses) that appears within another SQL datamanipulation statement. When evaluated, the subquery provides a value or set of values to the outerquery. Subqueries are often used in the WHERE clause of a select statement. They can also be used inthe FROM clause, creating a temporary data source for that query.

ExceptionYou raise an exception explicitly by issuing the RAISE statement within the block. Theexception being raised may be either user-defined or predefined.Trapping an ExceptionIf the exception is raised in the executable section of the block, processing branches to thecorresponding exception handler in the exception section of the block. If PL/SQL successfully handlesthe exception, then the exception does not propagate to the enclosing block or environment. ThePL/SQL block terminates successfully.Propagating an ExceptionIf the exception is raised in the executable section of the block and there is no corresponding exceptionhandler, the PL/SQL block terminates with failure and the exception is propagated to the callingenvironment.

WHEN OTHERS Exception Handler

Predefined Oracle Server errorDo not declare and allow the Oracle server to raise them implicitlyNonpredefined Oracle Server errorDeclare within the declarative section and allow the Oracle Server to raise them implicitlyUser-defined error Declare within the declarative section, and raise explicitly

Page 10: plsql

The exception-handling section traps only those exceptions that are specified; any other exceptions arenot trapped unless you use the OTHERS exception handler. This traps any exception not yet handled.For this reason, OTHERS is the last exception handler that is defined.

Trapping Nonpredefined Oracle Server ErrorsYou trap a nonpredefined Oracle server error by declaring it first, or by using the OTHERS handler.In PL/SQL, the PRAGMA EXCEPTION_INIT tells thecompiler to associate an exception name with an Oracle error number. ThatThat allows you to refer to anyinternal exception by name and to write a specific handler for it.

e_emps_remaining EXCEPTION;PRAGMA EXCEPTION_INIT(e_emps_remaining, -2292);BEGINDELETE FROM departmentsWHERE department_id = &p_deptno;COMMIT;EXCEPTIONWHEN e_emps_remaining THENDBMS_OUTPUT.PUT_LINE ('Cannot remove dept ' ||TO_CHAR(&p_deptno) || '. Employees exist. ');END;Error 2292 integrity constraint violation

SQLCODE Returns the numeric value for the error code (You can assign it to a NUMBERvariable.)SQLERRM Returns character data containing the message associated with the errorNumber 1- user defined 0-no exception

Trapping User-Defined ExceptionsPL/SQL allows you to define your own exceptions. User-defined PL/SQL exceptions must be:• Declared in the declare section of a PL/SQL block• Raised explicitly with RAISE statements

DECLAREe_invalid_department EXCEPTION;BEGINUPDATE departmentsSET department_name = '&p_department_desc'WHERE department_id = &p_department_number;IF SQL%NOTFOUND THENRAISE e_invalid_department;END IF;COMMIT;EXCEPTIONWHEN e_invalid_department THENDBMS_OUTPUT.PUT_LINE('No such department id.');END;

raise_application_error (error_number,message[, {TRUE | FALSE}]);Use the RAISE_APPLICATION_ERROR procedure to communicate a predefined exceptioninteractively by returning a nonstandard error code and error message. WithRAISE_APPLICATION_ERROR, you can report errors to your application and avoid returningunhandled exceptions.

Page 11: plsql

A subprogram is based on standard PL/SQL structure that contains a declarative section, anexecutable section, and an optional exception-handling section.A subprogram can be compiled and stored in the database. It provides modularity, extensibility,reusability, and maintainability.

Definition of a ProcedureA procedure is a named PL/SQL block that can accept parameters (sometimes referred to asarguments), and be invoked. Generally speaking, you use a procedure to perform an action. Aprocedure has a header, a declaration section, an executable section, and an optional exceptionhandlingsection.A procedure can be compiled and stored in the database as a schema object.Procedures promote reusability and maintainability.TheREPLACE option indicates that if the procedure exists, it will be dropped and replacedwith the new version created by the statement.

Formal Versus Actual ParametersFormal parameters are variables declared in the parameter list of a subprogram specification. Forexample, in the procedure RAISE_SAL, the variables P_ID and P_AMOUNT are formal parameters.Actual parameters are variables or expressions referenced in the parameter list of a subprogram call.For example, in the call raise_sal(v_id , 2000) to the procedure RAISE_SAL, the variableV_ID and 2000 are actual parameters.

IN (default) Passes a constant value from the calling environment into the procedureOUT Passes a value from the procedure to the calling environmentIN OUT Passes a value from the calling environment into the procedure and apossibly different value from the procedure back to the calling environment using the same parameter

the compiler hint NOCOPY?

CREATE OR REPLACE PROCEDURE query_emp(p_id IN employees.employee_id%TYPE,p_name OUT employees.last_name%TYPE,p_salary OUT employees.salary%TYPE,p_comm OUT employees.commission_pct%TYPE)ISBEGINSELECT last_name, salary, commission_pctINTO p_name, p_salary, p_commFROM employeesWHERE employee_id = p_id;END query_emp;EXECUTE query_emp(171, :g_name, :g_sal, :g_comm)PRINT g_name

Note: Passing a constant or expression as an actual parameter to the OUT variable causes compilationerrors. For example:EXECUTE query_emp(171, :g_name, raise+100, :g_comm)causes a compilation error.

CREATE OR REPLACE PROCEDURE format_phone(p_phone_no IN OUT VARCHAR2)ISBEGINp_phone_no := '(' || SUBSTR(p_phone_no,1,3) ||

Page 12: plsql

')' || SUBSTR(p_phone_no,4,3) ||'-' || SUBSTR(p_phone_no,7);END format_phone;VARIABLE g_phone_no VARCHAR2(15)BEGIN:g_phone_no := '8006330575';END;/PRINT g_phone_noEXECUTE format_phone (:g_phone_no)PRINT g_phone_no

Parameter passing methodPositional Lists values in the order in which the parameters are declaredNamed association Lists values in arbitrary order by associating each one with its parameter name, using special syntax (=>)Combination Lists the first values positionally, and the remainder using the special syntax of the named method

CREATE OR REPLACE PROCEDURE add_dept(p_name IN departments.department_name%TYPEDEFAULT 'unknown',p_loc IN departments.location_id%TYPEDEFAULT 1700)ISadd_dept ('TRAINING', 2500);add_dept ( p_loc => 2400, p_name =>'EDUCATION');add_dept ( p_loc => 1200)

CREATE OR REPLACE PROCEDURE leave_emp2(p_id IN employees.employee_id%TYPE)ISPROCEDURE log_execISBEGININSERT INTO log_table (user_id, log_date)VALUES (USER, SYSDATE);END log_exec;BEGINDELETE FROM employeesWHERE employee_id = p_id;log_exec;END leave_emp2;

DECLAREv_id NUMBER := 163;BEGINraise_salary(v_id); --invoke procedureCOMMIT;...END;DROP PROCEDURE raise_salary;

Stored Functions

Page 13: plsql

A function is a named PL/SQL block that can accept parameters and be invoked. Generally speaking,you use a function to compute a value. Functions and procedures are structured alike. A function mustreturn a value to the calling environment, whereas a procedure returns zero or more values to itscalling environment. Like a procedure, a function has a header, a declarative part, an executable part,and an optional exception-handling part. A function must have a RETURN clause in the header and atleast one RETURN statement in the executable section.

Use SHOW ERRORS to see any compilation errors.CREATE OR REPLACE FUNCTION get_sal(p_id IN employees.employee_id%TYPE)RETURN NUMBERISv_salary employees.salary%TYPE :=0;BEGINSELECT salaryINTO v_salaryFROM employeesWHERE employee_id = p_id;RETURN v_salary;END get_sal;RETURNstatement in the executable section of the code. There can be a RETURN statement in the exceptionsection of the program also.

VARIABLE g_salary NUMBEREXECUTE :g_salary := get_sal(117)PRINT g_salary

Restrictions on Calling Functions from SQL ExpressionsParameters to a PL/SQL function called from a SQL statement must use positional notation.Named notation is not supported.• Stored PL/SQL functions cannot be called from the CHECK constraint clause of a CREATE orALTER TABLE command or be used to specify a default value for a column.• You must own or have the EXECUTE privilege on the function to call it from a SQL statement.• The functions must return data types that are valid SQL data types. They cannot be PL/SQLspecificdata types such as BOOLEAN, RECORD, or TABLE. The same restriction applies toparameters of the function.When called froma SELECT statement or a parallelized UPDATE or DELETE statement, thefunction cannot modify any database tables.• When called froman UPDATE, or DELETE statement, the function cannot query or modify anydatabase tables modified by that statement.• When called froma SELECT, INSERT, UPDATE, or DELETE statement, the function cannotexecute SQL transaction control statements (such as COMMIT), session control statements (suchas SET ROLE), or system control statements (such as ALTER SYSTEM). Also, it cannotexecute DDL statements (such as CREATE) because they are followed by an automatic commit.• The function cannot call another subprogram that breaks one of the above restrictions.

How Procedures and Functions DifferYou create a procedure to store a series of actions for later execution. A procedure can contain zero or

Page 14: plsql

more parameters that can be transferred to and from the calling environment, but a procedure does nothave to return a value.You create a function when you want to compute a value, which must be returned to the callingenvironment. A function can contain zero or more parameters that are transferred from the callingenvironment. Functions should return only a single value, and the value is returned through a RETURNstatement. Functions used in SQL statements cannot have OUT or IN OUT mode parameters.

following benefits:• Improved performance– Avoid reparsing for multiple users by exploiting the shared SQL area– Avoid PL/SQL parsing at run time by parsing at compile time– Reduce the number of calls to the database and decrease network traffic by bundlingcommands• Easy maintenance– Modify routines online without interfering with other users– Modify one routine to affect multiple applications– Modify one routine to eliminate duplicate testing• Improved data security and integrity– Control indirect access to database objects from nonprivileged users with securityprivileges– Ensure that related actions are performed together, or not at all, by funneling activity forrelated tables through a single path• Improved code clarity: By using appropriate identifier names to describe the actions of theroutine, you reduce the need for comments and enhance clarity.

CREATE (ANY) PROCEDUREALTER ANY PROCEDUREDROP ANY PROCEDUREEXECUTE ANY PROCEDURE

Using USER_SOURCETo obtain the text of a stored procedure or function, use the USER_SOURCE data dictionary view.

Obtaining Compile ErrorsTo obtain the text for compile errors, use the USER_ERRORS data dictionary view or the SHOWERRORS iSQL*Plus command.CREATE OR REPLACE PROCEDURE log_execution

SELECT line || '/' || position POS, textFROM user_errorsWHERE name = 'LOG_EXECUTION'ORDER BY line;

SHOW ERRORS [{FUNCTION|PROCEDURE|PACKAGE|PACKAGEBODY|TRIGGER|VIEW} [schema.]name]

The DBMS_OUTPUT package:– Accumulates information into a buffer– Allows retrieval of the information from the buffer• Autonomous procedure calls (for example, writingthe output to a log table)• Software that uses DBMS_DEBUG– Procedure Builder– Third-party debugging software

Packages Overview

Page 15: plsql

Packages bundle related PL/SQL types, items, and subprograms into one container.A package usually has a specification and a body, stored separately in the database.The specification is the interface to your applications. It declares the types, variables, constants,exceptions, cursors, and subprograms available for use. The package specification may also includePRAGMAs, which are directives to the compiler.The body fully defines cursors and subprograms, and so implements the specification.The package itself cannot be called, parameterized, or nested. Still, the format of a package is similar tothat of a subprogram. Once written and compiled, the contents can be shared by many applications.When you call a packaged PL/SQL construct for the first time, the whole package is loaded intomemory. Thus, later calls to constructs in the same package require no disk input/output (I/O).

Package DevelopmentYou create a package in two parts: first the package specification, and then the package body. Publicpackage constructs are those that are declared in the package specification and defined in the packagebody. Private package constructs are those that are defined solely within the package body.The Oracle server stores the specification and body of a package separately in the database. Thisenables you to change the definition of a program construct in the package body without causing theOracle server to invalidate other schema objects that call or reference the program construct.1.Write the text of the CREATE PACKAGE statement within a SQL script file to create the packagespecification and run the script file. The source code is compiled into P code and is stored withinthe data dictionary.2. Write the text of the CREATE PACKAGE BODY statement within a SQL script file to create thepackage body and run the script file.The source code is compiled into P code and is also stored within the data dictionary.3. Invoke any public construct within the package from an Oracle server environment.

Package specs-package_name Name the package-public type anditem declarations-Declare variables, constants, cursors, exceptions, or types subprogram specifications-Declare the PL/SQL subprograms

CREATE OR REPLACE PACKAGE comm_package ISg_comm NUMBER := 0.10; --initialized to 0.10PROCEDURE reset_comm(p_comm IN NUMBER);END comm_package;/• G_COMM is a global variable and is initialized to 0.10.• RESET_COMM is a public procedure that isimplemented in the package body.

CREATE [OR REPLACE] PACKAGE BODY package_nameIS|ASprivate type and item declarationssubprogram bodiesEND package_name;• The REPLACE option drops and recreates thepackage body.• Identifiers defined only in the package body are

Page 16: plsql

private constructs. These are not visible outsidethe package body.• All private constructs must be declared beforethey are used in the public constructs.

package_name Is the name of the packageprivate type anditem declarationsDeclares variables, constants, cursors, exceptions, or typessubprogram bodies Defines the PL/SQL subprograms, public and private

When you are coding thepackage body, the definition of the private function has to be above the definition of the publicprocedure.

Invoking Package ConstructsAfter the package is stored in the database, you can invoke a package construct within the package orfrom outside the package, depending on whether the construct is private or public.When you invoke a package procedure or function from within the same package, you do not need toqualify its name.When you invoke a package procedure or function from outside the package, you must qualify itsname with the name of the package.Example 2Call the RESET_COMM procedure from iSQL*Plus, making the prevailing commission 0.15 for theuser session.Example 3Call the RESET_COMM procedure that is located in the SCOTT schema from iSQL*Plus, making theprevailing commission 0.15 for the user session.Example 4Call the RESET_COMM procedure that is located in a remote database that is determined by thedatabase link named NY from iSQL*Plus, making the prevailing commission 0.15 for the user session.

Declaring a Bodiless PackageYou cancreate a package specification that does not need a package body. As discussed earlier in thislesson, if a specification declares only types, constants, variables, exceptions, and callspecifications, the package body is unnecessary.CREATE OR REPLACE PACKAGE global_consts ISmile_2_kilo CONSTANT NUMBER := 1.6093;kilo_2_mile CONSTANT NUMBER := 0.6214;yard_2_meter CONSTANT NUMBER := 0.9144;meter_2_yard CONSTANT NUMBER := 1.0936;END global_consts;/EXECUTE DBMS_OUTPUT.PUT_LINE('20 miles = '||20*global_consts.mile_2_kilo||' km')

Referencing a Public Variable froma Stand-Alone ProcedureExample:CREATE OR REPLACE PROCEDURE meter_to_yard(p_meter IN NUMBER, p_yard OUT NUMBER)IS

Page 17: plsql

BEGINp_yard := p_meter * global_consts.meter_2_yard;END meter_to_yard;/VARIABLE yard NUMBEREXECUTE meter_to_yard (1, :yard)PRINT yard

To remove the package specification and the body,use the following syntax:To remove the package body, use the following syntax:DROP PACKAGE package_name;Removing PackagesDROP PACKAGE BODY package_name;

The package specification should contain onlythose constructs that you want to be public.Place items in the declaration part of the packagebody when you must maintain them throughouta session or across transactions

Advantages of Using PackagesPackages provide an alternative to creating procedures and functions as stand-alone schema objects, andthey offer several benefits.ModularityYou encapsulate logically related programming structures in a named module. Each package is easy tounderstand, and the interface between packages is simple, clear, and well defined.Easier Application DesignAll you need initially is the interface information in the package specification. You can code andcompile a specification without its body. Then stored subprograms that reference the package cancompile as well. You need not define the package body fully until you are ready to complete theapplication.Hiding InformationYou can decide which constructs are public (visible and accessible) or private (hidden and inaccessible).Only the declarations in the package specification are visible and accessible to applications. The packagebody hides the definition of the private constructs so that only the package is affected (not yourapplication or any calling programs) if the definition changes. This enables you to change theimplementation without having to recompile calling programs. Also, by hiding implementation detailsfrom users, you protect the integrity of the package.Added FunctionalityPackaged public variables and cursors persist for the duration of a session. Thus, they can be shared byall subprograms that execute in the environment. They also enable you to maintain data acrosstransactions without having to store it in the database. Private constructs also persist for the duration ofthe session, but can only be accessed within the package.Better PerformanceWhen you call a packaged subprogram the first time, the entire package is loaded into memory. Thisway, later calls to related subprograms in the package require no further disk I/O. Packaged

Page 18: plsql

subprograms also stop cascading dependencies and so avoid unnecessary compilation.OverloadingWith packages you can overload procedures and functions, which means you can create multiplesubprograms with the same name in the same package, each taking parameters of different number ordatatype.

OverloadingThis feature enables you to define different subprograms with the same name. You can distinguish thesubprograms both by name and by parameters. Sometimes the processing in two subprograms is thesame, but the parameters passed to them varies. In that case it is logical to give them the same name.PL/SQL determines which subprogram is called by checking its formal parameters. Only local orpackaged subprograms can be overloaded. Stand-alone subprograms cannot be overloaded.RestrictionsYou cannot overload:• Two subprograms if their formal parameters differ only in data type and the different data typesare 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 arebased on types in the same family (VARCHAR and STRING are PL/SQL subtypes ofVARCHAR2)• Two functions that differ only in return type, even if the types are in different familiesYou get a run-time error when you overload subprograms with the above features.Note: The above restrictions apply if the names of the parameters are also the same. If you usedifferent names for the parameters, then you can invoke the subprograms by using named notation forthe parameters.You can overload two subprograms if their formal parameters differ only in name or parameter mode.

CREATE OR REPLACE PACKAGE overload ISprocedure p (x number);procedure p ( n number);END;/EXECUTE overload.p(4)overload.p(4); END;*ERROR at line 1:ORA-06550: line 1, column 7:PLS-00307: too many declarations of 'P' match this callORA-06550: line 1, column 7:PL/SQL: Statement ignoredNow, invoke the procedure with named notation:EXECUTE overload.p(x => 4)

For example, see the TO_CHAR function of theSTANDARD package.FUNCTION TO_CHAR (p1 DATE) RETURN VARCHAR2;FUNCTION TO_CHAR (p2 NUMBER) RETURN VARCHAR2;FUNCTION TO_CHAR (p1 DATE, P2 VARCHAR2) RETURN VARCHAR2;FUNCTION TO_CHAR (p1 NUMBER, P2 VARCHAR2) RETURN VARCHAR2;

Page 19: plsql

• If you redeclare a built-in subprogram in a PL/SQLprogram, your local declaration overrides theglobal declaration.

Using Forward DeclarationsPL/SQL does not allow forward references. You must declare an identifier before using it. Therefore, asubprogram must be declared before calling it.

PL/SQL enables for a special subprogram declaration called a forward declaration. It consists of thesubprogram specification terminated by a semicolon. You can use forward declarations to do thefollowing:• Define subprograms in logical or alphabetical order• Define mutually recursive subprograms• Group subprograms in a package

CREATE OR REPLACE PACKAGE BODY forward_packISPROCEDURE calc_rating(. . .); -- forward declarationPROCEDURE award_bonus(. . .)IS -- subprograms definedBEGIN -- in alphabetical ordercalc_rating(. . .);. . .END;PROCEDURE calc_rating(. . .)ISBEGIN. . .END;END forward_pack;

PRAGMA RESTRICT_REFERENCES ???????????????

Describe the use and application of some Oracleserver-supplied packages:– DBMS_DDL– DBMS_JOB– DBMS_OUTPUT– UTL_FILE– UTL_HTTP and UTL_TCPWrite dynamic SQL statements using DBMS_SQLand EXECUTE IMMEDIATEMost of the standard packages are created by running catproc.sql…..sys schema

Using Native Dynamic SQL (Dynamic SQL)You can write PL/SQL blocks that use dynamic SQL. Dynamic SQL statements are not embedded inyour source program but rather are stored in character strings that are input to, or built by, the program.That is, the SQL statements can be created dynamically at run time by using variables. For example,you use dynamic SQL to create a procedure that operates on a table whose name is not known until runtime, or to write and execute a data definition language (DDL) statement (such as CREATE TABLE), adata control statement (such as GRANT), or a session control statement (such as ALTER SESSION). In

Page 20: plsql

PL/SQL, such statements cannot be executed statically.In Oracle8, and earlier, you have to use DBMS_SQL to write dynamic SQL.In Oracle 8i, you can use DBMS_SQL or native dynamic SQL. The EXECUTE IMMEDIATE statementcan perform dynamic single-row queries. Also, this is used for functionality such as objects andcollections, which are not supported by DBMS_SQL. If the statement is a multirow SELECT statement,you use OPEN-FOR, FETCH, and CLOSE statements.

SQL statements go through various stages:• Parse• Bind• Execute• Fetch

Using the DBMS_SQL PackageThe DBMS_SQL package is used to write dynamic SQLin stored procedures and to parse DDL statements.Some of the procedures and functions of the packageinclude:– OPEN_CURSOR– PARSE– BIND_VARIABLE– EXECUTE– FETCH_ROWS– CLOSE_CURSORUsing the DBMS_SQL PackageUsing DBMS_SQL, you can write stored procedures and anonymous PL/SQL blocks that use dynamicSQL.DBMS_SQL can issue data definition language statements in PL/SQL. For example, you can choose toissue a DROP TABLE statement from within a stored procedure.The operations provided by this package are performed under the current user, not under the packageowner SYS. Therefore, if the caller is an anonymous PL/SQL block, the operations are performedaccording to the privileges of the current user; if the caller is a stored procedure, the operations areperformed according to the owner of the stored procedure.Using this package to execute DDL statements can result in a deadlock. The most likely reason for thisis that the package is being used to drop a procedure that you are still using.

SELECT text FROM all_source WHERE name ='DBMS_SQL' ORDER BY LINE;

DBMS_SQLCREATE OR REPLACE PROCEDURE delete_all_rows(p_tab_name IN VARCHAR2, p_rows_del OUT NUMBER)IScursor_name INTEGER;BEGINcursor_name := DBMS_SQL.OPEN_CURSOR;DBMS_SQL.PARSE(cursor_name, 'DELETE FROM '||p_tab_name,DBMS_SQL.NATIVE );p_rows_del := DBMS_SQL.EXECUTE (cursor_name);DBMS_SQL.CLOSE_CURSOR(cursor_name);END;Use dynamic SQL to delete rowsVARIABLE deleted NUMBER

Page 21: plsql

EXECUTE delete_all_rows('employees', :deleted)PRINT deleted

Execute ImmediateUse the EXECUTE IMMEDIATE statement for nativedynamic SQL with better performance.• INTO is used for single-row queries and specifiesthe variables or records into which column valuesare retrieved.• USING is used to hold all bind arguments. Thedefault parameter mode is IN.

dynamic_string A string expression that represents a dynamic SQL statement (withoutterminator) or a PL/SQL block (with terminator)define_variable A variable that stores the selected column valuerecord A user-defined or %ROWTYPE record that stores a selected rowbind_argument An expression whose value is passed to the dynamic SQL statement orPL/SQL blockYou can use the INTO clause for a single-row query, but you must use OPEN-FOR, FETCH, andCLOSE for a multirow query.CREATE PROCEDURE del_rows(p_table_name IN VARCHAR2,p_rows_deld OUT NUMBER)ISBEGINEXECUTE IMMEDIATE 'delete from '||p_table_name;p_rows_deld := SQL%ROWCOUNT;END;The EXECUTE IMMEDIATE statement prepares (parses) and immediately executes thedynamic SQL statement.

EXECUTE IMMEDIATE dynamic_string[INTO {define_variable[, define_variable] ... | record}][USING [IN|OUT|IN OUT] bind_argument[, [IN|OUT|IN OUT] bind_argument] ... ];In the EXECUTE IMMEDIATE statement:• TheINTO clause specifies the variables or record into which column values are retrieved. It isused only for single-row queries. For each value retrieved by the query, there must be acorresponding, type-compatible variable or field in the INTO clause.• TheRETURNING INTO clause specifies the variables into which column values are returned.It is used only for DML statements that have a RETURNING clause (without a BULKCOLLECT clause). For each value returned by the DML statement, there must be acorresponding, type-compatible variable in the RETURNING INTO clause.• TheUSING clause holds all bind arguments. The default parameter mode is IN. For DMLstatements that have a RETURNING clause, you can place OUT arguments in the RETURNINGINTO clause without specifying the parameter mode, which, by definition, is OUT. If you useboth the USING clause and the RETURNING INTO clause, the USING clause can contain onlyIN arguments.

Using the DBMS_DDL PackageThe DBMS_DDL Package:• Provides access to some SQL DDL statementsfrom stored procedures• Includes some procedures:– ALTER_COMPILE (object_type, owner, object_name)– ANALYZE_OBJECT (object_type, owner, name,method)Note: This package runs with the privileges of calling

Page 22: plsql

user, rather than the package owner SYS.DBMS_DDL.ALTER_COMPILE('PROCEDURE','A_USER','QUERY_EMP')DBMS_DDL.ANALYZE_OBJECT('TABLE','A_USER','JOBS','COMPUTE')Practical Uses• You can recompile your modified PL/SQL program units by usingDBMS_DDL.ALTER_COMPILE. The object type must be either procedure, function, package,package body, or trigger.• You can analyze a single object, using DBMS_DDL.ANALYZE_OBJECT. (There is a way ofanalyzing more than one object at a time, using DBMS_UTILITY.) The object type should beTABLE, CLUSTER, or INDEX. The method must be COMPUTE, ESTIMATE, or DELETE.• This package gives developers access to ALTER and ANALYZE SQL statements through PL/SQLenvironments.

Using DBMS_JOB for SchedulingDBMS_JOB Enables the scheduling and execution ofPL/SQL programs:• Submitting jobs• Executing jobs• Changing execution parameters of jobs• Removing jobs• Suspending Jobs

DBMS_JOB SubprogramsAvailable subprograms include:• SUBMIT• REMOVE• CHANGE• WHAT• NEXT_DATE• INTERVAL• BROKEN• RUNDBMS_JOB SubprogramsSubprogram DescriptionSUBMIT Submits a job to the job queueREMOVE Removes a specified job from the job queueCHANGE Alters a specified job that has already been submitted to thejob queue (you can alter the job description, the time atwhich the job will be run, or the interval between executionsof the job)WHAT Alters the job description for a specified jobNEXT_DATE Alters the next execution time for a specified jobINTERVAL Alters the interval between executions for a specified jobBROKEN Disables job execution (if a job is marked as broken, theOracle server does not attempt to execute it)RUN Forces a specified job to run

DBMS_JOB.SUBMIT (job => :jobno,what => 'OVER_PACK.ADD_DEPT(''EDUCATION'',2710);',next_date => TRUNC(SYSDATE + 1),interval => 'TRUNC(SYSDATE + 1)'

Using the DBMS_OUTPUT PackageThe DBMS_OUTPUT package enables you to outputmessages from PL/SQL blocks. Available proceduresinclude:• PUT

Page 23: plsql

• NEW_LINE• PUT_LINE• GET_LINE• GET_LINES• ENABLE/DISABLEPUT Appends text from the procedure to the current line of the lineoutput bufferNEW_LINE Places an end_of_line marker in the output bufferPUT_LINE Combines the action of PUT and NEW_LINEGET_LINE Retrieves the current line from the output buffer into theprocedureGET_LINES Retrieves an array of lines from the output buffer into theprocedureENABLE/DISABLE Enables or disables calls to the DBMS_OUTPUT procedures

UTL_FILE Oracle-supplied package:– Provides text file I/O capabilities– Is available with version 7.3 and later• The DBMS_LOB Oracle-supplied package:– Provides read-only operations on external BFILES– Is available with version 8 and later– Enables read and write operations on internal LOBs

Is similar to standard operating system I/O– Open files– Get text– Put text– Close files– Use the exceptions specific to the UTL_FILEpackage

UTL_FILE Procedures and Functions• Function FOPEN• Function IS_OPEN• Procedure GET_LINE• Procedure PUT, PUT_LINE, PUTF• Procedure NEW_LINE• Procedure FFLUSH• Procedure FCLOSE, FCLOSE_ALLThe UTL_FILE Package: Procedures and FunctionsNote: The maximum size of an input record is 1,023 bytes unless you specify a larger size in theoverloaded version of FOPEN.Function or Procedure DescriptionFOPEN A function that opens a file for input or output and returns a filehandle used in subsequent I/O operationsIS_OPEN A function that returns a Boolean value whenever a file handlerefers to an open fileGET_LINE A procedure that reads a line of text from the opened file andplaces the text in the output buffer parameter (the maximum sizeof an input record is 1,023 bytes unless you specify a larger sizein the overloaded version of FOPEN)PUT, PUT_LINE A procedure that writes a text string stored in the bufferparameter to the opened file (no line terminator is appended byput; use new_line to terminate the line, or use PUT_LINEto write a complete line with a terminator)PUTF A formatted put procedure with two format specifiers: %s and\n (use %s to substitute a value into the output string. \n is anew line character)NEW_LINE Procedure that terminates a line in an output file

Page 24: plsql

FFLUSH Procedure that writes all data buffered in memory to a fileFCLOSE Procedure that closes an opened fileFCLOSE_ALL Procedure that closes all opened file handles for the session

IF v_newdeptno <> v_olddeptno THENUTL_FILE.PUTF (v_filehandle, 'DEPARTMENT: %s\n',v_emp_rec.department_id);END IF;UTL_FILE.PUTF (v_filehandle,' EMPLOYEE: %s earns: %s\n',v_emp_rec.last_name, v_emp_rec.salary);v_olddeptno := v_newdeptno;END LOOP;UTL_FILE.PUT_LINE (v_filehandle, '*** END OF REPORT ***');UTL_FILE.FCLOSE (v_filehandle);EXCEPTIONWHEN UTL_FILE.INVALID_FILEHANDLE THENRAISE_APPLICATION_ERROR (-20001, 'Invalid File.');WHEN UTL_FILE.WRITE_ERROR THENRAISE_APPLICATION_ERROR (-20002, 'Unable to write tofile');

The UTL_HTTP PackageUTL_HTTP is a package that allows you to make HTTP requests directly from the database. TheUTL_HTTP package makes hypertext transfer protocol (HTTP) callouts from PL/SQL and SQL. Youcan use it to access data on the Internet or to call Oracle Web Server Cartridges. By couplingUTL_HTTP with the DBMS_JOBS package, you can easily schedule reoccurring requests be made fromyour database server out to theWeb.

Using the UTL_TCP PackageThe UTL_TCP package enables PL/SQL applications to communicate with external TCP/IP-based serversusing TCP/IP. Because many Internet application protocols are based on TCP/IP, this package is useful toPL/SQL applications that use Internet protocols.

Guidelines for Creating Directory ObjectsTo associate an operating system file to a BFILE, you should first create a DIRECTORY object that isan alias for the full pathname to the operating system file.

Recursive trigger: This is a trigger that contains a DML operation changing the very same table.• Cascading trigger: The action of one trigger cascades to another trigger, causing this secondtrigger to fire. The Oracle server allows up to 32 triggers to cascade at any one time. However,the number of cascading triggers can be limited by changing the value of the OPEN_CURSORSdatabase initialization parameter, which is set to 50 by default.

Trigger timing When the trigger fires in relation to the triggering eventBEFOREAFTERINSTEAD OFTriggering event Which data manipulation operation on the table or view causes the trigger to fireINSERTUPDATEDELETETrigger type How many times the trigger bodyexecutesStatementRow

Page 25: plsql

Trigger body What action the trigger performs Complete PL/SQL block

Trigger timing: When should the trigger fire?• BEFORE: Execute the trigger body before thetriggering DML event on a table.• AFTER: Execute the trigger body after thetriggering DML event on a table.• INSTEAD OF: Execute the trigger body instead ofthe triggering statement. This is used for viewsthat are not otherwise modifiable.

Trigger type: Should the trigger body execute for eachrow the statement affects or only once?Statement TriggerA statement trigger is fired once on behalf of the triggering event, even if no rows are affected at all.Statement triggers are useful if the trigger action does not depend on the data from rows that areaffected or on data provided by the triggering event itself: for example, a trigger that performs acomplex security check on the current user.Row TriggerA row trigger fires each time the table is affected by the triggering event. If the triggering event affectsno rows, a row trigger is not executed.Row triggers are useful if the trigger action depends on data of rows that are affected or on dataprovided by the triggering event itself.

CREATE OR REPLACE TRIGGER secure_empBEFORE INSERT ON employeesBEGINIF (TO_CHAR(SYSDATE,'DY') IN ('SAT','SUN')) OR(TO_CHAR(SYSDATE,'HH24:MI')NOT BETWEEN '08:00' AND '18:00')THEN RAISE_APPLICATION_ERROR (-20500,'You mayinsert into EMPLOYEES table onlyduring business hours.');END IF;END;

CREATE OR REPLACE TRIGGER secure_empBEFORE INSERT OR UPDATE OR DELETE ON employeesBEGINIF (TO_CHAR (SYSDATE,'DY') IN ('SAT','SUN')) OR(TO_CHAR (SYSDATE, 'HH24') NOT BETWEEN '08' AND '18')THENIF DELETING THENRAISE_APPLICATION_ERROR (-20502,'You may delete fromEMPLOYEES table only during business hours.');ELSIF INSERTING THENRAISE_APPLICATION_ERROR (-20500,'You may insert intoEMPLOYEES table only during business hours.');ELSIF UPDATING ('SALARY') THENRAISE_APPLICATION_ERROR (-20503,'You may updateSALARY only during business hours.');ELSERAISE_APPLICATION_ERROR (-20504,'You may updateEMPLOYEES table only during normal hours.');END IF;END IF;

Page 26: plsql

END;

CREATE OR REPLACE TRIGGER restrict_salaryBEFORE INSERT OR UPDATE OF salary ON employeesFOR EACH ROWBEGINIF NOT (:NEW.job_id IN ('AD_PRES', 'AD_VP'))AND :NEW.salary > 15000THENRAISE_APPLICATION_ERROR (-20202,'Employeecannot earn this amount');END IF;END;

CREATE OR REPLACE TRIGGER audit_emp_valuesAFTER DELETE OR INSERT OR UPDATE ON employeesFOR EACH ROWBEGININSERT INTO audit_emp_table (user_name, timestamp,id, old_last_name, new_last_name, old_title,new_title, old_salary, new_salary)VALUES (USER, SYSDATE, :OLD.employee_id,:OLD.last_name, :NEW.last_name, :OLD.job_id,:NEW.job_id, :OLD.salary, :NEW.salary );END;

Why Use INSTEAD OF Triggers?A view cannot be modified by normal DML statements if the view query contains set operators, groupfunctions, 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 insertioninto one table and an update to another. So, you write an INSTEAD OF trigger that fires when youwrite an insert against the view. Instead of the original insertion, the trigger body executes, whichresults in an insertion of data into one table and an update to another table

CREATE OR REPLACE TRIGGER new_emp_deptINSTEAD OF INSERT OR UPDATE OR DELETE ON emp_detailsFOR EACH ROWBEGINIF INSERTING THENINSERT INTO new_empsVALUES (:NEW.employee_id, :NEW.last_name, :NEW.salary,:NEW.department_id, :NEW.email, :New.job_id, SYSDATE);UPDATE new_deptsSET tot_dept_sal = tot_dept_sal + :NEW.salaryWHERE department_id = :NEW.department_id;ELSIF DELETING THENDELETE FROM new_empsWHERE employee_id = :OLD.employee_id;UPDATE new_deptsSET tot_dept_sal = tot_dept_sal - :OLD.salaryWHERE department_id = :OLD.department_id;

ALTER TRIGGER trigger_name COMPILE

CREATE OR REPLACE TRIGGER audit_emp_trigAFTER UPDATE or INSERT or DELETE on EMPLOYEESFOR EACH ROW

Page 27: plsql

BEGINIF DELETING THEN var_pack.set_g_del(1);ELSIF INSERTING THEN var_pack.set_g_ins(1);ELSIF UPDATING ('SALARY')THEN var_pack.set_g_up_sal(1);ELSE var_pack.set_g_upd(1);END IF;END audit_emp_trig;

AUDIT INSERT, UPDATE, DELETEON departmentsBY ACCESSWHENEVER SUCCESSFUL;

Cursor Variables• Cursor variables are like C or Pascal pointers,which hold the memory location (address) of anitem instead of the item itself• In PL/SQL, a pointer is declared as REF X, whereREF is short for REFERENCE and X stands for aclass of objects• A cursor variable has the data type REF CURSOR• A cursor is static, but a cursor variable is dynamic• Cursor variables give you more flexibility

Why Use Cursor Variables?• You can use cursor variables to pass query resultsets between PL/SQL stored subprograms andvarious clients.• PL/SQL can share a pointer to the query work areain which the result set is stored.• You can pass the value of a cursor variable freelyfrom one scope to another.• You can reduce network traffic by having aPL/SQL block open (or close) several host cursorvariables in a single round trip.

Define a REF CURSOR type.Define a REF CURSOR typeTYPE ref_type_name IS REF CURSOR [RETURN return_type];• Declare a cursor variable of that type.ref_cv ref_type_name;• Example:DECLARETYPE DeptCurTyp IS REF CURSOR RETURNdepartments%ROWTYPE;dept_cv DeptCurTyp;

TYPE EmpCurTyp IS REF CURSOR RETURN employees%ROWTYPE; -- strongTYPE GenericCurTyp IS REF CURSOR; -- weak

TYPE EmpRecTyp IS RECORD (empno NUMBER(4),ename VARCHAR2(1O),sal NUMBER(7,2));TYPE EmpCurTyp IS REF CURSOR RETURN EmpRecTyp;emp_cv EmpCurTyp; -- declare cursor variable

Using the OPEN-FOR, FETCH, and CLOSE Statements

Page 28: plsql

You use three statements to process a dynamic multirow query: OPEN-FOR, FETCH, and CLOSE.First, you OPEN a cursor variable FOR a multirow query. Then, you FETCH rows from the result setone at a time. When all the rows are processed, you CLOSE the cursor variable.Opening the Cursor VariableThe OPEN-FOR statement associates a cursor variable with a multirow query, executes the query,identifies the result set, positions the cursor to point to the first row of the results set, then sets therows-processed count kept by %ROWCOUNT to zero. Unlike the static form of OPEN-FOR, thedynamic form has an optional USING clause. At run time, bind arguments in the USING clausereplace corresponding placeholders in the dynamic SELECT statement. The syntax is:OPEN {cursor_variable | :host_cursor_variable} FOR dynamic_string[USING bind_argument[, bind_argument]...];

DECLARETYPE EmpCurTyp IS REF CURSOR; -- define weak REF CURSOR typeemp_cv EmpCurTyp; -- declare cursor variablemy_ename VARCHAR2(15);my_sal NUMBER := 1000;BEGINOPEN emp_cv FOR -- open cursor variable'SELECT last_name, salary FROM employees WHERE salary > :s'USING my_sal;...END;

DECLARETYPE EmpCurTyp IS REF CURSOR;emp_cv EmpCurTyp;emp_rec employees%ROWTYPE;sql_stmt VARCHAR2(200);my_job VARCHAR2(10) := 'ST_CLERK';BEGINsql_stmt := 'SELECT * FROM employeesWHERE job_id = :j';OPEN emp_cv FOR sql_stmt USING my_job;LOOPFETCH emp_cv INTO emp_rec;EXIT WHEN emp_cv%NOTFOUND;-- process recordEND LOOP;CLOSE emp_cv;END;/An Example of FetchingThe example in the preceding slide shows that you can fetch rows from the result set of a dynamicmultirow query into a record. First you must define a REF CURSOR type, EmpCurTyp. Next youdefine a cursor variable emp_cv, of the type EmpcurTyp. In the executable section of the PL/SQLblock, the OPEN-FOR statement associates the cursor variable EMP_CV with the multirow query,sql_stmt. The FETCH statement returns a row from the result set of a multirow query and assignsthe values of select-list items to EMP_REC in the INTO clause. When the last row is processed, closethe cursor variable EMP_CV.

Page 29: plsql

NOCOPY Compiler Hint

Suppose a subprogram declares an IN parameter, an OUT parameter, and an IN OUT parameter. When you call the subprogram, the IN parameter is passed by reference. That is, a pointer to the IN actual parameter is passed to the corresponding formal parameter. So, both parameters reference the same memory location, which holds the value of the actual parameter. By default, the OUT and IN OUT parameters are passed by value. That is, the value of the IN OUT actual parameter is copied into the corresponding formal parameter. Then, if the subprogram exits normally, the values assigned to the OUT and IN OUT formal parameters are copied into the corresponding actual parameters. When the parameters hold large data structures such as collections, records, and instances of object types, all this copying slows down execution and uses up memory. To prevent that, you can specify the NOCOPY hint, which allows the PL/SQL compiler to pass OUT and IN OUT parameters by reference. In the following example, you ask the compiler to pass IN OUT parameter my_staff by reference instead of by value: DECLARE TYPE Staff IS VARRAY(200) OF Employee; PROCEDURE reorganize (my_staff IN OUT NOCOPY Staff) IS ...

Remember, NOCOPY is a hint, not a directive. So, the compiler might pass my_staff by value despite your request. Usually, however, NOCOPY succeeds. So, it can benefit any PL/SQL application that passes around large data structures.

OverloadingPL/SQL lets you overload subprogram names. That is, you can use the same name for several different subprograms as long as their formal parameters differ in number, order, or datatype family.

CollectionA collection is an ordered group of elements, all of the same type (for example, the grades for a class of students) Collections work like the arrays found in most third-generation programming languages. However, collections can have only one dimension and must be indexed by integers

Taking Advantage of Bulk Binds

Embedded in the Oracle RDBMS, the PL/SQL engine accepts any valid PL/SQL block or subprogram. As Figure 4-3 shows, the PL/SQL engine executes procedural statements but sends SQL statements to the SQL engine, which executes the SQL statements and, in some cases, returns data to the PL/SQL engine.

Figure 4-3 Context Switching

Page 30: plsql

Each context switch between the PL/SQL and SQL engines adds to overhead. So, if many switches are required, performance suffers. That can happen when SQL statements execute inside a loop using collection (index-by table, nested table, varray, or host array) elements as bind variables. For example, the following DELETE statement is sent to the SQL engine with each iteration of the FOR loop: DECLARE TYPE NumList IS VARRAY(20) OF NUMBER; depts NumList := NumList(10, 30, 70, ...); -- department numbersBEGIN ... FOR i IN depts.FIRST..depts.LAST LOOP ... DELETE FROM emp WHERE deptno = depts(i); END LOOP;END;

In such cases, if the SQL statement affects five or more database rows, the use of bulk binds can improve performance considerably.

How Do Bulk Binds Improve Performance?

The assigning of values to PL/SQL variables in SQL statements is called binding. The binding of an entire collection at once is called bulk binding. Bulk binds improve performance by minimizing the number of context switches between the PL/SQL and SQL engines. With bulk binds, entire collections, not just individual elements, are passed back and forth. For example, the following DELETE statement is sent to the SQL engine just once, with an entire nested table: DECLARE TYPE NumList IS TABLE OF NUMBER; mgrs NumList := NumList(7566, 7782, ...); -- manager numbersBEGIN ... FORALL i IN mgrs.FIRST..mgrs.LAST DELETE FROM emp WHERE mgr = mgrs(i);END;

In the example below, 5000 part numbers and names are loaded into index-by tables. Then, all table elements are inserted into a database table twice. First, they are inserted using a FOR loop, which completes in 38 seconds. Then, they are bulk-inserted using a FORALL statement, which completes in only 3 seconds. SQL> SET SERVEROUTPUT ONSQL> CREATE TABLE parts (pnum NUMBER(4), pname CHAR(15));

Table created.

Page 31: plsql

SQL> GET test.sql 1 DECLARE 2 TYPE NumTab IS TABLE OF NUMBER(4) INDEX BY BINARY_INTEGER; 3 TYPE NameTab IS TABLE OF CHAR(15) INDEX BY BINARY_INTEGER; 4 pnums NumTab; 5 pnames NameTab; 6 t1 CHAR(5); 7 t2 CHAR(5); 8 t3 CHAR(5); 9 PROCEDURE get_time (t OUT NUMBER) IS10 BEGIN SELECT TO_CHAR(SYSDATE,'SSSSS') INTO t FROM dual; END;11 BEGIN12 FOR j IN 1..5000 LOOP -- load index-by tables13 pnums(j) := j;14 pnames(j) := 'Part No. ' || TO_CHAR(j); 15 END LOOP;16 get_time(t1);17 FOR i IN 1..5000 LOOP -- use FOR loop18 INSERT INTO parts VALUES (pnums(i), pnames(i));19 END LOOP;20 get_time(t2);21 FORALL i IN 1..5000 -- use FORALL statement22 INSERT INTO parts VALUES (pnums(i), pnames(i));23 get_time(t3);24 DBMS_OUTPUT.PUT_LINE('Execution Time (secs)');25 DBMS_OUTPUT.PUT_LINE('---------------------');26 DBMS_OUTPUT.PUT_LINE('FOR loop: ' || TO_CHAR(t2 - t1));27 DBMS_OUTPUT.PUT_LINE('FORALL: ' || TO_CHAR(t3 - t2));28* END;SQL> /Execution Time (secs)---------------------FOR loop: 38FORALL: 3

PL/SQL procedure successfully completed.

To bulk-bind input collections, use the FORALL statement. To bulk-bind output collections, use the BULK COLLECT clause.

Using the FORALL Statement

The keyword FORALL 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. Its syntax follows: FORALL index IN lower_bound..upper_bound sql_statement;

The index can be referenced only within the FORALL statement and only as a collection subscript. The SQL statement must be an INSERT, UPDATE, or DELETE statement that references collection elements. And, the bounds must specify a valid range of consecutive index numbers. The SQL engine executes the SQL statement once for each index number in the range. As the following example shows, you can use the bounds to bulk-bind arbitrary slices of a collection: DECLARE TYPE NumList IS VARRAY(15) OF NUMBER; depts NumList := NumList();BEGIN -- fill varray here ... FORALL j IN 6..10 -- bulk-bind middle third of varray

Page 32: plsql

UPDATE emp SET sal = sal * 1.10 WHERE deptno = depts(j);END;

The SQL statement can reference more than one collection. However, the PL/SQL engine bulk-binds only subscripted collections. So, in the following example, it does not bulk-bind the collection sals, which is passed to the function median: FORALL i IN 1..20 INSERT INTO emp2 VALUES (enums(i), names(i), median(sals), ...);

The next example shows that the collection subscript cannot be an expression: FORALL j IN mgrs.FIRST..mgrs.LAST DELETE FROM emp WHERE mgr = mgrs(j+1); -- illegal subscript

All collection elements in the specified range must exist. If an element is missing or was deleted, you get an error, as the following example shows: DECLARE TYPE NumList IS TABLE OF NUMBER; depts NumList := NumList(10, 20, 30, 40);BEGIN depts.DELETE(3); -- delete third element FORALL i IN depts.FIRST..depts.LAST DELETE FROM emp WHERE deptno = depts(i); -- raises an "element does not exist" exceptionEND;

Rollback Behavior

If a FORALL statement fails, database changes are rolled back to an implicit savepoint marked before each execution of the SQL statement. Changes made during previous executions are not rolled back. For example, suppose you create a database table that stores department numbers and job titles, as follows: CREATE TABLE emp2 (deptno NUMBER(2), job VARCHAR2(15));

Next, you insert some rows into the table, as follows: INSERT INTO emp2 VALUES(10, 'Clerk');INSERT INTO emp2 VALUES(10, 'Clerk');INSERT INTO emp2 VALUES(20, 'Bookkeeper'); -- 10-char job titleINSERT INTO emp2 VALUES(30, 'Analyst');INSERT INTO emp2 VALUES(30, 'Analyst');

Then, you try to append the 7-character string ' (temp)' to certain job titles using the following UPDATE statement: DECLARE TYPE NumList IS TABLE OF NUMBER; depts NumList := NumList(10, 20);BEGIN FORALL j IN depts.FIRST..depts.LAST UPDATE emp2 SET job = job || ' (temp)' WHERE deptno = depts(j); -- raises a "value too large" exceptionEXCEPTION WHEN OTHERS THEN COMMIT;END;

The SQL engine executes the UPDATE statement twice, once for each index number in the specified range; that is, once for depts(10) and once for depts(20). The first execution succeeds, but the second execution fails because the string value 'Bookkeeper (temp)' is too large for the job column. In this case, only the second execution is rolled back.

Using the BULK COLLECT Clause

Page 33: plsql

The keywords BULK COLLECT tell the SQL engine to bulk-bind output collections before returning them to the PL/SQL engine. You can use these keywords in the SELECT INTO, FETCH INTO, and RETURNING INTO clauses. Here is the syntax: ... BULK COLLECT INTO collection_name[, collection_name] ...

The SQL engine bulk-binds all collections referenced in the INTO list. The corresponding columns must store scalar (not composite) values. In the following example, the SQL engine loads the entire empno and ename database columns into nested tables before returning the tables to the PL/SQL engine: DECLARE TYPE NumTab IS TABLE OF emp.empno%TYPE; TYPE NameTab IS TABLE OF emp.ename%TYPE; enums NumTab; -- no need to initialize names NameTab;BEGIN SELECT empno, ename BULK COLLECT INTO enums, names FROM emp; ...END;

The SQL engine initializes and extends collections for you. (However, it cannot extend varrays beyond their maximum size.) Then, starting at index 1, it inserts elements consecutively and overwrites any pre-existent elements. The SQL engine bulk-binds entire database columns. So, if a table has 50,000 rows, the engine loads 50,000 column values into the target collection. However, you can use the pseudocolumn ROWNUM to limit the number of rows processed. In the following example, you limit the number of rows to 100: DECLARE TYPE NumTab IS TABLE OF emp.empno%TYPE; sals NumTab;BEGIN SELECT sal BULK COLLECT INTO sals FROM emp WHERE ROWNUM <= 100; ...END;

Bulk Fetching

The following example shows that you can bulk-fetch from a cursor into one or more collections: DECLARE TYPE NameTab IS TABLE OF emp.ename%TYPE; TYPE SalTab IS TABLE OF emp.sal%TYPE; names NameTab; sals SalTab; CURSOR c1 IS SELECT ename, sal FROM emp WHERE sal > 1000;BEGIN OPEN c1; FETCH c1 BULK COLLECT INTO names, sals; ...END;

Restriction: You cannot bulk-fetch from a cursor into a collection of records, as the following example shows: DECLARE TYPE EmpRecTab IS TABLE OF emp%ROWTYPE; emp_recs EmpRecTab; CURSOR c1 IS SELECT ename, sal FROM emp WHERE sal > 1000;BEGIN OPEN c1; FETCH c1 BULK COLLECT INTO emp_recs; -- illegal ...END;

Page 34: plsql

Using FORALL and BULK COLLECT Together

You can combine the BULK COLLECT clause with a FORALL statement, in which case, the SQL engine bulk-binds column values incrementally. In the following example, if collection depts has 3 elements, each of which causes 5 rows to be deleted, then collection enums has 15 elements when the statement completes: FORALL j IN depts.FIRST..depts.LAST DELETE FROM emp WHERE empno = depts(j) RETURNING empno BULK COLLECT INTO enums;

The column values returned by each execution are added to the values returned previously. (With a FOR loop, the previous values are overwritten.) Restriction: You cannot use the SELECT ... BULK COLLECT statement in a FORALL statement.

Using Host Arrays

Client-side programs can use anonymous PL/SQL blocks to bulk-bind input and output host arrays. In fact, that is the most efficient way to pass collections to and from the database server. Host arrays are declared in a host environment such as an OCI or Pro*C program and must be prefixed with a colon to distinguish them from PL/SQL collections. In the example below, an input host array is used in a DELETE statement. At run time, the anonymous PL/SQL block is sent to the database server for execution. DECLARE ...BEGIN -- assume that values were assigned to the host array -- and host variables in the host environment FORALL i IN :lower..:upper DELETE FROM emp WHERE deptno = :depts(i); ...END;

You cannot use collection methods such as FIRST and LAST with host arrays. For example, the following statement is illegal: FORALL i IN :depts.FIRST..:depts.LAST -- illegal DELETE FROM emp WHERE deptno = :depts(i);

Using Cursor Attributes

To process SQL data manipulation statements, the SQL engine opens an implicit cursor named SQL. This cursor's attributes (%FOUND, %ISOPEN, %NOTFOUND, and %ROWCOUNT) return useful information about the most recently executed SQL data manipulation statement. The SQL cursor has only one composite attribute, %BULK_ROWCOUNT, which has the semantics of an index-by table. Its ith element stores the number of rows processed by the ith execution of a SQL statement. If the ith execution affects no rows, %BULK_ROWCOUNT(i) returns zero. An example follows: DECLARE TYPE NumList IS TABLE OF NUMBER; depts NumList := NumList(10, 20, 50);BEGIN FORALL j IN depts.FIRST..depts.LAST UPDATE emp SET sal = sal * 1.10 WHERE deptno = depts(j); IF SQL%BULK_ROWCOUNT(3) = 0 THEN ... END IFEND;

%BULK_ROWCOUNT and the FORALL statement use the same subscripts. For instance, if the FORALL statement uses the range -5..10, so does %BULK_ROWCOUNT.

Page 35: plsql

Note: Only index-by tables can have negative subscripts. You can also use the scalar attributes %FOUND, %NOTFOUND, and %ROWCOUNT with bulk binds. For example, %ROWCOUNT returns the total number of rows processed by all executions of the SQL statement. %FOUND and %NOTFOUND refer only to the last execution of the SQL statement. However, you can use %BULK_ROWCOUNT to infer their values for individual executions. For example, when %BULK_ROWCOUNT(i) is zero, %FOUND and %NOTFOUND are FALSE and TRUE, respectively. Restrictions: %BULK_ROWCOUNT cannot be assigned to other collections. Also, it cannot be passed as a parameter to subprograms.

What Is a Record?

A record is a group of related data items stored in fields, each with its own name and datatype. Suppose you have various data about an employee such as name, salary, and hire date. These items are logically related but dissimilar in type. A record containing a field for each item lets you treat the data as a logical unit. Thus, records make it easier to organize and represent information. The attribute %ROWTYPE lets you declare a record that represents a row in a database table. However, you cannot specify the datatypes of fields in the record or declare fields of your own. The datatype RECORD lifts those restrictions and lets you define your own records.

Defining and Declaring Records

To create records, you define a RECORD type, then declare records of that type. You can define RECORD types in the declarative part of any PL/SQL block, subprogram, or package using the syntax TYPE type_name IS RECORD (field_declaration[,field_declaration]...);

where field_declaration stands for field_name field_type [[NOT NULL] {:= | DEFAULT} expression]

and where type_name is a type specifier used later to declare records, field_type is any PL/SQL datatype except REF CURSOR, and expression yields a value of the same type as field_type. Note: Unlike TABLE and VARRAY types, RECORD types cannot be CREATEd and stored in the database. You can use %TYPE and %ROWTYPE to specify field types. In the following example, you define a RECORD type named DeptRec: DECLARE TYPE DeptRec IS RECORD ( dept_id dept.deptno%TYPE, dept_name VARCHAR2(15), dept_loc VARCHAR2(15));

Notice that field declarations are like variable declarations. Each field has a unique name and specific datatype. So, the value of a record is actually a collection of values, each of some simpler type. As the example below shows, PL/SQL lets you define records that contain objects, collections, and other records (called nested records). However, object types cannot have attributes of type RECORD. DECLARE TYPE TimeRec IS RECORD ( seconds SMALLINT, minutes SMALLINT, hours SMALLINT); TYPE FlightRec IS RECORD ( flight_no INTEGER, plane_id VARCHAR2(10), captain Employee, -- declare object

Page 36: plsql

passengers PassengerList, -- declare varray depart_time TimeRec, -- declare nested record airport_code VARCHAR2(10));

The next example shows that you can specify a RECORD type in the RETURN clause of a function specification. That allows the function to return a user-defined record of the same type. DECLARE TYPE EmpRec IS RECORD ( emp_id INTEGER last_name VARCHAR2(15), dept_num INTEGER(2), job_title VARCHAR2(15), salary REAL(7,2)); ... FUNCTION nth_highest_salary (n INTEGER) RETURN EmpRec IS ...

Declaring Records

Once you define a RECORD type, you can declare records of that type, as the following example shows: DECLARE TYPE StockItem IS RECORD ( item_no INTEGER(3), description VARCHAR2(50), quantity INTEGER, price REAL(7,2)); item_info StckItem; -- declare record

The identifier item_info represents an entire record. Like scalar variables, user-defined records can be declared as the formal parameters of procedures and functions. An example follows: DECLARE TYPE EmpRec IS RECORD ( emp_id emp.empno%TYPE, last_name VARCHAR2(10), job_title VARCHAR2(15), salary NUMBER(7,2)); ... PROCEDURE raise_salary (emp_info EmpRec);

Initializing and Referencing Records

The example below shows that you can initialize a record in its type definition. When you declare a record of type TimeRec, its three fields assume an initial value of zero. DECLARE TYPE TimeRec IS RECORD ( secs SMALLINT := 0, mins SMALLINT := 0, hrs SMALLINT := 0);

The next example shows that you can impose the NOT NULL constraint on any field, and so prevent the assigning of nulls to that field. Fields declared as NOT NULL must be initialized. DECLARE TYPE StockItem IS RECORD ( item_no INTEGER(3) NOT NULL := 999,