Oracle® Database 2 Day Developer's Guide 11g Release 2 (11.2) E10766-06 |
|
|
PDF · Mobi · ePub |
This chapter contains the following topics:
Tip:
If you have problems creating or running PL/SQL code, check the Oracle Database trace files. TheUSER_DUMP_DEST
initialization parameter specifies the current location of the trace files. You can find the value of this parameter by issuing SHOW PARAMETER USER_DUMP_DEST
in the SQL Worksheet of SQL Developer or in SQL*Plus. For more information about trace files, see Oracle Database Performance Tuning Guide.A subprogram is a PL/SQL unit that consists of SQL and PL/SQL statements that solve a specific problem or perform a set of related tasks. A subprogram can have parameters, whose values are supplied by the invoker. A subprogram can be either a procedure or a function. Typically, you use a procedure to perform an action and a function to compute and return a value.
A stored subprogram is a subprogram that is stored in the database. Because they are stored in the database, stored programs can be used as building blocks for many different database applications. (A subprogram that is declared within another subprogram, or within an anonymous block, is called a nested subprogram or local subprogram. It cannot be invoked from outside the subprogram or block in which it is declared. An anonymous block is a block that is not stored in the database.)
There are two kinds of stored subprograms:
Standalone stored subprograms are useful for testing pieces of program logic, but when you are sure that they work as intended, Oracle recommends that you put them into packages.
See Also:
Oracle Database Concepts for general information about stored subprograms
Oracle Database PL/SQL Language Reference for complete information about PL/SQL subprograms
A package is a PL/SQL unit that consists of related subprograms and the explicit cursors and variables that they use.
Oracle recommends that you put your subprograms into packages. Some of the reasons are:
Packages allow you to hide implementation details from client programs.
Hiding implementation details from client programs is a widely accepted best practice. Many Oracle customers follow this practice strictly, allowing client programs to access the database only by invoking PL/SQL subprograms. Some customers allow client programs to use SELECT
statements to retrieve information from database tables, but require them to invoke PL/SQL subprograms for all business functions that change the database.
Package subprograms must be qualified with package names when invoked, which ensures that their names will always work.
For example, suppose that you developed a schema-level procedure named CONTINUE
before Oracle Database 11g Release 1 (11.1). Release 11.1 introduced the CONTINUE
statement. Therefore, if you ported your code to 11.1, it would no longer compile. However, if you had developed your procedure inside a package, your code would refer to the procedure as package_name
.CONTINUE
, so the code would still compile.
Package subprograms can send and receive records and collections.
Standalone stored subprograms can send and receive only scalar parameters—single values with no internal components, such as VARCHAR2
, NUMBER
, and DATE
.
Note:
Oracle Database supplies many PL/SQL packages to extend database functionality and provide PL/SQL access to SQL features. You can use the supplied packages when creating your applications or for ideas in creating your own stored procedures. For information about these packages, see Oracle Database PL/SQL Packages and Types Reference.See Also:
Oracle Database Concepts for general information about packages
Oracle Database PL/SQL Language Reference for more reasons to use packages
Oracle Database PL/SQL Language Reference for complete information about PL/SQL packages
Oracle Database PL/SQL Packages and Types Reference for complete information about the PL/SQL packages that Oracle provides
Every PL/SQL subprogram, package, parameter, variable, constant, exception, and explicit cursor has a name, which is a PL/SQL identifier.
The minimum length of an identifier is one character; the maximum length is 30 characters. The first character must be a letter, but each later character can be either a letter, numeral, dollar sign ($), underscore (_), or number sign (#). For example, these are acceptable identifiers:
X t2 phone# credit_limit LastName oracle$number money$$$tree SN## try_again_
PL/SQL is not case-sensitive for identifiers. For example, PL/SQL considers these to be the same:
lastname LastName LASTNAME
You cannot use a PL/SQL reserved word as an identifier. You can use a PL/SQL keyword as an identifier, but it is not recommended. For lists of PL/SQL reserved words and keywords, see Oracle Database PL/SQL Language Reference.
Tip:
Use meaningful names for identifiers, and follow a naming convention. For example, start each constant name withcon_
, each variable name with var_
, and so on.See Also:
Oracle Database PL/SQL Language Reference for additional general information about PL/SQL identifiers
Oracle Database PL/SQL Language Reference for additional information about PL/SQL naming conventions
Oracle Database PL/SQL Language Reference for information about the scope and visibility of PL/SQL identifiers
Oracle Database PL/SQL Language Reference for information how to collect data on PL/SQL identifiers
Oracle Database PL/SQL Language Reference for information about how PL/SQL resolves identifier names
Every PL/SQL constant, variable, subprogram parameter, and function return value has a data type that determines its storage format, constraints, valid range of values, and operations that can be performed on it.
A PL/SQL data type is either a SQL data type (such as VARCHAR2
, NUMBER
, and DATE
) or a PL/SQL-only data type. The latter include BOOLEAN
, RECORD
, REF
CURSOR
, and many predefined subtypes. PL/SQL also lets you define your own subtypes.
A subtype is a subset of another data type, which is called its base type. A subtype has the same valid operations as its base type, but only a subset of its valid values. Subtypes can increase reliability, provide compatibility with ANSI/ISO types, and improve readability by indicating the intended use of constants and variables.
The predefined numeric subtype PLS_INTEGER
is especially useful, because its operations use hardware arithmetic, rather than the library arithmetic that its base type uses.
You cannot use PL/SQL-only data types at schema level (that is, in tables or standalone stored subprograms). Therefore, to use them in a stored subprogram, you must put the subprogram in a package.
See Also:
Oracle Database PL/SQL Language Reference for general information about PL/SQL data types
Oracle Database PL/SQL Language Reference for information about the PLS_INTEGER
data type
Topics:
Note:
To do the tutorials in this document, you must be connected to Oracle Database as the userHR
from SQL Developer. For instructions, see "Connecting to Oracle Database as User HR from SQL Developer".A subprogram follows PL/SQL block structure; that is, it has:
The declarative part contains declarations of types, constants, variables, exceptions, explicit cursors, and nested subprograms. These items are local to the subprogram and cease to exist when the subprogram completes execution.
The executable part contains statements that assign values, control execution, and manipulate data.
Exception-handling part (optional)
The exception-handling part contains code that handles exceptions (run-time errors).
Comments can appear anywhere in PL/SQL code. The PL/SQL compiler ignores them. Adding comments to your program promotes readability and aids understanding. A single-line comment starts with a double hyphen (--
) and extends to the end of the line. A multiline comment starts with a slash and asterisk (/*
) and ends with an asterisk and a slash (*/
).
The structure of a procedure is:
PROCEDURE name [ ( parameter_list ) ] { IS | AS } [ declarative_part ] BEGIN -- executable part begins statement; [ statement; ]... [ EXCEPTION -- executable part ends, exception-handling part begins] exception_handler; [ exception_handler; ]... ] END; /* exception-handling part ends if it exists; otherwise, executable part ends */
The structure of a function is like that of a procedure, except that it includes a RETURN
clause and at least one RETURN
statement (and some optional clauses that are beyond the scope of this document):
FUNCTION name [ ( parameter_list ) ] RETURN data_type [ clauses ] { IS | AS } [ declarative_part ] BEGIN -- executable part begins -- at least one statement must be a RETURN statement statement; [ statement; ]... [ EXCEPTION -- executable part ends, exception-handling part begins] exception_handler; [ exception_handler; ]... ] END; /* exception-handling part ends if it exists; otherwise, executable part ends */
The code that begins with PROCEDURE
or FUNCTION
and ends before IS
or AS
is the subprogram signature. The declarative, executable, and exception-handling parts comprise the subprogram body. The syntax of exception-handler
is in "About Exceptions and Exception Handlers".
See Also:
Oracle Database PL/SQL Language Reference for more information about subprogram partsTo create a standalone stored procedure, use either the SQL Developer tool Create PL/SQL Procedure or the DDL statement CREATE
PROCEDURE
.
This tutorial shows how to use the Create PL/SQL Procedure tool to create a standalone stored procedure named ADD_EVALUATION
that adds a row to the EVALUATIONS
table (created in "Creating Tables with the CREATE TABLE Statement").
To create a standalone stored procedure using Create PL/SQL Procedure tool:
On the Connections tab, expand hr_conn.
Under the hr_conn
icon, a list of schema object types appears.
Right-click Procedures.
A list of choices appears.
Click New Procedure.
The Create PL/SQL Procedure window opens.
For Schema, accept the default value, HR
.
For Name, change PROCEDURE1
to ADD_EVALUATION
.
Click the icon Add Column.
A row appears under the column headings. Its fields have these default values: Name, param1
; Type, VARCHAR2
; Mode, IN
; Default Value, empty.
For Name, change param1
to evaluation_id
.
For Type, select NUMBER
from the drop-down list.
For Mode, accept the default value, IN
.
Leave Default Value empty.
Add a second parameter by repeating steps 6 through 10 with the Name employee_id
and the Type NUMBER
.
Add a third parameter by repeating steps 6 through 10 with the Name evaluation_date
and the Type DATE
.
Add a fourth parameter by repeating steps 6 through 10 with the Name job_id
and the Type VARCHAR2
.
Add a fifth parameter by repeating steps 6 through 10 with the Name manager_id
and the Type NUMBER
.
Add a sixth parameter by repeating steps 6 through 10 with the Name department_id
and the Type NUMBER
.
Add a seventh parameter by repeating steps 6 through 10 with the Name total_score
and the Type NUMBER
.
Click OK.
The ADD_EVALUATION
pane opens, showing the CREATE
PROCEDURE
statement that created the procedure:
CREATE OR REPLACE PROCEDURE ADD_EVALUATION ( evaluation_id IN NUMBER , employee_id IN NUMBER , evaluation_date IN DATE , job_id IN VARCHAR2 , manager_id IN NUMBER , department_id IN NUMBER , total_score IN NUMBER ) AS BEGIN NULL; END ADD_EVALUATION;
Because the only statement in the execution part of the procedure is NULL
, the procedure does nothing.
Replace NULL
with this statement:
INSERT INTO EVALUATIONS ( evaluation_id, employee_id, evaluation_date, job_id, manager_id, department_id, total_score ) VALUES ( ADD_EVALUATION.evaluation_id, ADD_EVALUATION.employee_id, ADD_EVALUATION.evaluation_date, ADD_EVALUATION.job_id, ADD_EVALUATION.manager_id, ADD_EVALUATION.department_id, ADD_EVALUATION.total_score );
(Qualifying the parameter names with the procedure name ensures that they are not confused with the columns that have the same names.)
The title of the ADD_EVALUATION
pane is in italic font, indicating that the procedure is not yet saved in the database.
From the File menu, select Save.
Oracle Database compiles the procedure and saves it. The title of the ADD_EVALUATION
pane is no longer in italic font.
See Also:
Oracle Database SQL Developer User's Guide for another example of using SQL Developer to create a standalone stored procedure
"About Data Definition Language (DDL) Statements" for general information that applies to the CREATE
PROCEDURE
statement
Oracle Database PL/SQL Language Reference for information about the CREATE
PROCEDURE
statement
To create a standalone stored function, use either the SQL Developer tool Create PL/SQL Function or the DDL statement CREATE
FUNCTION
.
This tutorial shows how to use the Create PL/SQL Function tool to create a standalone stored function named calculate_score
that has three parameters and returns a value of type NUMBER
.
To create a standalone stored function using Create PL/SQL Function tool:
On the Connections tab, expand hr_conn.
Under the hr_conn
icon, a list of schema object types appears.
Right-click Functions.
A list of choices appears.
Click New Function.
The Create PL/SQL Function window opens. It looks like the Create PL/SQL Procedure window (see "Tutorial: Creating a Standalone Stored Procedure") except that its Parameters pane has a row for the value that the function returns. In that row, the value of Name is <Return>
and the default value of Type is VARCHAR2
.
For Schema, accept the default value, HR
.
For Name, change FUNCTION1
to calculate_score
.
In the Parameters pane, in the Type field of the only row, select NUMBER
from the drop-down list.
Click the icon Add Column.
A row appears under the column headings. Its fields have these default values: Name, param1
; Type, VARCHAR2
; Mode, IN
; Default Value, empty.
For Name, change param1
to cat
.
For Type, accept the default, VARCHAR2
.
For Mode, accept the default value, IN
.
Leave Default Value empty.
Add a second parameter by repeating steps 7 through 11 with the Name score
and the Type NUMBER
.
Add a third parameter by repeating steps 7 through 11 with the Name weight
and the Type NUMBER
.
Click OK.
The calculate_score
pane opens, showing the CREATE
FUNCTION
statement that created the function:
CREATE OR REPLACE FUNCTION calculate_score ( cat IN VARCHAR2 , score IN NUMBER , weight IN NUMBER ) RETURN NUMBER AS BEGIN RETURN NULL; END calculate_score;
Because the only statement in the execution part of the function is RETURN
NULL
, the function does nothing.
Replace NULL
with score
*
weight
.
The title of the calculate_score
pane is in italic font, indicating that the function is not yet saved in the database.
Select Save from the File menu.
Oracle Database compiles the function and saves it. The title of the calculate_score
pane is no longer in italic font.
See Also:
"About Data Definition Language (DDL) Statements" for general information that applies to the CREATE
FUNCTION
statement
Oracle Database PL/SQL Language Reference for information about the CREATE
FUNCTION
statement
To change a standalone stored subprogram, use either the SQL Developer tool Edit or the DDL statement ALTER
PROCEDURE
or ALTER
FUNCTION
.
To change a standalone stored subprogram using the Edit tool:
On the Connections tab, expand hr_conn.
Under the hr_conn
icon, a list of schema object types appears.
Expand either Functions or Procedures.
A list of functions or procedures appears.
Click the function or procedure to change.
To the right of the Connections pane, a frame appears. Its top tab has the name of the subprogram to change. Under the top tab are subtabs.
Click the subtab Code.
The Code pane appears, showing the code that created the subprogram to change.
Click the icon Edit.
Another pane appears, also with the name of the subprogram to change.
In the pane, change the code.
The title of the pane changes to italic font, indicating that the change is not yet saved in the database.
Select Save from the File menu.
Oracle Database compiles the subprogram and saves it. The title of the pane is no longer in italic font.
See Also:
"About Data Definition Language (DDL) Statements" for general information that applies to the ALTER
PROCEDURE
and ALTER
FUNCTION
statements
Oracle Database PL/SQL Language Reference for information about the ALTER
PROCEDURE
statement
Oracle Database PL/SQL Language Reference for information about the ALTER
FUNCTION
statement
This tutorial shows how to use the SQL Developer tool Run to test the standalone stored function calculate_score
(created in "Tutorial: Creating a Standalone Stored Function").
To test the calculate_score function using the Run tool:
On the Connections tab, expand hr_conn.
Under the hr_conn
icon, a list of schema object types appears.
Expand Functions.
A list of functions appears.
Right-click calculate_score.
A list of choices appears.
Click Run.
The Run PL/SQL window opens. Its PL/SQL Block frame includes this code:
v_Return := calculate_score ( CAT => CAT, SCORE => SCORE, WEIGHT => WEIGHT );
Change the values of SCORE
and WEIGHT
to 8 and 0.2, respectively:
v_Return := calculate_score ( CAT => CAT, SCORE => 8, WEIGHT => 0.2 );
Click OK.
The Running - Log window opens, showing this result:
Connecting to the database hr_conn.
v_Return = 1.6
Process exited.
Disconnecting from the database hr_conn.
See Also:
Oracle Database SQL Developer User's Guide for information about using SQL Developer to run and debug procedures and functionsTo drop a standalone stored subprogram, use either the SQL Developer navigation frame and Drop tool, or the DDL statement DROP
PROCEDURE
or DROP
FUNCTION
.
To drop a standalone stored subprogram using the Drop tool:
On the Connections tab, expand hr_conn.
Under the hr_conn
icon, a list of schema object types appears.
Expand either Functions or Procedures.
A list of functions or procedures appears.
Right-click the name of the function or procedure to drop.
A list of choices appears.
Click Drop.
The Drop window opens.
Click Apply.
The Confirmation window opens.
Click OK.
See Also:
"About Data Definition Language (DDL) Statements" for general information that applies to the DROP
PROCEDURE
and DROP
FUNCTION
statements
Oracle Database SQL Language Reference for information about the DROP
PROCEDURE
statement
Oracle Database SQL Language Reference for information about the DROP
FUNCTION
statement
Topics:
See Also:
"Tutorial: Declaring Variables and Constants in a Subprogram", which shows how to change a package bodyA package always has a specification, and it usually has a body.
The package specification defines the package, declaring the types, variables, constants, exceptions, explicit cursors, and subprograms that can be referenced from outside the package. A package specification is an application program interface (API): It has all the information that client programs need to invoke its subprograms, but no information about their implementation.
The package body defines the queries for the explicit cursors, and the code for the subprograms, that are declared in the package specification (therefore, a package with neither explicit cursors nor subprograms does not need a body). The package body can also define local subprograms, which are not declared in the specification and can be invoked only by other subprograms in the package. Package body contents are hidden from client programs. You can change the package body without invalidating the applications that call the package.
See Also:
Oracle Database PL/SQL Language Reference for more information about the package specification
Oracle Database PL/SQL Language Reference for more information about the package body
To create a package specification, use either the SQL Developer tool Create PL/SQL Package or the DDL statement CREATE
PACKAGE
.
This tutorial shows how to use the Create PL/SQL Package tool to create a specification for a package named EMP_EVAL
.
This package specification is the API for the sample application that the tutorials and examples in this document show how to develop and deploy.
To create a package specification using Create PL/SQL Package tool:
On the Connections tab, expand hr_conn.
Under the hr_conn
icon, a list of schema object types appears.
Right-click Packages.
A list of choices appears.
Click New Package.
The Create PL/SQL Package window opens. The field Schema has the value HR
, the field Name has the default value PACKAGE1
, and the check box Add New Source In Lowercase is deselected.
For Schema, accept the default value, HR
.
For Name, change PACKAGE1
to EMP_EVAL
.
Click OK.
The EMP_EVAL
pane opens, showing the CREATE
PACKAGE
statement that created the package:
CREATE OR REPLACE PACKAGE emp_eval AS /* TODO enter package declarations (types, exceptions, methods etc) here */ END emp_eval;
The title of the pane is in italic font, which indicates that the package is not saved to the database.
(Optional) In the CREATE
PACKAGE
statement, replace the comment with declarations.
If you do not do this step now, you can do it later, as shown in "Tutorial: Changing a Package Specification".
From the File menu, select Save.
Oracle Database compiles the package and saves it. The title of the EMP_EVAL
pane is no longer in italic font.
See Also:
Oracle Database PL/SQL Language Reference for information about theCREATE
PACKAGE
statement (for the package specification)To change a package specification, use either the SQL Developer tool Edit or the DDL statement CREATE
PACKAGE
with the OR
REPLACE
clause.
This tutorial shows how to use the Edit tool to change the specification for the EMP_EVAL
package (created in "Tutorial: Creating a Package Specification"). Specifically, the tutorial shows how to add declarations for a procedure, eval_department
, and a function, calculate_score
.
To change EMP_EVAL package specification using the Edit tool:
On the Connections tab, expand hr_conn.
Under the hr_conn
icon, a list of schema object types appears.
Expand Packages.
A list of packages appears.
Right-click EMP_EVAL.
A list of choices appears.
Click Edit.
The EMP_EVAL
pane opens, showing the CREATE
PACKAGE
statement that created the package:
CREATE OR REPLACE PACKAGE emp_eval AS /* TODO enter package declarations (types, exceptions, methods etc) here */ END emp_eval;
The title of the pane is not in italic font, which indicates that the package is saved in the database.
In the EMP_EVAL
pane, replace the comment with this code:
PROCEDURE eval_department ( dept_id IN NUMBER ); FUNCTION calculate_score ( evaluation_id IN NUMBER , performance_id IN NUMBER) RETURN NUMBER;
A new EMP_EVAL
pane opens, showing the changed CREATE
PACKAGE
statement. The title of the pane is in italic font, which indicates that the changes have not been saved to the database.
Click the icon Compile.
The changed package specification compiles and is saved to the database. The title of the EMP_EVAL
pane is no longer in italic font.
See Also:
Oracle Database PL/SQL Language Reference for information about theCREATE
PACKAGE
statement with the OR
REPLACE
clauseTo create a package body, use either the SQL Developer tool Create Body or the DDL statement CREATE
PACKAGE
BODY
.
This tutorial shows how to use the Create Body tool to create a body for the EMP_EVAL
package (created in "Tutorial: Creating a Package Specification" and changed in "Tutorial: Changing a Package Specification").
This package body will contain the implementation details of the sample application that the tutorials and examples in this document show how to develop and deploy.
To create a body for the package EMP_EVAL using the Create Body tool:
On the Connections tab, expand hr_conn.
Under the hr_conn
icon, a list of schema object types appears.
Expand Packages.
A list of packages appears.
Right-click EMP_EVAL.
A list of choices appears.
Click Create Body.
The EMP_EVAL
Body pane appears, showing the automatically generated code for the package body:
CREATE OR REPLACE PACKAGE BODY emp_eval AS PROCEDURE eval_department(dept_id IN NUMBER) AS BEGIN /* TODO implementation required */ NULL; END eval_department; FUNCTION calculate_score ( evaluation_id IN NUMBER , performance_id IN NUMBER) RETURN NUMBER AS BEGIN /* TODO implementation required */ RETURN NULL; END calculate_score; END emp_eval;
The title of the pane is in italic font, which indicates that the code is not saved in the database.
(Optional) In the CREATE
PACKAGE
BODY
statement:
Replace the comments with executable statements.
(Optional) In the executable part of the procedure, either delete NULL
or replace it with an executable statement.
(Optional) In the executable part of the function, either replace NULL
with another expression.
If you do not do this step now, you can do it later, as shown in "Tutorial: Declaring Variables and Constants in a Subprogram".
Click the icon Compile.
The changed package body compiles and is saved to the database. The title of the EMP_EVAL
Body pane is no longer in italic font.
See Also:
Oracle Database PL/SQL Language Reference for information about theCREATE
PACKAGE
BODY
statement (for the package body)To drop a package (specification and body), use either the SQL Developer navigation frame and Drop tool, or the DDL statement DROP
PACKAGE
.
To drop a package using the Drop tool:
On the Connections tab, expand the hr_conn
information by clicking the plus sign (+) to the left of the hr_conn
icon.
Under the hr_conn
icon, a list of schema object types appears.
Expand Packages.
A list of packages appears.
Right-click the name of the package to drop.
A list of choices appears.
Click Drop Package.
The Drop window opens.
Click Apply.
The Confirmation window opens.
Click OK.
See Also:
Oracle Database PL/SQL Language Reference for information about theDROP
PACKAGE
statementOne significant advantage that PL/SQL has over SQL is that PL/SQL lets you declare and use variables and constants.
A variable or constant declared in a package specification can be used by any program that has access to the package. A variable or constant declared in a package body or subprogram is local to that package or subprogram.
A variable holds a value of a particular data type. Your program can change the value at run time. A constant holds a value that cannot be changed.
A variable or constant can have any PL/SQL data type. When declaring a variable, you can assign it an initial value; if you do not, its initial value is NULL
. When declaring a constant, you must assign it an initial value. To assign an initial value to a variable or constant, use the assignment operator (:=
).
Tip:
Declare all values that do not change as constants. This practice optimizes your compiled code and makes your source code easier to maintain.Topics:
See Also:
Oracle Database PL/SQL Language Reference for general information about variables and constantsThis tutorial shows how to use the SQL Developer tool Edit to declare variables and constants in the EMP_EVAL
.calculate_score
function (specified in "Tutorial: Creating a Package Specification"). (This tutorial is also an example of changing a package body.)
To declare variables and constants in calculate_score function:
On the Connections tab, expand the hr_conn
information by clicking the plus sign (+) to the left of the hr_conn
icon.
Under the hr_conn
icon, a list of schema object types appears.
Expand Packages.
A list of packages appears.
Expand EMP_EVAL.
A list appears.
Right-click EMP_EVAL Body.
A list of choices appears.
Click Edit.
The EMP_EVAL
Body pane appears, showing the code for the package body:
CREATE OR REPLACE PACKAGE BODY emp_eval AS PROCEDURE eval_department ( dept_id IN NUMBER ) AS BEGIN /* TODO implementation required */ NULL; END eval_department; FUNCTION calculate_score ( evaluation_id IN NUMBER , performance_id IN NUMBER) RETURN NUMBER AS BEGIN /* TODO implementation required */ RETURN NULL; END calculate_score; END emp_eval;
Between RETURN
NUMBER
AS
and BEGIN
, add these variable and constant declarations:
n_score NUMBER(1,0); -- variable n_weight NUMBER; -- variable max_score CONSTANT NUMBER(1,0) := 9; -- constant, initial value 9 max_weight CONSTANT NUMBER(8,8) := 1; -- constant, initial value 1
From the File menu, select Save.
Oracle Database saves the changed package body.
See Also:
Oracle Database PL/SQL Language Reference for general information about declaring variables and constants
"Assigning Values to Variables with the Assignment Operator"
After "Tutorial: Declaring Variables and Constants in a Subprogram", the code for the calculate_score
function, in the body of the package EMP_EVAL
, is:
FUNCTION calculate_score ( evaluation_id IN NUMBER , performance_id IN NUMBER ) RETURN NUMBER AS n_score NUMBER(1,0); -- variable n_weight NUMBER; -- variable max_score CONSTANT NUMBER(1,0) := 9; -- constant, initial value 9 max_weight CONSTANT NUMBER(8,8) := 1; -- constant, initial value 1 BEGIN /* TODO implementation required */ RETURN NULL; END calculate_score;
The variables, constants, and parameters of the function represent values from the tables SCORES
(created in "Creating Tables with the CREATE TABLE Statement") and PERFORMANCE_PARTS
(created in "Tutorial: Creating a Table with the Create Table Tool"):
Variable n_score
will hold a value from the SCORE
column of the SCORES
table, and constant max_score
will be compared to such values.
Variable n_weight
will hold a value from the WEIGHT
column of the PERFORMANCE_PARTS
table, and constant max_weight
will be compared to such values.
Parameter evaluation_id
will hold a value from the EVALUATION_ID
column of the SCORES
table.
Parameter performance_id
will hold a value from the PERFORMANCE_ID
column of the SCORES
table.
Therefore, each variable, constant, and parameter has the same data type as its corresponding column.
If the data types of the columns change, you want the data types of the variables, constants, and parameters to change to the same data types; otherwise, the calculate_score
function will be invalidated.
To ensure that the data types of the variables, constants, and parameters will always match those of the columns, declare them with the %TYPE
attribute. The %TYPE
attribute supplies the data type of a table column or another variable, ensuring the correct data type assignment.
See Also:
Oracle Database PL/SQL Language Reference for more information about the %TYPE
attribute
Oracle Database PL/SQL Language Reference for the syntax of the %TYPE
attribute
This tutorial shows how to use the SQL Developer tool Edit to change the declarations of the variables, constants, and formal parameters of the EMP_EVAL
.calculate_score
function (shown in "Tutorial: Declaring Variables and Constants in a Subprogram") to declarations that use the %TYPE
attribute.
To change the declarations in calculate_score to use %TYPE:
On the Connections tab, expand the hr_conn
information by clicking the plus sign (+) to the left of the hr_conn
icon.
Under the hr_conn
icon, a list of schema object types appears.
Expand Packages.
A list of packages appears.
Expand EMP_EVAL.
A list appears.
Right-click EMP_EVAL Body.
A list of choices appears.
Click Edit.
The EMP_EVAL
Body pane appears, showing the code for the package body:
CREATE OR REPLACE PACKAGE BODY emp_eval AS PROCEDURE eval_department ( dept_id IN NUMBER ) AS BEGIN /* TODO implementation required */ NULL; END eval_department; FUNCTION calculate_score ( evaluation_id IN NUMBER , performance_id IN NUMBER ) RETURN NUMBER AS n_score NUMBER(1,0); -- variable n_weight NUMBER; -- variable max_score CONSTANT NUMBER(1,0) := 9; -- constant, initial value 9 max_weight CONSTANT NUMBER(8,8) := 1; -- constant, initial value 1 BEGIN /* TODO implementation required */ RETURN NULL; END calculate_score; END emp_eval;
In the code for the function, make the changes shown in bold font:
FUNCTION calculate_score ( evaluation_id IN SCORES.EVALUATION_ID%TYPE , performance_id IN SCORES.PERFORMANCE_ID%TYPE) RETURN NUMBER AS n_score SCORES.SCORE%TYPE; n_weight PERFORMANCE_PARTS.WEIGHT%TYPE; max_score CONSTANT SCORES.SCORE%TYPE := 9; max_weight CONSTANT PERFORMANCE_PARTS.WEIGHT%TYPE := 1;
Right-click EMP_EVAL.
A list of choices appears.
Click Edit.
The EMP_EVAL
pane opens, showing the CREATE
PACKAGE
statement that created the package:
CREATE OR REPLACE PACKAGE emp_eval AS PROCEDURE eval_department(dept_id IN NUMBER); FUNCTION calculate_score(evaluation_id IN NUMBER , performance_id IN NUMBER) RETURN NUMBER; END emp_eval;
In the code for the function, make the changes shown in bold font:
FUNCTION calculate_score(evaluation_id IN scores.evaluation_id%TYPE , performance_id IN scores.performance_id%TYPE)
Right-click EMP_EVAL.
A list of choices appears.
Click Compile.
You can assign a value to a variable in these ways:
Use the assignment operator to assign it the value of an expression.
Use the SELECT
INTO
or FETCH
statement to assign it a value from a table.
Pass it to a subprogram as an OUT
or IN
OUT
parameter, and then assign the value inside the subprogram
Bind the variable to a value.
Topics:
See Also:
Oracle Database PL/SQL Language Reference for more information about assigning values to variables
Oracle Database 2 Day + .NET Developer's Guide for information about binding variables
Oracle Database 2 Day + PHP Developer's Guide for information about binding variables
Oracle Database 2 Day + Java Developer's Guide for information about binding variables
Oracle Database 2 Day + Application Express Developer's Guide for information about binding variables
With the assignment operator (:=
), you can assign the value of an expression to a variable in either the declarative or executable part of a subprogram.
In the declarative part of a subprogram, you can assign an initial value to a variable when you declare it. The syntax is:
variable_name data_type := expression;
In the executable part of a subprogram, you can assign a value to a variable with an assignment statement. The syntax is:
variable_name := expression;
Example 7-1 shows, in bold font, the changes to make to the EMP_EVAL
.calculate_score
function to add a variable, running_total
, and use it as the return value of the function. The assignment operator appears in both the declarative and executable parts of the function. (The data type of running_total
must be NUMBER
, rather than SCORES
.SCORE%TYPE
or PERFORMANCE_PARTS
.WEIGHT%TYPE
, because it holds the product of two NUMBER
values with different precisions and scales.)
Example 7-1 Assigning Values to a Variable with Assignment Operator
FUNCTION calculate_score(evaluation_id IN SCORES.EVALUATION_ID%TYPE , performance_id IN SCORES.PERFORMANCE_ID%TYPE) RETURN NUMBER AS n_score SCORES.SCORE%TYPE; n_weight PERFORMANCE_PARTS.WEIGHT%TYPE; running_total NUMBER := 0; max_score CONSTANT SCORES.SCORE%TYPE := 9; max_weight CONSTANT PERFORMANCE_PARTS.WEIGHT%TYPE:= 1; BEGIN running_total := max_score * max_weight; RETURN running_total; END calculate_score;
See Also:
Oracle Database PL/SQL Language Reference for variable declaration syntax
Oracle Database PL/SQL Language Reference for assignment statement syntax
To use table values in subprograms or packages, you must assign them to variables with SELECT
INTO
statements.
Example 7-2 shows, in bold font, the changes to make to the EMP_EVAL
.calculate_score
function to have it calculate running_total
from table values.
Example 7-2 Assigning Table Values to Variables with SELECT INTO
FUNCTION calculate_score ( evaluation_id IN scores.evaluation_id%TYPE , performance_id IN scores.performance_id%TYPE ) RETURN NUMBER AS n_score scores.score%TYPE; n_weight performance_parts.weight%TYPE; running_total NUMBER := 0; max_score CONSTANT scores.score%TYPE := 9; max_weight CONSTANT performance_parts.weight%TYPE:= 1; BEGIN SELECT s.score INTO n_score FROM SCORES s WHERE evaluation_id = s.evaluation_id AND performance_id = s.performance_id; SELECT p.weight INTO n_weight FROM PERFORMANCE_PARTS p WHERE performance_id = p.performance_id; running_total := n_score * n_weight; RETURN running_total; END calculate_score;
The add_eval
procedure in Example 7-3 inserts a row into the EVALUATIONS
table, using values from the corresponding row in the EMPLOYEES
table. Add the add_eval
procedure to the body of the EMP_EVAL
package, but not to the specification. Because it is not in the specification, add_eval
is local to the package—it can be invoked only by other subprograms in the package, not from outside the package.
Example 7-3 Inserting a Table Row with Values from Another Table
PROCEDURE add_eval ( employee_id IN EMPLOYEES.EMPLOYEE_ID%TYPE , today IN DATE ) AS job_id EMPLOYEES.JOB_ID%TYPE; manager_id EMPLOYEES.MANAGER_ID%TYPE; department_id EMPLOYEES.DEPARTMENT_ID%TYPE; BEGIN SELECT e.job_id INTO job_id FROM EMPLOYEES e WHERE employee_id = e.employee_id; SELECT e.manager_id INTO manager_id FROM EMPLOYEES e WHERE employee_id = e.employee_id; SELECT e.department_id INTO department_id FROM EMPLOYEES e WHERE employee_id = e.employee_id; INSERT INTO EVALUATIONS ( evaluation_id, employee_id, evaluation_date, job_id, manager_id, department_id, total_score ) VALUES ( evaluations_seq.NEXTVAL, -- evaluation_id add_eval.employee_id, -- employee_id add_eval.today, -- evaluation_date add_eval.job_id, -- job_id add_eval.manager_id, -- manager_id add_eval.department_id, -- department_id 0 -- total_score ); END add_eval;
See Also:
Oracle Database PL/SQL Language Reference for more information about theSELECT
INTO
statementUnlike SQL, which runs statements in the order in which you enter them, PL/SQL has control statements that let you control the flow of your program.
Topics:
PL/SQL has three categories of control statements:
Conditional selection statements, which let you execute different statements for different data values.
The conditional selection statements are IF
and and CASE
.
Loop statements, which let you repeat the same statements with a series of different data values.
The loop statements are FOR
LOOP
, WHILE
LOOP
and basic LOOP
.
The EXIT
statement transfers control to the end of a loop. The CONTINUE
statement exits the current iteration of a loop and transfers control to the next iteration. Both EXIT
and CONTINUE
have an optional WHEN
clause, in which you can specify a condition.
Sequential control statements, which let you go to a specified, labeled statement, or to do nothing.
The sequential control statements are GOTO
and and NULL
.
See Also:
Oracle Database PL/SQL Language Reference for an overview of PL/SQL control statements
The IF
statement either executes or skips a sequence of statements, depending on the value of a Boolean expression.
The IF
statement has this syntax:
IF boolean_expression THEN statement [, statement ] [ ELSEIF boolean_expression THEN statement [, statement ] ]... [ ELSE statement [, statement ] ] END IF;
Suppose that your company evaluates employees twice a year in the first 10 years of employment, but only once a year afterward. You want a function that returns the evaluation frequency for an employee. You can use an IF
statement to determine the return value of the function, as in Example 7-4.
Add the eval_frequency
function to the body of the EMP_EVAL
package, but not to the specification. Because it is not in the specification, eval_frequency
is local to the package—it can be invoked only by other subprograms in the package, not from outside the package.
Example 7-4 IF Statement that Determines Return Value of Function
FUNCTION eval_frequency (emp_id IN EMPLOYEES.EMPLOYEE_ID%TYPE) RETURN PLS_INTEGER AS h_date EMPLOYEES.HIRE_DATE%TYPE; today EMPLOYEES.HIRE_DATE%TYPE; eval_freq PLS_INTEGER; BEGIN SELECT SYSDATE INTO today FROM DUAL; SELECT HIRE_DATE INTO h_date FROM EMPLOYEES WHERE EMPLOYEE_ID = emp_id; IF ((h_date + (INTERVAL '120' MONTH)) < today) THEN eval_freq := 1; ELSE eval_freq := 2; END IF; RETURN eval_freq; END eval_frequency;
See Also:
Oracle Database PL/SQL Language Reference for the syntax of the IF
statement
Oracle Database PL/SQL Language Reference for more information about using the IF
statement
The CASE
statement chooses from a sequence of conditions, and executes the corresponding statement.
The simple CASE
statement evaluates a single expression and compares it to several potential values. It has this syntax:
CASE expression WHEN value THEN statement [ WHEN value THEN statement ]... [ ELSE statement [, statement ]... ] END CASE;
The searched CASE
statement evaluates multiple Boolean expressions and chooses the first one whose value is TRUE
. For information about the searched CASE
statement, see Oracle Database PL/SQL Language Reference.
Tip:
When you can use either aCASE
statement or nested IF
statements, use a CASE
statement—it is both more readable and more efficient.Suppose that, if an employee is evaluated only once a year, you want the eval_frequency
function to suggest a salary increase, which depends on the JOB_ID
.
Add the CASE
statement shown in Example 7-5 to the eval_frequency
function. (For information about the procedure that prints the strings, DBMS_OUTPUT.PUT_LINE
, see Oracle Database PL/SQL Packages and Types Reference.)
Example 7-5 CASE Statement that Determines Which String to Print
FUNCTION eval_frequency (emp_id IN EMPLOYEES.EMPLOYEE_ID%TYPE) RETURN PLS_INTEGER AS h_date EMPLOYEES.HIRE_DATE%TYPE; today EMPLOYEES.HIRE_DATE%TYPE; eval_freq PLS_INTEGER; j_id EMPLOYEES.JOB_ID%TYPE; BEGIN SELECT SYSDATE INTO today FROM DUAL; SELECT HIRE_DATE INTO h_date FROM EMPLOYEES WHERE EMPLOYEE_ID = emp_id; IF ((h_date + (INTERVAL '120' MONTH)) < today) THEN eval_freq := 1; SELECT JOB_ID INTO j_id FROM EMPLOYEES WHERE EMPLOYEE_ID = emp_id; CASE j_id WHEN 'PU_CLERK' THEN DBMS_OUTPUT.PUT_LINE( 'Consider 8% salary increase for employee # ' || emp_id); WHEN 'SH_CLERK' THEN DBMS_OUTPUT.PUT_LINE( 'Consider 7% salary increase for employee # ' || emp_id); WHEN 'ST_CLERK' THEN DBMS_OUTPUT.PUT_LINE( 'Consider 6% salary increase for employee # ' || emp_id); WHEN 'HR_REP' THEN DBMS_OUTPUT.PUT_LINE( 'Consider 5% salary increase for employee # ' || emp_id); WHEN 'PR_REP' THEN DBMS_OUTPUT.PUT_LINE( 'Consider 5% salary increase for employee # ' || emp_id); WHEN 'MK_REP' THEN DBMS_OUTPUT.PUT_LINE( 'Consider 4% salary increase for employee # ' || emp_id); ELSE DBMS_OUTPUT.PUT_LINE( 'Nothing to do for employee #' || emp_id); END CASE; ELSE eval_freq := 2; END IF; RETURN eval_freq; END eval_frequency;
See Also:
Oracle Database PL/SQL Language Reference for the syntax of the CASE
statement
Oracle Database PL/SQL Language Reference for more information about using the CASE
statement
The FOR
LOOP
statement repeats a sequence of statements once for each integer in the range lower_bound
through upper_bound
. Its syntax is:
FOR counter IN lower_bound..upper_bound LOOP statement [, statement ]... END LOOP;
The statements between LOOP
and END
LOOP
can use counter
, but cannot change its value.
Suppose that, instead of only suggesting a salary increase, you want the eval_frequency
function to report what the salary would be if it increased by the suggested amount every year for five years.
Change the eval_frequency
function as shown in bold font in Example 7-6. (For information about the procedures that prints the strings, DBMS_OUTPUT.PUT_LINE
, see Oracle Database PL/SQL Packages and Types Reference.)
Example 7-6 FOR LOOP Statement that Computes Salary After Five Years
FUNCTION eval_frequency (emp_id IN EMPLOYEES.EMPLOYEE_ID%TYPE) RETURN PLS_INTEGER AS h_date EMPLOYEES.HIRE_DATE%TYPE; today EMPLOYEES.HIRE_DATE%TYPE; eval_freq PLS_INTEGER; j_id EMPLOYEES.JOB_ID%TYPE; sal EMPLOYEES.SALARY%TYPE; sal_raise NUMBER(3,3) := 0; BEGIN SELECT SYSDATE INTO today FROM DUAL; SELECT HIRE_DATE INTO h_date FROM EMPLOYEES WHERE EMPLOYEE_ID = emp_id; IF ((h_date + (INTERVAL '120' MONTH)) < today) THEN eval_freq := 1; SELECT JOB_ID INTO j_id FROM EMPLOYEES WHERE EMPLOYEE_ID = emp_id; SELECT SALARY INTO sal FROM EMPLOYEES WHERE EMPLOYEE_ID = emp_id; CASE j_id WHEN 'PU_CLERK' THEN sal_raise := 0.08; WHEN 'SH_CLERK' THEN sal_raise := 0.07; WHEN 'ST_CLERK' THEN sal_raise := 0.06; WHEN 'HR_REP' THEN sal_raise := 0.05; WHEN 'PR_REP' THEN sal_raise := 0.05; WHEN 'MK_REP' THEN sal_raise := 0.04; ELSE NULL; END CASE; IF (sal_raise != 0) THEN BEGIN DBMS_OUTPUT.PUT_LINE('If salary ' || sal || ' increases by ' || ROUND((sal_raise * 100),0) || '% each year for 5 years, it will be:'); FOR i IN 1..5 LOOP sal := sal * (1 + sal_raise); DBMS_OUTPUT.PUT_LINE(ROUND(sal, 2) || ' after ' || i || ' year(s)'); END LOOP; END; END IF; ELSE eval_freq := 2; END IF; RETURN eval_freq; END eval_frequency;
See Also:
Oracle Database PL/SQL Language Reference for the syntax of the FOR
LOOP
statement
Oracle Database PL/SQL Language Reference for more information about using the FOR
LOOP
statement
The WHILE
LOOP
statement repeats a sequence of statements while a condition is TRUE
. Its syntax is:
WHILE condition LOOP statement [, statement ]... END LOOP;
Note:
If the statements betweenLOOP
and END
LOOP
never cause condition
to become FALSE
, the WHILE
LOOP
statement runs indefinitely.Suppose that the eval_frequency
function uses the WHILE
LOOP
statement instead of the FOR
LOOP
statement, and ends after the proposed salary exceeds the maximum salary for the JOB_ID
.
Change the eval_frequency
function as shown in bold font in Example 7-7. (For information about the procedures that prints the strings, DBMS_OUTPUT.PUT_LINE
, see Oracle Database PL/SQL Packages and Types Reference.)
Example 7-7 WHILE LOOP Statement that Computes Salary to Maximum
FUNCTION eval_frequency (emp_id IN EMPLOYEES.EMPLOYEE_ID%TYPE) RETURN PLS_INTEGER AS h_date EMPLOYEES.HIRE_DATE%TYPE; today EMPLOYEES.HIRE_DATE%TYPE; eval_freq PLS_INTEGER; j_id EMPLOYEES.JOB_ID%TYPE; sal EMPLOYEES.SALARY%TYPE; sal_raise NUMBER(3,3) := 0; sal_max JOBS.MAX_SALARY%TYPE; BEGIN SELECT SYSDATE INTO today FROM DUAL; SELECT HIRE_DATE INTO h_date FROM EMPLOYEES WHERE EMPLOYEE_ID = emp_id; IF ((h_date + (INTERVAL '120' MONTH)) < today) THEN eval_freq := 1; SELECT JOB_ID INTO j_id FROM EMPLOYEES WHERE EMPLOYEE_ID = emp_id; SELECT SALARY INTO sal FROM EMPLOYEES WHERE EMPLOYEE_ID = emp_id; SELECT MAX_SALARY INTO sal_max FROM JOBS WHERE JOB_ID = j_id; CASE j_id WHEN 'PU_CLERK' THEN sal_raise := 0.08; WHEN 'SH_CLERK' THEN sal_raise := 0.07; WHEN 'ST_CLERK' THEN sal_raise := 0.06; WHEN 'HR_REP' THEN sal_raise := 0.05; WHEN 'PR_REP' THEN sal_raise := 0.05; WHEN 'MK_REP' THEN sal_raise := 0.04; ELSE NULL; END CASE; IF (sal_raise != 0) THEN BEGIN DBMS_OUTPUT.PUT_LINE('If salary ' || sal || ' increases by ' || ROUND((sal_raise * 100),0) || '% each year, it will be:'); WHILE sal <= sal_max LOOP sal := sal * (1 + sal_raise); DBMS_OUTPUT.PUT_LINE(ROUND(sal, 2)); END LOOP; DBMS_OUTPUT.PUT_LINE('Maximum salary for this job is ' || sal_max); END; END IF; ELSE eval_freq := 2; END IF; RETURN eval_freq; END eval_frequency;
See Also:
Oracle Database PL/SQL Language Reference for the syntax of the WHILE
LOOP
statement
Oracle Database PL/SQL Language Reference for more information about using the WHILE
LOOP
statement
The basic LOOP
statement repeats a sequence of statements. Its syntax is:
LOOP statement [, statement ]... END LOOP;
At least one statement
must be an EXIT
statement; otherwise, the LOOP
statement runs indefinitely.
The EXIT
WHEN
statement (the EXIT
statement with its optional WHEN
clause) exits a loop when a condition is TRUE
and transfers control to the end of the loop.
In the eval_frequency
function, in the last iteration of the WHILE
LOOP
statement, the last computed value usually exceeds the maximum salary.
Change the WHILE
LOOP
statement to a basic LOOP
statement that includes an EXIT
WHEN
statement, as shown in Example 7-8.
WHILE
LOOP
Example 7-8 Using the EXIT WHEN Statement
FUNCTION eval_frequency (emp_id IN EMPLOYEES.EMPLOYEE_ID%TYPE) RETURN PLS_INTEGER AS h_date EMPLOYEES.HIRE_DATE%TYPE; today EMPLOYEES.HIRE_DATE%TYPE; eval_freq PLS_INTEGER; j_id EMPLOYEES.JOB_ID%TYPE; sal EMPLOYEES.SALARY%TYPE; sal_raise NUMBER(3,3) := 0; sal_max JOBS.MAX_SALARY%TYPE; BEGIN SELECT SYSDATE INTO today FROM DUAL; SELECT HIRE_DATE INTO h_date FROM EMPLOYEES WHERE EMPLOYEE_ID = emp_id; IF ((h_date + (INTERVAL '120' MONTH)) < today) THEN eval_freq := 1; SELECT JOB_ID INTO j_id FROM EMPLOYEES WHERE EMPLOYEE_ID = emp_id; SELECT SALARY INTO sal FROM EMPLOYEES WHERE EMPLOYEE_ID = emp_id; SELECT MAX_SALARY INTO sal_max FROM JOBS WHERE JOB_ID = j_id; CASE j_id WHEN 'PU_CLERK' THEN sal_raise := 0.08; WHEN 'SH_CLERK' THEN sal_raise := 0.07; WHEN 'ST_CLERK' THEN sal_raise := 0.06; WHEN 'HR_REP' THEN sal_raise := 0.05; WHEN 'PR_REP' THEN sal_raise := 0.05; WHEN 'MK_REP' THEN sal_raise := 0.04; ELSE NULL; END CASE; IF (sal_raise != 0) THEN BEGIN DBMS_OUTPUT.PUT_LINE('If salary ' || sal || ' increases by ' || ROUND((sal_raise * 100),0) || '% each year, it will be:'); LOOP sal := sal * (1 + sal_raise); EXIT WHEN sal > sal_max; DBMS_OUTPUT.PUT_LINE(ROUND(sal,2)); END LOOP; DBMS_OUTPUT.PUT_LINE('Maximum salary for this job is ' || sal_max); END; END IF; ELSE eval_freq := 2; END IF; RETURN eval_freq; END eval_frequency;
See Also:
Oracle Database PL/SQL Language Reference for the syntax of the LOOP
statement
Oracle Database PL/SQL Language Reference for the syntax of the EXIT
statement
Oracle Database PL/SQL Language Reference for more information about using the LOOP
and EXIT
statements
Topics:
Tutorial: Creating and Invoking a Subprogram with a Record Parameter
Using an Explicit Cursor to Retrieve Result Set Rows One at a Time
Tutorial: Using an Explicit Cursor to Retrieve Result Set Rows One at a Time
Using a Cursor Variable to Retrieve Result Set Rows One at a Time
Tutorial: Using a Cursor Variable to Retrieve Result Set Rows One at a Time
See Also:
Oracle Database PL/SQL Language Reference for more information about recordsA record is a PL/SQL composite variable that can store data values of different types, similar to a struct
type in C, C++, or Java. The internal components of a record are called fields. To access a record field, you use dot notation: record_name
.field_name
.
You can treat record fields like scalar variables. You can also pass entire records as subprogram parameters (if neither the sending nor receiving subprogram is a standalone stored subprogram).
Records are useful for holding data from table rows, or from certain columns of table rows. Each record field corresponds to a table column.
There are three ways to create a record:
Declare a RECORD
type, and then declare a variable of that type.
The syntax is:
TYPE record_name IS RECORD ( field_name data_type [:= initial_value] [, field_name data_type [:= initial_value ] ]... ); variable_name record_name;
Declare a variable of the type table_name
%ROWTYPE
.
The fields of the record have the same names and data types as the columns of the table.
Declare a variable of the type cursor_name
%ROWTYPE
.
The fields of the record have the same names and data types as the columns of the table in the FROM
clause of the cursor SELECT
statement.
See Also:
Oracle Database PL/SQL Language Reference for more information about defining RECORD
types and declaring records of that type
Oracle Database PL/SQL Language Reference for the syntax of a RECORD
type definition
Oracle Database PL/SQL Language Reference for more information about the %ROWTYPE
attribute
Oracle Database PL/SQL Language Reference for the syntax of the %ROWTYPE
attribute
This tutorial shows how to use the SQL Developer tool Edit to declare a RECORD
type, sal_info
, whose fields can hold salary information for an employee—job ID, minimum and maximum salary for that job ID, current salary, and suggested raise.
To declare RECORD type sal_info:
On the Connections tab, expand the hr_conn
information by clicking the plus sign (+) to the left of the hr_conn
icon.
Under the hr_conn
icon, a list of schema object types appears.
Expand Packages.
A list of packages appears.
Right-click EMP_EVAL.
A list of choices appears.
Click Edit.
The EMP_EVAL
pane opens, showing the CREATE
PACKAGE
statement that created the package:
CREATE OR REPLACE PACKAGE emp_eval AS PROCEDURE eval_department(dept_id IN NUMBER); FUNCTION calculate_score(evaluation_id IN NUMBER , performance_id IN NUMBER) RETURN NUMBER; END emp_eval;
In the EMP_EVAL
pane, immediately before END emp_eval
, add this code:
TYPE sal_info IS RECORD ( j_id jobs.job_id%type , sal_min jobs.min_salary%type , sal_max jobs.max_salary%type , sal employees.salary%type , sal_raise NUMBER(3,3) );
A new EMP_EVAL
pane opens, showing the changed CREATE
PACKAGE
statement. The title of the pane is in italic font, which indicates that the changes have not been saved to the database.
Click the icon Compile.
The changed package specification compiles and is saved to the database. The title of the EMP_EVAL
pane is no longer in italic font.
Now you can declare records of the type sal_info
, as in "Tutorial: Creating and Invoking a Subprogram with a Record Parameter".
If you declared the RECORD
type sal_info
in "Tutorial: Declaring a RECORD Type", this tutorial shows how to use the SQL Developer tool Edit to do the following:
Create a procedure, salary_schedule
, which has a parameter of type sal_info
.
Change the eval_frequency
function so that it declares a record, emp_sal
, of the type sal_info
, populates its fields, and passes it to the salary_schedule
procedure.
Because eval_frequency
will invoke salary_schedule
, the declaration of salary_schedule
must precede the declaration of eval_frequency
(otherwise, the package will not compile). However, the definition of salary_schedule
can be anywhere in the package body.
To create salary_schedule and change eval_frequency:
On the Connections tab, expand the hr_conn
information by clicking the plus sign (+) to the left of the hr_conn
icon.
Under the hr_conn
icon, a list of schema object types appears.
Expand Packages.
A list of packages appears.
Expand EMP_EVAL.
A list appears.
Right-click EMP_EVAL Body.
A list of choices appears.
Click Edit.
The EMP_EVAL
Body pane appears, showing the code for the package body.
In the EMP_EVAL
Body pane, immediately before END emp_eval
, add this definition of the salary_schedule
procedure:
PROCEDURE salary_schedule (emp IN sal_info) AS accumulating_sal NUMBER; BEGIN DBMS_OUTPUT.PUT_LINE('If salary ' || emp.sal || ' increases by ' || ROUND((emp.sal_raise * 100),0) || '% each year, it will be:'); accumulating_sal := emp.sal; WHILE accumulating_sal <= emp.sal_max LOOP accumulating_sal := accumulating_sal * (1 + emp.sal_raise); DBMS_OUTPUT.PUT_LINE(ROUND(accumulating_sal,2) ||', '); END LOOP; END salary_schedule;
A new EMP_EVAL
Body pane opens, showing the changed CREATE
PACKAGE
BODY
statement. The title of the pane is in italic font, which indicates that the changes have not been saved to the database.
In the EMP_EVAL
Body pane, enter the code shown in bold font, in this position:
create or replace PACKAGE BODY EMP_EVAL AS FUNCTION eval_frequency (emp_id EMPLOYEES.EMPLOYEE_ID%TYPE) RETURN PLS_INTEGER; PROCEDURE salary_schedule(emp IN sal_info); PROCEDURE add_eval(employee_id IN employees.employee_id%type, today IN DATE); PROCEDURE eval_department (dept_id IN NUMBER) AS
Edit the eval_frequency
function, making the changes shown in bold font:
FUNCTION eval_frequency (emp_id EMPLOYEES.EMPLOYEE_ID%TYPE) RETURN PLS_INTEGER AS h_date EMPLOYEES.HIRE_DATE%TYPE; today EMPLOYEES.HIRE_DATE%TYPE; eval_freq PLS_INTEGER; emp_sal SAL_INFO; -- replaces sal, sal_raise, and sal_max BEGIN SELECT SYSDATE INTO today FROM DUAL; SELECT HIRE_DATE INTO h_date FROM EMPLOYEES WHERE EMPLOYEE_ID = emp_id; IF ((h_date + (INTERVAL '120' MONTH)) < today) THEN eval_freq := 1; /* populate emp_sal */ SELECT JOB_ID INTO emp_sal.j_id FROM EMPLOYEES WHERE EMPLOYEE_ID = emp_id; SELECT MIN_SALARY INTO emp_sal.sal_min FROM JOBS WHERE JOB_ID = emp_sal.j_id; SELECT MAX_SALARY INTO emp_sal.sal_max FROM JOBS WHERE JOB_ID = emp_sal.j_id; SELECT SALARY INTO emp_sal.sal FROM EMPLOYEES WHERE EMPLOYEE_ID = emp_id; emp_sal.sal_raise := 0; -- default CASE emp_sal.j_id WHEN 'PU_CLERK' THEN emp_sal.sal_raise := 0.08; WHEN 'SH_CLERK' THEN emp_sal.sal_raise := 0.07; WHEN 'ST_CLERK' THEN emp_sal.sal_raise := 0.06; WHEN 'HR_REP' THEN emp_sal.sal_raise := 0.05; WHEN 'PR_REP' THEN emp_sal.sal_raise := 0.05; WHEN 'MK_REP' THEN emp_sal.sal_raise := 0.04; ELSE NULL; END CASE; IF (emp_sal.sal_raise != 0) THEN salary_schedule(emp_sal); END IF; ELSE eval_freq := 2; END IF; RETURN eval_freq; END eval_frequency;
Click Compile.
When Oracle Database executes a SQL statement, it stores the result set and processing information in an unnamed private SQL area. A pointer to this unnamed area, called a cursor, lets you retrieve the rows of the result set one at a time. Cursor attributes return information about the state of the cursor.
Every time you run either a SQL DML statement or a PL/SQL SELECT
INTO
statement, PL/SQL opens an implicit cursor. You can get information about this cursor from its attributes, but you cannot control it. After the statement runs, the database closes the cursor; however, its attribute values remain available until another DML or SELECT
INTO
statement runs.
PL/SQL also lets you declare explicit cursors. An explicit cursor has a name and is associated with a query (SQL SELECT
statement)—usually one that returns multiple rows. After declaring an explicit cursor, you must open it (with the OPEN
statement), fetch rows one at a time from the result set (with the FETCH
statement), and close the cursor (with the CLOSE
statement). After closing the cursor, you can neither fetch records from the result set nor see the cursor attribute values.
The syntax for the value of an implicit cursor attribute is SQL
attribute
(for example, SQL%FOUND
). SQL
attribute
always refers to the most recently run DML or SELECT
INTO
statement.
The syntax for the value of an explicit cursor attribute is cursor_name
immediately followed by attribute
(for example, c1%FOUND
).
Table 7-1 lists the cursor attributes and the values that they can return. (Implicit cursors have additional attributes that are beyond the scope of this book.)
Table 7-1 Cursor Attribute Values
Attribute | Values for Explicit Cursor | Values for Implicit Cursor |
---|---|---|
If cursor is not open, If cursor is open but no fetch was attempted, If the most recent fetch returned a row, If the most recent fetch did not return a row, |
If no DML or If the most recent DML or If the most recent DML or |
|
If cursor is not open, If cursor is open but no fetch was attempted, If the most recent fetch returned a row, If the most recent fetch did not return a row, |
If no DML or If the most recent DML or If the most recent DML or |
|
If cursor is not open, |
|
|
If cursor is open, |
Always |
See Also:
Oracle Database PL/SQL Language Reference for more information about the SELECT
INTO
statement
Oracle Database Advanced Application Developer's Guide for more information about using cursors in applications
Oracle Database PL/SQL Language Reference for more information about managing cursors in PL/SQL
The following procedure uses each necessary statement in its simplest form, but provides references to its complete syntax.
To use an explicit cursor to retrieve result set rows one at a time:
In the declarative part:
Declare the cursor:
CURSOR cursor_name IS query;
For complete explicit cursor declaration syntax, see Oracle Database PL/SQL Language Reference.
Declare a record to hold the row returned by the cursor:
record_name cursor_name%ROWTYPE;
For complete %ROWTYPE
syntax, see Oracle Database PL/SQL Language Reference.
In the executable part:
Open the cursor:
OPEN cursor_name;
For complete OPEN
statement syntax, see Oracle Database PL/SQL Language Reference.
Fetch rows from the cursor (rows from the result set) one at a time, using a LOOP
statement that has syntax similar to this:
LOOP FETCH cursor_name INTO record_name; EXIT WHEN cursor_name%NOTFOUND; -- Process row that is in record_name: statement; [ statement; ]... END LOOP;
For complete FETCH
statement syntax, see Oracle Database PL/SQL Language Reference.
Close the cursor:
CLOSE cursor_name;
For complete CLOSE
statement syntax, see Oracle Database PL/SQL Language Reference.
This tutorial shows how to implement the procedure EMP_EVAL
.eval_department
, which uses an explicit cursor, emp_cursor
.
To implement the EMP_EVAL.eval_department procedure:
In the EMP_EVAL
package specification, change the declaration of the eval_department
procedure as shown in bold font:
PROCEDURE eval_department(dept_id IN employees.department_id%TYPE);
(For instructions for changing a package specification, see "Tutorial: Changing a Package Specification".)
In the EMP_EVAL
package body, change the definition of the eval_department
procedure as shown in bold font:
PROCEDURE eval_department (dept_id IN employees.department_id%TYPE) AS CURSOR emp_cursor IS SELECT * FROM EMPLOYEES WHERE DEPARTMENT_ID = dept_id; emp_record EMPLOYEES%ROWTYPE; -- for row returned by cursor all_evals BOOLEAN; -- true if all employees in dept need evaluations today DATE; BEGIN today := SYSDATE; IF (EXTRACT(MONTH FROM today) < 6) THEN all_evals := FALSE; -- only new employees need evaluations ELSE all_evals := TRUE; -- all employees need evaluations END IF; OPEN emp_cursor; DBMS_OUTPUT.PUT_LINE ( 'Determining evaluations necessary in department # ' || dept_id ); LOOP FETCH emp_cursor INTO emp_record; EXIT WHEN emp_cursor%NOTFOUND; IF all_evals THEN add_eval(emp_record.employee_id, today); ELSIF (eval_frequency(emp_record.employee_id) = 2) THEN add_eval(emp_record.employee_id, today); END IF; END LOOP; DBMS_OUTPUT.PUT_LINE('Processed ' || emp_cursor%ROWCOUNT || ' records.'); CLOSE emp_cursor; END eval_department;
(For a step-by-step example of changing a package body, see "Tutorial: Declaring Variables and Constants in a Subprogram".)
Compile the EMP_EVAL
package specification.
Compile the EMP_EVAL
package body.
A cursor variable is like a cursor (see "About Cursors"), except that it is not limited to one query. You can open a cursor variable for a query, process the result set, and then use the cursor variable for another query. Cursor variables are useful for passing query results between subprograms.
To declare a cursor variable, you declare a REF
CURSOR
type, and then declare a variable of that type (therefore, a cursor variable is often called a REF
CURSOR
). A REF
CURSOR
type can be either strong or weak.
A strong REF
CURSOR
type specifies a return type, which is the RECORD
type of its cursor variables. The PL/SQL compiler does not allow you to use these strongly typed cursor variables for queries that return rows that are not of the return type. Strong REF
CURSOR
types are less error-prone than weak ones, but weak ones are more flexible.
A weak REF
CURSOR
type does not specify a return type. The PL/SQL compiler accepts weakly typed cursor variables in any queries. Weak REF
CURSOR
types are interchangeable; therefore, instead of creating weak REF
CURSOR
types, you can use the predefined type weak cursor type SYS_REFCURSOR
.
After declaring a cursor variable, you must open it for a specific query (with the OPEN
FOR
statement), fetch rows one at a time from the result set (with the FETCH
statement), and then either close the cursor (with the CLOSE
statement) or open it for another specific query (with the OPEN
FOR
statement). Opening the cursor variable for another query closes it for the previous query. After closing a cursor variable for a specific query, you can neither fetch records from the result set of that query nor see the cursor attribute values for that query.
See Also:
Oracle Database PL/SQL Language Reference for more information about using cursor variables
Oracle Database PL/SQL Language Reference for the syntax of cursor variable declaration
The following procedure uses each of the necessary statements in its simplest form, but provides references to their complete syntax.
To use a cursor variable to retrieve result set rows one at a time:
In the declarative part:
Declare the REF
CURSOR
type:
TYPE cursor_type IS REF CURSOR [ RETURN return_type ];
For complete REF
CURSOR
type declaration syntax, see Oracle Database PL/SQL Language Reference.
Declare a cursor variable of that type:
cursor_variable cursor_type;
For complete cursor variable declaration syntax, see Oracle Database PL/SQL Language Reference.
Declare a record to hold the row returned by the cursor:
record_name return_type;
For complete information about record declaration syntax, see Oracle Database PL/SQL Language Reference.
In the executable part:
Open the cursor variable for a specific query:
OPEN cursor_variable FOR query;
For complete information about OPEN
FOR
statement syntax, see Oracle Database PL/SQL Language Reference.
Fetch rows from the cursor variable (rows from the result set) one at a time, using a LOOP
statement that has syntax similar to this:
LOOP FETCH cursor_variable INTO record_name; EXIT WHEN cursor_variable%NOTFOUND; -- Process row that is in record_name: statement; [ statement; ]... END LOOP;
For complete information about FETCH
statement syntax, see Oracle Database PL/SQL Language Reference.
Close the cursor variable:
CLOSE cursor_variable;
Alternatively, you can open the cursor variable for another query, which closes it for the current query.
For complete information about CLOSE
statement syntax, see Oracle Database PL/SQL Language Reference.
This tutorial shows how to change the EMP_EVAL
.eval_department
procedure so that it uses a cursor variable instead of an explicit cursor, which lets it process multiple departments. The change includes adding a procedure that uses the cursor variable.
This tutorial also shows how to make EMP_EVAL
.eval_department
and EMP_EVAL
.add_eval
more efficient: Instead of passing one field of a record to add_eval
and having add_eval
use three queries to extract three other fields of the same record, eval_department
passes the entire record to add_eval
, and add_eval
uses dot notation to access the values of the other three fields.
To change the EMP_EVAL.eval_department procedure to use a cursor variable:
In the EMP_EVAL
package specification, add the procedure declaration and the REF
CURSOR
type definition, as shown in bold font:
create or replace PACKAGE emp_eval AS PROCEDURE eval_department (dept_id IN employees.department_id%TYPE); PROCEDURE eval_everyone; FUNCTION calculate_score(eval_id IN scores.evaluation_id%TYPE , perf_id IN scores.performance_id%TYPE) RETURN NUMBER; TYPE SAL_INFO IS RECORD ( j_id jobs.job_id%type , sal_min jobs.min_salary%type , sal_max jobs.max_salary%type , salary employees.salary%type , sal_raise NUMBER(3,3)); TYPE emp_refcursor_type IS REF CURSOR RETURN employees%ROWTYPE; END emp_eval;
(For instructions for changing a package specification, see "Tutorial: Changing a Package Specification".)
In the EMP_EVAL
package body, add a forward declaration for the procedure eval_loop_control
and change the declaration of the procedure add_eval
, as shown in bold font:
create or replace PACKAGE BODY EMP_EVAL AS FUNCTION eval_frequency (emp_id IN EMPLOYEES.EMPLOYEE_ID%TYPE) RETURN PLS_INTEGER; PROCEDURE salary_schedule(emp IN sal_info); PROCEDURE add_eval(emp_record IN EMPLOYEES%ROWTYPE, today IN DATE); PROCEDURE eval_loop_control(emp_cursor IN emp_refcursor_type); ...
(For a step-by-step example of changing a package body, see "Tutorial: Declaring Variables and Constants in a Subprogram".)
Change the eval_department
procedure to retrieve three separate result sets based on the department, and to invoke the eval_loop_control
procedure, as shown in bold font:
PROCEDURE eval_department(dept_id IN employees.department_id%TYPE) AS emp_cursor emp_refcursor_type; current_dept departments.department_id%TYPE; BEGIN current_dept := dept_id; FOR loop_c IN 1..3 LOOP OPEN emp_cursor FOR SELECT * FROM employees WHERE current_dept = eval_department.department_id; DBMS_OUTPUT.PUT_LINE ('Determining necessary evaluations in department #' || current_dept); eval_loop_control(emp_cursor); DBMS_OUTPUT.PUT_LINE ('Processed ' || emp_cursor%ROWCOUNT || ' records.'); CLOSE emp_cursor; current_dept := current_dept + 10; END LOOP; END eval_department;
Change the add_eval
as shown in bold font:
PROCEDURE add_eval(emp_record IN employees%ROWTYPE, today IN DATE) AS -- (Delete local variables) BEGIN INSERT INTO EVALUATIONS ( evaluation_id, employee_id, evaluation_date, job_id, manager_id, department_id, total_score ) VALUES ( evaluations_seq.NEXTVAL, -- evaluation_id emp_record.employee_id, -- employee_id today, -- evaluation_date emp_record.job_id, -- job_id emp_record.manager_id, -- manager_id emp_record.department_id, -- department_id 0 -- total_score ); END add_eval;
Before END
EMP_EVAL
, add the following procedure, which fetches the individual records from the result set and processes them:
PROCEDURE eval_loop_control (emp_cursor IN emp_refcursor_type) AS emp_record EMPLOYEES%ROWTYPE; all_evals BOOLEAN; today DATE; BEGIN today := SYSDATE; IF (EXTRACT(MONTH FROM today) < 6) THEN all_evals := FALSE; ELSE all_evals := TRUE; END IF; LOOP FETCH emp_cursor INTO emp_record; EXIT WHEN emp_cursor%NOTFOUND; IF all_evals THEN add_eval(emp_record, today); ELSIF (eval_frequency(emp_record.employee_id) = 2) THEN add_eval(emp_record, today); END IF; END LOOP; END eval_loop_control;
Before END
EMP_EVAL
, add the following procedure, which retrieves a result set that contains all employees in the company:
PROCEDURE eval_everyone AS emp_cursor emp_refcursor_type; BEGIN OPEN emp_cursor FOR SELECT * FROM employees; DBMS_OUTPUT.PUT_LINE('Determining number of necessary evaluations.'); eval_loop_control(emp_cursor); DBMS_OUTPUT.PUT_LINE('Processed ' || emp_cursor%ROWCOUNT || ' records.'); CLOSE emp_cursor; END eval_everyone;
Compile the EMP_EVAL
package specification.
Compile the EMP_EVAL
package body.
An associative array is a type of collection.
Topics:
See Also:
For more information about collections:A collection is a PL/SQL composite variable that stores elements of the same type in a specified order, similar to a one-dimensional array. The internal components of a collection are called elements. Each element has a unique subscript that identifies its position in the collection. To access a collection element, you use subscript notation: collection_name
(element_subscript
).
You can treat collection elements like scalar variables. You can also pass entire collections as subprogram parameters (if neither the sending nor receiving subprogram is a standalone stored subprogram).
A collection method is a built-in PL/SQL subprogram that either returns information about a collection or operates on a collection. To invoke a collection method, you use dot notation: collection_name
.method_name
. For example, collection_name
.COUNT
returns the number of elements in the collection.
PL/SQL has three types of collections:
Associative arrays (formerly called "PL/SQL tables" or "index-by tables")
This document explains only associative arrays.
See Also:
Oracle Database PL/SQL Language Reference for more information about PL/SQL collection types
Oracle Database PL/SQL Language Reference for more information about collection methods
An associative array is an unbounded set of key-value pairs. Each key is unique, and serves as the subscript of the element that holds the corresponding value. Therefore, you can access elements without knowing their positions in the array, and without traversing the array.
The data type of the key can be either PLS_INTEGER
or VARCHAR2
(
length
)
.
If the data type of the key is PLS_INTEGER
, and the associative array is indexed by integer, and it is dense (that is, it has no gaps between elements), then every element between the first and last element is defined and has a value (which can be NULL
).
If the key type is VARCHAR2
(
length
)
, the associative array is indexed by string (of length
characters), and it is sparse; that is, it might have gaps between elements.
When traversing a dense associative array, you do not need to beware of gaps between elements; when traversing a sparse associative array, you do.
To assign a value to an associative array element, you can use an assignment operator:
array_name(key) := value
If key
is not in the array, the assignment statement adds the key
-value
pair to the array. Otherwise, the statement changes the value of array_name
(
key
)
to value
.
Associative arrays are useful for storing data temporarily. They do not use the disk space or network operations that tables require. However, because associative arrays are intended for temporary storage, you cannot manipulate them with DML statements or use SELECT
INTO
statements to assign their values to variables.
If you declare an associative array in a package, and assign values to the variable in the package body, then the associative array exists for the life of the database session. Otherwise, it exists for the life of the subprogram in which you declare it.
See Also:
Oracle Database PL/SQL Language Reference for more information about associative arraysTo declare an associative array, you declare an associative array type, and then declare a variable of that type. The simplest syntax is:
TYPE array_type IS TABLE OF element_type INDEX BY key_type; array_name array_type;
An efficient way to declare an associative array is with a cursor, using the following procedure. The procedure uses each necessary statement in its simplest form, but provides references to its complete syntax.
To use a cursor to declare an associative array:
In the declarative part:
Declare the cursor:
CURSOR cursor_name IS query;
For complete explicit cursor declaration syntax, see Oracle Database PL/SQL Language Reference.
Declare the associative array type:
TYPE array_type IS TABLE OF cursor_name%ROWTYPE INDEX BY { PLS_INTEGER | VARCHAR2 length }
For complete associative array type declaration syntax, see Oracle Database PL/SQL Language Reference.
Declare an associative array variable of that type:
array_name array_type;
For complete variable declaration syntax, see Oracle Database PL/SQL Language Reference.
Example 7-9 uses the preceding procedure to declare two associative arrays, employees_jobs
and jobs_
, and then declares a third associative array, job_titles_type
, without using a cursor. The first two arrays are indexed by integer; the third is indexed by string.
Note:
TheORDER
BY
clause in the declaration of employees_jobs_cursor
determines the storage order of the elements of the associative array employee_jobs
.Example 7-9 Declaring Associative Arrays
DECLARE -- Declare cursor: CURSOR employees_jobs_cursor IS SELECT FIRST_NAME, LAST_NAME, JOB_ID FROM EMPLOYEES ORDER BY JOB_ID, LAST_NAME, FIRST_NAME; -- Declare associative array type: TYPE employees_jobs_type IS TABLE OF employees_jobs_cursor%ROWTYPE INDEX BY PLS_INTEGER; -- Declare associative array: employees_jobs employees_jobs_type; -- Use same procedure to declare another associative array: CURSOR jobs_cursor IS SELECT JOB_ID, JOB_TITLE FROM JOBS; TYPE jobs_type IS TABLE OF jobs_cursor%ROWTYPE INDEX BY PLS_INTEGER; jobs_ jobs_type; -- Declare associative array without using cursor: TYPE job_titles_type IS TABLE OF JOBS.JOB_TITLE%TYPE INDEX BY JOBS.JOB_ID%TYPE; -- jobs.job_id%type is varchar2(10) job_titles job_titles_type; BEGIN NULL; END; /
See Also:
Oracle Database PL/SQL Language Reference for associative array declaration syntax
The most efficient way to populate a dense associative array is with a cursor and the FETCH
statement with the BULK
COLLECT
INTO
clause, using the following procedure. The procedure uses each necessary statement in its simplest form, but provides references to its complete syntax.
You cannot use the following procedure to populate a sparse associative array. Instead, you must use an assignment statement inside a loop statement. For information about loop statements, see "Controlling Program Flow".
To use a cursor to populate an associative array indexed by integer:
If you have not done so, declare an associative array with a cursor, using the procedure in "Declaring Associative Arrays".
In the executable part of the PL/SQL unit in which you declared the associative array:
Open the cursor:
OPEN cursor_name;
For complete OPEN
statement syntax, see Oracle Database PL/SQL Language Reference.
Fetch all rows from the cursor into the associative array variable at once, using a FETCH
statement with the BULK
COLLECT
INTO
clause:
FETCH cursor_name BULK COLLECT INTO aa_variable;
For complete FETCH
statement syntax, see Oracle Database PL/SQL Language Reference.
Close the cursor:
CLOSE cursor_name;
For complete CLOSE
statement syntax, see Oracle Database PL/SQL Language Reference.
Example 7-10 uses the preceding procedure to populate the associative arrays employees_jobs
and jobs_
, which are indexed by integer. Then it uses an assignment statement inside a FOR
LOOP
statement to populate the associative array job_titles_type
, which is indexed by string.
FOR
LOOP
Example 7-10 Populating Associative Arrays
-- Declarative part from Example 7-9 goes here. BEGIN -- Populate associative arrays indexed by integer: OPEN employees_jobs_cursor; FETCH employees_jobs_cursor BULK COLLECT INTO employees_jobs; CLOSE employees_jobs_cursor; OPEN jobs_cursor; FETCH jobs_cursor BULK COLLECT INTO jobs_; CLOSE jobs_cursor; -- Populate associative array indexed by string: FOR i IN 1..jobs_.COUNT() LOOP job_titles(jobs_(i).job_id) := jobs_(i).job_title; END LOOP; END; /
See Also:
"About Cursors"A dense associative array (indexed by integer) has no gaps between elements—every element between the first and last element is defined and has a value (which can be NULL
). You can traverse a dense array with a FOR
LOOP
statement, as in Example 7-11.
When inserted in the executable part of Example 7-10, after the code that populates the employees_jobs
array, the FOR
LOOP
statement in Example 7-11 prints the elements of the employees_jobs
array in the order in which they were stored. Their storage order was determined by the ORDER
BY
clause in the declaration of employees_jobs_cursor
, which was used to declare employees_jobs
(see Example 7-9).
FOR
LOOP
FOR
LOOP
The upper bound of the FOR
LOOP
statement, employees_jobs
. COUNT
, invokes a collection method that returns the number of elements in the array. For more information about COUNT
, see Oracle Database PL/SQL Language Reference.
Example 7-11 Traversing a Dense Associative Array
-- Code that populates employees_jobs must precede this code:
FOR i IN 1..employees_jobs.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(
RPAD(employees_jobs(i).first_name, 23) ||
RPAD(employees_jobs(i).last_name, 28) || employees_jobs(i).job_id);
END LOOP;
Result:
William Gietz AC_ACCOUNT Shelley Higgins AC_MGR Jennifer Whalen AD_ASST Steven King AD_PRES Lex De Haan AD_VP Neena Kochhar AD_VP John Chen FI_ACCOUNT ... Jose Manuel Urman FI_ACCOUNT Nancy Greenberg FI_MGR Susan Mavris HR_REP David Austin IT_PROG ... Valli Pataballa IT_PROG Michael Hartstein MK_MAN Pat Fay MK_REP Hermann Baer PR_REP Shelli Baida PU_CLERK ... Sigal Tobias PU_CLERK Den Raphaely PU_MAN Gerald Cambrault SA_MAN ... Eleni Zlotkey SA_MAN Ellen Abel SA_REP ... Clara Vishney SA_REP Sarah Bell SH_CLERK ... Peter Vargas ST_CLERK Adam Fripp ST_MAN ... Matthew Weiss ST_MAN
A sparse associative array (indexed by string) might have gaps between elements. You can traverse it with a WHILE
LOOP
statement, as in Example 7-12.
To run the code in Example 7-12, which prints the elements of the job_titles
array:
At the end of the declarative part of Example 7-9, insert this variable declaration:
i jobs_.job_id%TYPE;
In the executable part of Example 7-10, after the code that populates the job_titles
array, insert the code from Example 7-12.
Example 7-12 includes two collection method invocations, job_titles
.FIRST
and job_titles
.NEXT(i)
. job_titles
.FIRST
returns the first element of job_titles
, and job_titles
.NEXT(i)
returns the subscript that succeeds i
. For more information about FIRST
, see Oracle Database PL/SQL Language Reference. For more information about NEXT
, see Oracle Database PL/SQL Language Reference.
WHILE
LOOP
Example 7-12 Traversing a Sparse Associative Array
/* Declare this variable in declarative part: i jobs_.job_id%TYPE; Add this code to the executable part, after code that populates job_titles: */ i := job_titles.FIRST; WHILE i IS NOT NULL LOOP DBMS_OUTPUT.PUT_LINE(RPAD(i, 12) || job_titles(i)); i := job_titles.NEXT(i); END LOOP;
Result:
AC_ACCOUNT Public Accountant AC_MGR Accounting Manager AD_ASST Administration Assistant AD_PRES President AD_VP Administration Vice President FI_ACCOUNT Accountant FI_MGR Finance Manager HR_REP Human Resources Representative IT_PROG Programmer MK_MAN Marketing Manager MK_REP Marketing Representative PR_REP Public Relations Representative PU_CLERK Purchasing Clerk PU_MAN Purchasing Manager SA_MAN Sales Manager SA_REP Sales Representative SH_CLERK Shipping Clerk ST_CLERK Stock Clerk ST_MAN Stock Manager
Topics:
See Also:
Oracle Database PL/SQL Language Reference for more information about handling PL/SQL errorsWhen a run-time error occurs in PL/SQL code, an exception is raised. If the subprogram (or block) in which the exception is raised has an exception-handling part, control transfers to it; otherwise, execution stops.
Run-time errors can arise from design faults, coding mistakes, hardware failures, and many other sources. Because you cannot anticipate all possible errors, Oracle recommends including exception-handling parts in your subprograms ("About Subprogram Structure" shows where to put the exception-handling part).
Oracle Database has many predefined exceptions, which it raises automatically when a program violates database rules or exceeds system-dependent limits. For example, if a SELECT
INTO
statement returns no rows, Oracle Database raises the predefined exception NO_DATA_FOUND
. For a summary of predefined PL/SQL exceptions, see Oracle Database PL/SQL Language Reference.
PL/SQL lets you define (declare) your own exceptions. An exception declaration has this syntax:
exception_name EXCEPTION;
Unlike a predefined exception, a user-defined exception must be raised explicitly, using either the RAISE
statement or the DBMS_STANDARD
.RAISE_APPLICATION_ERROR
procedure. For example:
IF condition THEN RAISE exception_name;
For information about the DBMS_STANDARD
.RAISE_APPLICATION_ERROR
procedure, see Oracle Database PL/SQL Language Reference.
The exception-handling part of a subprogram contains one or more exception handlers. An exception handler has this syntax:
WHEN { exception_name [ OR exception_name ]... | OTHERS } THEN statement; [ statement; ]...
A WHEN
OTHERS
exception handler handles unexpected run-time errors. If used, it must be last. For example:
EXCEPTION WHEN exception_1 THEN statement; [ statement; ]... WHEN exception_2 OR exception_3 THEN statement; [ statement; ]... WHEN OTHERS THEN statement; [ statement; ]... END;
An alternative to the WHEN
OTHERS
exception handler is the EXCEPTION_INIT
pragma, which associates a user-defined exception name with an Oracle Database error number.
See Also:
Oracle Database PL/SQL Language Reference for more information about exception declaration syntax
Oracle Database PL/SQL Language Reference for more information about exception handler syntax
Oracle Database PL/SQL Language Reference for more information about the EXCEPTION_INIT
pragma
Example 7-13 shows, in bold font, how to change the EMP_EVAL
.eval_department
procedure to handle the predefined exception NO_DATA_FOUND
. Make this change and compile the changed procedure. (For an example of how to change a package body, see "Tutorial: Declaring Variables and Constants in a Subprogram".)
Example 7-13 Handling Predefined Exception NO_DATA_FOUND
PROCEDURE eval_department(dept_id IN employees.department_id%TYPE) AS emp_cursor emp_refcursor_type; current_dept departments.department_id%TYPE; BEGIN current_dept := dept_id; FOR loop_c IN 1..3 LOOP OPEN emp_cursor FOR SELECT * FROM employees WHERE current_dept = dept_id; DBMS_OUTPUT.PUT_LINE ('Determining necessary evaluations in department #' || current_dept); eval_loop_control(emp_cursor); DBMS_OUTPUT.PUT_LINE ('Processed ' || emp_cursor%ROWCOUNT || ' records.'); CLOSE emp_cursor; current_dept := current_dept + 10; END LOOP; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE ('The query did not return a result set'); END eval_department;
See Also:
Oracle Database PL/SQL Language Reference for more information about predefined exceptions
Oracle Database PL/SQL Language Reference for more information about handling raised exceptions
Example 7-14 shows, in bold font, how to change the EMP_EVAL
.calculate_score
function to declare and handle two user-defined exceptions, wrong_weight
and wrong_score
. Make this change and compile the changed function. (For an example of how to change a package body, see "Tutorial: Declaring Variables and Constants in a Subprogram".)
Example 7-14 Handling User-Defined Exceptions
FUNCTION calculate_score ( evaluation_id IN scores.evaluation_id%TYPE , performance_id IN scores.performance_id%TYPE ) RETURN NUMBER AS weight_wrong EXCEPTION; score_wrong EXCEPTION; n_score scores.score%TYPE; n_weight performance_parts.weight%TYPE; running_total NUMBER := 0; max_score CONSTANT scores.score%TYPE := 9; max_weight CONSTANT performance_parts.weight%TYPE:= 1; BEGIN SELECT s.score INTO n_score FROM SCORES s WHERE evaluation_id = s.evaluation_id AND performance_id = s.performance_id; SELECT p.weight INTO n_weight FROM PERFORMANCE_PARTS p WHERE performance_id = p.performance_id; BEGIN IF (n_weight > max_weight) OR (n_weight < 0) THEN RAISE weight_wrong; END IF; END; BEGIN IF (n_score > max_score) OR (n_score < 0) THEN RAISE score_wrong; END IF; END; running_total := n_score * n_weight; RETURN running_total; EXCEPTION WHEN weight_wrong THEN DBMS_OUTPUT.PUT_LINE( 'The weight of a score must be between 0 and ' || max_weight); RETURN -1; WHEN score_wrong THEN DBMS_OUTPUT.PUT_LINE( 'The score must be between 0 and ' || max_score); RETURN -1; END calculate_score;
See Also:
Oracle Database PL/SQL Language Reference for more information about user-defined exceptions
Oracle Database PL/SQL Language Reference for more information about handling raised exceptions