PL/SQL (Procedural Language/Structured Query Language) is procedural extension for SQL and the Oracle relational database.
PL/SQL is available in Oracle Database (since version 6 – stored pl/sql procedures/functions/packages/
Oracle Corporation usually extends PL/SQL functionality with each successive release of the Oracle Database.
PL/SQL includes procedural language elements such as conditions and loops. It allows declaration of constants and variables, procedures and functions, types and variables of those types, and triggers.
It can handle exceptions (runtime errors). Arrays are supported involving the use of PL/SQL collections.
Implementations from version 8 of Oracle Database onwards have included features associated with object-orientation.
PL/SQL can be used for both server-side and Client side development. One can create PL/SQL units such as procedures, functions, packages, types, and triggers, which are stored in the database for reuse by applications that use any of the Oracle Database programmatic interfaces.
PL/SQL is a block-structured language and programs are divided and written in logical blocks of code. The basic parts of PL/ SQL blocks are
- Declarative block ( optional)
- Executable block (Mandatory )
- Exception Handling block (optional)
Declarative block
It is used to define user defined types, variable which can be used in the executable part for further manipulations.
Executable block
All procedural statement are included between BEGIN and END statements. It consists of the executable PL/SQL statements of the program. It should have at least one executable line of code, which may be NULL command.
Exception Handling block
This block starts with keyword EXCEPTION. Errors that occur during execution will handle in this block.
Notes :
- A PL/SQL program is a logical block , which contain any number of nested sub blocks.
- Block can be nested in the executable and exception handling parts of a PL/SQL block, or a sub program.
- A PL/ SQL in marked with either a DECLARE or BEGIN keywords and ends with the keyword END.
- Only BEGIN and END keywords are mandatory.
- A semicolon (;) has to be placed after the END keyword.
Basic Syntax :
DECLARE variables; cursor declerations; user_defined exceptions; BEGIN < executable command (s)> EXCEPTION Action to perform when error occured END;
Under Progress………………….
PL/SQL combines the data-manipulating power of SQL with the processing power of procedural languages.
When you can solve a problem with SQL, you can issue SQL statements from your PL/SQL program, without learning new APIs.
Like other procedural programming languages, PL/SQL lets you declare constants and variables, control program flow, define subprograms, and trap runtime errors.
You can break complex problems into easily understandable subprograms, which you can reuse in multiple applications.
PLSQL Advantages
Tight Integration with SQL
PL/SQL is tightly integrated with SQL, the most widely used database manipulation language.
PL/SQL fully supports SQL data types and lets use all SQL data manipulation, cursor control, and transaction control statements, and all SQL functions, operators.
High Performance
PL/SQL executes block of statements at a time, significantly reducing traffic between the application and the database.
The PL/SQL compiler has an optimizer that can rearrange code for better performance.
High Productivity
PL/SQL lets you write compact code for manipulating data. Just as a scripting language like PERL can read, transform, and write data in files, PL/SQL can query, transform, and update data in a database.
PL/SQL has many features that save designing and debugging time, and it is the same in all environments. If you learn to use PL/SQL with one Oracle tool, you can transfer your knowledge to other Oracle tools. For example, you can create a PL/SQL block in SQL Developer and then use it in an Oracle Forms trigger.
Portability
You can run PL/SQL applications on any operating system and platform where Oracle Database runs.
Scalability
PL/SQL stored subprograms increase scalability by centralizing application processing on the database server.
Manageability
PL/SQL stored subprograms increase manageability because you can maintain only one copy of a subprogram, on the database server, rather than one copy on each client system. Any number of applications can use the subprograms, and you can change the subprograms without affecting the applications that invoke them.
Support for Object-Oriented Programming
PL/SQL supports object-oriented programming with “Abstract Data Types”.
Support for Developing Web Applications
PL/SQL lets you create applications that generate web pages directly from the database, allowing you to make your database available on the Web and make back-office data accessible on the intranet.
You can implement a Web browser-based application entirely in PL/SQL with PL/SQL Gateway and the PL/SQL Web Toolkit.
SQLvsPLSQL
PL/SQL | SQL |
---|---|
PL/SQL is a procedural language to create applications. | SQL is a data oriented language for selecting and manipulating sets of data. |
PL/SQL can be the application language just like Java or PHP can. PL/SQL might be the language we use to build, format and display those screens, web pages and reports. | SQL may be the source of data for our screens, web pages and reports. |
PL/SQL is executed as a block of code. | SQL is executed one statement at a time. |
PL/SQL tell the database how to do things (procedural). | SQL tells the database what to do (declarative), not how to do it. |
PL/SQL is used to code program blocks, triggers, functions, procedures and packages. | SQL is used to code queries, DML and DDL statements. |
We can embed SQL in a PL/SQL program. | we cannot embed PL/SQL within a SQL statement. |
We will learn about DataTypes of PL/SQL in this chapter. The PL/SQL variables, constants and parameters must have a valid datatype, which specifies a storage format, constraints, and a valid range of values. We will focus on the SCALAR and the LOB data types in this chapter. The other two datatypes will be covered in other chapters.
S.No | Category | Description |
---|---|---|
1 | Scalar | Single values with no internal components, such as a NUMBER, DATE, or BOOLEAN. |
2 | Large Object (LOB) | Pointers to large objects that are stored separately from other data items, such as text, graphic images, video clips, and sound waveforms. |
3 | Composite | Data items that have internal components that can be accessed individually. For example, collections and records. |
4 | Reference | Pointers to other data items. |
PL/SQL Scalar DataTypes and Subtypes
PL/SQL Scalar Data Types and Subtypes come under the following categories
S.No | Data Type | Description |
---|---|---|
1 | Numeric | Numeric values on which arithmetic operations are performed. |
2 | Character | Alphanumeric values that represent single characters or strings of characters. |
3 | Boolean | Logical values on which logical operations are performed. |
4 | Datetime | Dates and times. |
PL/SQL provides subtypes of data types. For example, the datatype NUMBER has a subtype called INTEGER. You can use the subtypes in your PL/SQL program to make the data types compatible with data types in other programs while embedding the PL/SQL code in another program, such as a Java program.
PL/SQL Numeric DataTypes and Sub-types
Following table lists out the PL/SQL pre-defined numeric datatypes and their sub-types –
S.No | Data Type | Description |
---|---|---|
1 | PLS_INTEGER | Signed integer in range -2,147,483,648 through 2,147,483,647, represented in 32 bits. |
2 | BINARY_INTEGER | Signed integer in range -2,147,483,648 through 2,147,483,647, represented in 32 bits. |
3 | BINARY_FLOAT | Single-precision IEEE 754 format floating-point number. |
4 | BINARY_DOUBLE | Double-precision IEEE 754format floating-point number. |
5 | NUMBER(prec, scale) | Fixed-point or floating-point number with absolute value in range 1E-130 to (but not including) 1.0E126. A NUMBER variable can also represent 0. |
6 | DEC(prec, scale) | ANSI specific fixed-point type with maximum precision of 38 decimal digits. |
7 | DECIMAL(prec, scale) | IBM specific fixed-point type with maximum precision of 38 decimal digits. |
8 | NUMERIC(pre, secale) | Floating type with maximum precision of 38 decimal digits. |
9 | DOUBLE PRECISION | ANSI specific floating-point type with maximum precision of 126 binary digits (approximately 38 decimal digits). |
10 | FLOAT | ANSI and IBM specific floating-point type with maximum precision of 126 binary digits (approximately 38 decimal digits). |
11 | INT | ANSI specific integer type with maximum precision of 38 decimal digits./td> |
12 | INTEGER | ANSI and IBM specific integer type with maximum precision of 38 decimal digits |
13 | SMALLINT | ANSI and IBM specific integer type with maximum precision of 38 decimal digits |
14 | REAL | Floating-point type with maximum precision of 63 binary digits (approximately 18 decimal digits). |
Following is a valid declaration –
DECLARE num1 INTEGER; num2 REAL; num3 DOUBLE PRECISION; BEGIN null; END; /
When the above code is compiled and executed, it produces the following result −
PL/SQL procedure successfully completed
PL/SQL Character Data Types and Subtypes
S.No | Data Type | Description |
---|---|---|
1 | CHAR | Fixed-length character string with maximum size of 32,767 bytes. |
2 | VARCHAR2 | Variable-length character string with maximum size of 32,767 bytes. |
3 | RAW | Variable-length binary or byte string with maximum size of 32,767 bytes, not interpreted by PL/SQL. |
4 | NCHAR | Fixed-length national character string with maximum size of 32,767 bytes. |
5 | NVARCHAR2 | Variable-length national character string with maximum size of 32,767 bytesl. |
6 | LONG | Variable-length character string with maximum size of 32,760 bytes. |
7 | LONG RAW | Variable-length binary or byte string with maximum size of 32,760 bytes, not interpreted by PL/SQL. |
8 | ROWID | Physical row identifier, the address of a row in an ordinary table. |
9 | UROWID | Universal row identifier (physical, logical, or foreign row identifier). |
PL/SQL Boolean Data Types
The BOOLEAN data type stores logical values that are used in logical operations. The logical values are the Boolean values TRUE and FALSE and the value NULL.
However, SQL has no data type equivalent to BOOLEAN. Therefore, Boolean values cannot be used in −
- SQL statements
- Built-in SQL functions (such as TO_CHAR)
- PL/SQL functions invoked from SQL statements
PL/SQL Datetime and Interval Types
The DATE datatype is used to store fixed-length datetimes, which include the time of day in seconds since midnight. Valid dates range from January 1, 4712 BC to December 31, 9999 AD.
The default date format is set by the Oracle initialization parameter NLS_DATE_FORMAT. For example, the default might be ‘DD-MON-YY’, which includes a two-digit number for the day of the month, an abbreviation of the month name, and the last two digits of the year. For example, 01-OCT-12.
Each DATE includes the century, year, month, day, hour, minute, and second. The following table shows the valid values for each field −
Name of field | Valid Datetime Values | Valid Interval Values |
---|---|---|
YEAR | -4712 to 9999 (excluding year 0) | Any nonzero integer |
MONTH | 01 to 12 | 0 to 11 |
DAY | 01 to 31 (limited by the values of MONTH and YEAR, according to the rules of the calendar for the locale) | Any nonzero integer |
HOUR | 00 to 23 | 0 to 23 |
MINUTE | 00 to 59 | 0 to 59 |
SECOND | 00 to 59.9(n), where 9(n) is the precision of time fractional seconds | 0 to 59.9(n), where 9(n) is the precision of interval fractional seconds |
TIMEZONE_HOUR | -12 to 14 (range accommodates daylight savings time changes) | Not applicable |
TIMEZONE_MINUTE | 00 to 59 | Not applicable |
TIMEZONE_REGION | Found in the dynamic performance view V$TIMEZONE_NAMES | Not applicable |
TIMEZONE_ABBR | Found in the dynamic performance view V$TIMEZONE_NAMES | Not applicable |
PL/SQL Large Object (LOB) Data Types
Large Object (LOB) data types refer to large data items such as text, graphic images, video clips, and sound waveforms. LOB datatypes allow efficient, random, piecewise access to this data. Following are the predefined PL/SQL LOB data types −
Data Type | Description | Size |
---|---|---|
BFILE | Used to store large binary objects in operating system files outside the database. | System-dependent. Cannot exceed 4 gigabytes (GB). |
BLOB | Used to store large binary objects in the database. | 8 to 128 terabytes (TB) |
CLOB | Used to store large blocks of character data in the database. | 8 to 128 TB |
NCLOB | Used to store large blocks of NCHAR data in the database. | 8 to 128 TB |
PL/SQL User-Defined 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. PL/SQL predefines several subtypes in package STANDARD. For example, PL/SQL predefine the subtypes CHARACTER and INTEGER
SUBTYPE CHARACTER IS CHAR; SUBTYPE INTEGER IS NUMBER(38,0);
You can define and use your own subtypes. The following program illustrates defining and using a user-defined subtype −
DECLARE SUBTYPE name IS char(20); SUBTYPE message IS varchar2(100); salutation name; greetings message; BEGIN salutation := 'Reader '; greetings := 'Welcome to the World of PL/SQL'; dbms_output.put_line('Hello ' || salutation || greetings); END; /
When the above code is executed at the SQL prompt, it produces the following result −
Hello Reader Welcome to the World of PL/SQL PL/SQL procedure successfully completed.
NULLs in PL/SQL
PL/SQL NULL values represent missing or unknown data and they are not an integer, a character, or any other specific data type. Note that NULL is not the same as an empty data string or the null character value ‘\0’. A null can be assigned but it cannot be equated with anything, including itself.
We will learn Variables in Pl/SQL in this lesson. A variable is nothing but a name given to a storage area that our programs can manipulate. Each variable in PL/SQL has a specific data type, which determines the size and the layout of the variable’s memory; the range of values that can be stored within that memory and the set of operations that can be applied to the variable.
The name of a PL/SQL variable consists of a letter optionally followed by more letters, numerals, dollar signs, underscores, and number signs and should not exceed 30 characters. By default, variable names are not case-sensitive. You cannot use a reserved PL/SQL keyword as a variable name.
PL/SQL programming language allows to define various types of variables, such as date time data types, records, collections, etc. which we will cover in subsequent chapters. For this chapter, let us study only basic variable types.
Variable Declaration in PL/SQL
PL/SQL variables must be declared in the declaration section or in a package as a global variable. When you declare a variable, PL/SQL allocates memory for the variable’s value and the storage location is identified by the variable name.
The syntax for declaring a variable is
variable_name [CONSTANT] datatype [NOT NULL] [:= | DEFAULT initial_value]
Where, variable_name is a valid identifier in PL/SQL, datatype must be a valid PL/SQL data type or any user defined data type which we already have discussed in the last chapter. Some valid variable declarations along with their definition are shown below −
sales number(10, 2); pi CONSTANT double precision := 3.1415; name varchar2(25); address varchar2(100);
When you provide a size, scale or precision limit with the data type, it is called a constrained declaration. Constrained declarations require less memory than unconstrained declarations. For example −
sales number(10, 2); name varchar2(25); address varchar2(100);
Initializing Variables in PL/SQL
Whenever you declare a variable, PL/SQL assigns it a default value of NULL. If you want to initialize a variable with a value other than the NULL value, you can do so during the declaration, using either of the following
- The DEFAULT keyword
- The assignment operator
For example −
counter binary_integer := 0; greetings varchar2(20) DEFAULT 'Have a Good Day';
You can also specify that a variable should not have a NULL value using the NOT NULL constraint. If you use the NOT NULL constraint, you must explicitly assign an initial value for that variable.
It is a good programming practice to initialize variables properly otherwise, sometimes programs would produce unexpected results. Try the following example which makes use of various types of variables
DECLARE a integer := 20; b integer := 30; c integer; f real; BEGIN c := a + b; dbms_output.put_line('Value of c: ' || c); f := 70.0/3.0; dbms_output.put_line('Value of f: ' || f); END; /
When the above code is executed, it produces the following result
Value of c: 50 Value of f: 23.333333333333333333 PL/SQL procedure successfully completed.
Variable Scope in PL/SQL
PL/SQL allows the nesting of blocks, i.e., each program block may contain another inner block. If a variable is declared within an inner block, it is not accessible to the outer block. However, if a variable is declared and accessible to an outer block, it is also accessible to all nested inner blocks. There are two types of variable scope
- Local variables: Variables declared in an inner block and not accessible to outer blocks.
- Global variables: Variables declared in the outermost block or a package.
Following example shows the usage of Local and Global variables in its simple form
DECLARE -- Global variables num1 number := 75; num2 number := 45; BEGIN dbms_output.put_line('Outer Variable num1: ' || num1); dbms_output.put_line('Outer Variable num2: ' || num2); DECLARE -- Local variables num1 number := 250; num2 number := 155; BEGIN dbms_output.put_line('Inner Variable num1: ' || num1); dbms_output.put_line('Inner Variable num2: ' || num2); END; END; /
When the above code is executed, it produces the following result −
Outer Variable num1: 75 Outer Variable num2: 45 Inner Variable num1: 250 Inner Variable num2: 155 PL/SQL procedure successfully completed.
Assigning SQL Query Results to PL/SQL Variables
You can use the SELECT INTO statement of SQL to assign values to PL/SQL variables. For each item in the SELECT list, there must be a corresponding, type-compatible variable in the INTO list. The following example illustrates the concept. Let us create a table named CUSTOMERS −
CREATE TABLE CUSTOMERS( ID NUMBER NOT NULL, NAME VARCHAR2(50) NOT NULL, AGE NUMBER NOT NULL, ADDRESS VARCHAR2(500), SALARY NUMBER, PRIMARY KEY (ID) ); Table Created
Let us now insert some values in the table −
INSERT INTO CUSTOMERS (ID,NAME,AGE,ADDRESS,SALARY) VALUES (1, 'Joseph', 30, 'Athens', 2500.00 ); INSERT INTO CUSTOMERS (ID,NAME,AGE,ADDRESS,SALARY) VALUES (2, 'Henry', 35, 'Okland', 3500.00 ); INSERT INTO CUSTOMERS (ID,NAME,AGE,ADDRESS,SALARY) VALUES (3, 'William', 27, 'Moscow', 2800.00 ); INSERT INTO CUSTOMERS (ID,NAME,AGE,ADDRESS,SALARY) VALUES (4, 'David', 25, 'Fresno', 4500.00 );
The following program assigns values from the above table to PL/SQL variables using the SELECT INTO clause of SQL
DECLARE c_id customers.id%type := 1; c_name customers.name%type; c_addr customers.address%type; c_sal customers.salary%type; BEGIN SELECT name, address, salary INTO c_name, c_addr, c_sal FROM customers WHERE id = c_id; dbms_output.put_line ('Customer ' ||c_name || ' from ' || c_addr || ' earns ' || c_sal); END; /
When the above code is executed, it produces the following result
Customer Joseph from Athens earns 2000 PL/SQL procedure completed successfully
Constants and Literals
We will learn constants and literals in PL/SQL. A constant holds a value that once declared, does not change in the program. A constant declaration specifies its name, data type, and value, and allocates storage for it. The declaration can also impose the NOT NULL constraint.
Declaring a Constant
A constant is declared using the CONSTANT keyword. It requires an initial value and does not allow that value to be changed.
Illustration
PI CONSTANT NUMBER := 3.141592654; DECLARE -- constant declaration pi constant number := 3.141592654; -- other declarations radius number(5,2); dia number(5,2); circumference number(7, 2); area number (10, 2); BEGIN -- processing radius := 9.5; dia := radius * 2; circumference := 2.0 * pi * radius; area := pi * radius * radius; -- output dbms_output.put_line('Radius: ' || radius); dbms_output.put_line('Diameter: ' || dia); dbms_output.put_line(' Circumference: ' || circumference); dbms_output.put_line('Area: ' || area); END; /
When the above code is executed at the SQL prompt, it produces the following result −
Radius: 9.5 Diameter: 19 Circumference: 59.69 Area: 283.53 Pl/SQL procedure successfully completed.
The PL/SQL Literals
A literal is an explicit numeric, character, string, or Boolean value not represented by an identifier. For example, TRUE, 786, NULL, ‘tutorialspoint’ are all literals of type Boolean, number, or string. PL/SQL, literals are case-sensitive. PL/SQL supports the following kinds of literals −
- Numeric Literals
- Character Literals
- String Literals
- BOOLEAN Literals
- Date and Time Literals
The following table provides examples from all these categories of literal values.
S.No | Literal Type | Example |
---|---|---|
1 | Numeric Literals | 050 78 -14 0 +32767 6.6667 0.0 -12.0 3.14159 +7800.00 6E5 1.0E-8 3.14159e0 -1E38 -9.5e-3 |
2 | Character Literals | ‘A’ ‘%’ ‘9’ ‘ ‘ ‘z’ ‘(‘ |
3 | String Literals | ‘Hello, world!’ ‘Tutorials Point’ ’19-NOV-12′ |
4 | BOOLEAN Literals | TRUE, FALSE, and NULL. |
5 | Date and Time Literals | DATE ‘1978-12-25’; TIMESTAMP ‘2012-10-29 12:01:01’; |
To embed single quotes within a string literal, place two single quotes next to each other as shown in the following program −
DECLARE message varchar2(30):= 'That''s tutorialspoint.com!'; BEGIN dbms_output.put_line(message); END; /
When the above code is executed at the SQL prompt, it produces the following result −
That's tutorialspoint.com! PL/SQL procedure successfully completed.
PLSQL supports the conditional statements like other programming languages such as C++, java etc.
The conditional selection statements, IF and CASE, run different statements for different data values.
The IF statement either runs or skips a sequence of one or more statements, depending on a condition. The IF statement has these forms:
- IF THEN
- IF THEN ELSE
- IF THEN ELSIF
The CASE statement chooses from a sequence of conditions, and runs the corresponding statement. The CASE statement has these forms:
- Simple, which evaluates a single expression and compares it to several potential values.
- Searched, which evaluates multiple conditions and chooses the first one that is true.
IF THEN Statement
IF THEN statement associates a condition with a sequence of statements enclosed by the keywords THEN and END IF. If the condition is true, the statements get executed and if the condition is false or NULL then IF statement does nothing.
Syntax:
IF condition THEN statements END IF;
Example:
DECLARE a number(3) :=100; b number(3) :=150; BEGIN if a < b then dbms_output.put_line ('b is the highest number'); END IF; END; b is the highest number PL/SQL procedure successfully completed.
IF THEN ELSE
IF statement adds the keyword ELSE followed by an alternative sequence of statements. If the condition is false or NULL, then alternative sequenceof statements get executed. It ensure that either of sequence of statements are executed.
Syntax:
IF condition THEN statements ELSE else_statements END IF;
Example:
DECLARE a number(3) :=100; b number(3) :=150; BEGIN if a > b then dbms_output.put_line ('a is the highest number'); ELSE dbms_output.put_line ('b is the highest number'); END IF; END; b is the highest number PL/SQL procedure successfully completed.
IF THEN ELSIF
The IF THEN ELSIF statement runs the first statements for which condition is true. Remaining conditions are not evaluated. If no condition is true, the else_statements run, if they exist; otherwise, the IF THEN ELSIF statement does nothing.
Syntax:
IF condition_1 THEN statements_1 ELSIF condition_2 THEN statements_2 [ ELSIF condition_3 THEN statements_3 ]... [ ELSE else_statements ] END IF;
Example:
DECLARE grade CHAR(1); BEGIN grade := 'C'; IF grade = 'A' THEN DBMS_OUTPUT.PUT_LINE('Excellent'); ELSIF grade = 'B' THEN DBMS_OUTPUT.PUT_LINE('Very Good'); ELSIF grade = 'C' THEN DBMS_OUTPUT.PUT_LINE('Good'); ELSIF grade = 'D' THEN DBMS_OUTPUT. PUT_LINE('Fair'); ELSIF grade = 'F' THEN DBMS_OUTPUT.PUT_LINE('Poor'); ELSE DBMS_OUTPUT.PUT_LINE('No such grade'); END IF; END; / Good PL/SQL procedure successfully completed.
Simple CASE Statement
The simple CASE statement runs the first statements for which selector_value equals selector. Remaining conditions are not evaluated. If no selector_value equals selector, the CASE statement runs else_statements if they exist and raises the predefined exception CASE_NOT_FOUND otherwise.
Syntax:
CASE selector WHEN selector_value_1 THEN statements_1 WHEN selector_value_2 THEN statements_2 ... WHEN selector_value_n THEN statements_n [ ELSE else_statements ] END CASE;]
Example:
DECLARE grade CHAR(1); BEGIN grade := 'B'; CASE grade WHEN 'A' THEN DBMS_OUTPUT.PUT_LINE('Excellent'); WHEN 'B' THEN DBMS_OUTPUT.PUT_LINE('Very Good'); WHEN 'C' THEN DBMS_OUTPUT.PUT_LINE('Good'); WHEN 'D' THEN DBMS_OUTPUT.PUT_LINE('Fair'); WHEN 'F' THEN DBMS_OUTPUT.PUT_LINE('Poor'); ELSE DBMS_OUTPUT.PUT_LINE('No such grade'); END CASE; END; / Very Good PL/SQL procedure successfully completed.
Searched CASE Statement
The searched CASE statement runs the first statements for which condition is true. Remaining conditions are not evaluated. If no condition is true, the CASE statement runs else_statements if they exist and raises the predefined exception CASE_NOT_FOUND otherwise.
Syntax:
CASE WHEN condition_1 THEN statements_1 WHEN condition_2 THEN statements_2 ... WHEN condition_n THEN statements_n [ ELSE else_statements ] END CASE;]
Example:
DECLARE grade CHAR(1); BEGIN grade := 'C'; CASE WHEN grade = 'A' THEN DBMS_OUTPUT.PUT_LINE('Excellent'); WHEN grade = 'B' THEN DBMS_OUTPUT.PUT_LINE('Very Good'); WHEN grade = 'C' THEN DBMS_OUTPUT.PUT_LINE('Good'); WHEN grade = 'D' THEN DBMS_OUTPUT.PUT_LINE('Fair'); WHEN grade = 'F' THEN DBMS_OUTPUT.PUT_LINE('Poor'); ELSE DBMS_OUTPUT.PUT_LINE('No such grade'); END CASE; END; / Good PL/SQL procedure successfully completed.
PLSQL Operators
We will learn operators in PL/SQL in this lesson. An operator is a symbol that tells the compiler to perform specific mathematical or logical manipulation. PL/SQL language is rich in built-in operators and provides the following types of operators −
- Arithmetic operators
- Relational operators
- Comparison operators
- Logical operators
- String operators
Here, we will understand the arithmetic, relational, comparison and logical operators one by one. The String operators will be discussed in a later chapter − PL/SQL Strings.
Arithmetic Operators
Following table shows all the arithmetic operators supported by PL/SQL. Let us assume variable A holds 10 and variable B holds 5, then
Operator | Description | Example |
---|---|---|
+ | Adds two operands | A + B will give 15 |
– | Subtracts second operand from the first | A – B will give 5 |
* | Multiplies both operands | A * B will give 50 |
/ | Divides numerator by de-numerator | A / B will give 2 |
** | Exponentiation operator, raises one operand to the power of other | A ** B will give 100000 |
Relational Operators
Relational operators compare two expressions or values and return a Boolean result. Following table shows all the relational operators supported by PL/SQL. Let us assume variable A holds 10 and variable B holds 20, then −
Operator | Description | Example |
---|---|---|
= | Checks if the values of two operands are equal or not, if yes then condition becomes true. | (A = B) is not true. |
!= <> ~= | Checks if the values of two operands are equal or not, if values are not equal then condition becomes true. | (A != B) is true. |
> | Checks if the value of left operand is greater than the value of right operand, if yes then condition becomes true. | (A > B) is not true. |
< | Checks if the value of left operand is less than the value of right operand, if yes then condition becomes true. | (A < B) is true. |
>= | Checks if the value of left operand is greater than or equal to the value of right operand, if yes then condition becomes true. | (A >= B) is not true. |
<= | Checks if the value of left operand is less than or equal to the value of right operand, if yes then condition becomes true. | (A <= B) is true |
Comparison Operators
Comparison operators are used for comparing one expression to another. The result is always either TRUE, FALSE or NULL.
Operator | Description | Example |
---|---|---|
LIKE | The LIKE operator compares a character, string, or CLOB value to a pattern and returns TRUE if the value matches the pattern and FALSE if it does not. | If ‘Zara Ali’ like ‘Z% A_i’ returns a Boolean true, whereas, ‘Nuha Ali’ like ‘Z% A_i’ returns a Boolean false. |
BETWEEN | The BETWEEN operator tests whether a value lies in a specified range. x BETWEEN a AND b means that x >= a and x <= b. | If x = 10 then, x between 5 and 20 returns true, x between 5 and 10 returns true, but x between 11 and 20 returns false. |
IN | The IN operator tests set membership. x IN (set) means that x is equal to any member of set. | If x = ‘m’ then, x in (‘a’, ‘b’, ‘c’) returns Boolean false but x in (‘m’, ‘n’, ‘o’) returns Boolean true. |
IS NULL | The IS NULL operator returns the BOOLEAN value TRUE if its operand is NULL or FALSE if it is not NULL. Comparisons involving NULL values always yield NULL. | If x = ‘m’, then ‘x is null’ returns Boolean false. |
Logical Operators
In this table shows the Logical operators supported by PL/SQL. All these operators work on Boolean operands and produce Boolean results. Let us assume variable A> holds true and variable B holds false, then −
Operator | Description | Examples |
---|---|---|
and | Called the logical AND operator. If both the operands are true then condition becomes true. | (A and B) is false. |
or | Called the logical OR Operator. If any of the two operands is true then condition becomes true. | (A or B) is true. |
not | Called the logical NOT Operator. Used to reverse the logical state of its operand. If a condition is true then Logical NOT operator will make it false. | not (A and B) is true. |
PL/SQL Operator Precedence
Operator precedence determines the grouping of terms in an expression. This affects how an expression is evaluated. Certain operators have higher precedence than others; for example, the multiplication operator has higher precedence than the addition operator.
For example, x = 7 + 3 * 2; here, x is assigned 13, not 20 because operator * has higher precedence than +, so it first gets multiplied with 3*2 and then adds into 7.
Here, operators with the highest precedence appear at the top of the table, those with the lowest appear at the bottom. Within an expression, higher precedence operators will be evaluated first.
The precedence of operators goes as follows: =, <, >, <=, >=, <>, !=, ~=, ^=, IS NULL, LIKE, BETWEEN, IN.
Operator | Operation |
---|---|
** | exponentiation |
+, – | identity, negation |
*, / | multiplication, division |
+, -, || | addition, subtraction, concatenation |
NOT | logical negation |
AND | conjunction |
OR | inclusion |
A LOOP statement is an iterative control statement that allows you to executes a sequence of statements multiple times.
PLSQL provides these loop statements.
- Basic loop
- FOR loop
- WHILE loop
- Cursor FOR loop
Loop Control Statements
To prevent an infinite loop, at least one statement must transfer control outside the loop. The statements that can transfer control outside the loop are
- CONTINUE
- EXIT / EXIT WHEN
- GOTO
CONTINUE
The CONTINUE statement exits the current iteration of a loop, either conditionally or unconditionally, and transfers control to the next iteration of either the current loop or an enclosing labeled loop.
Restrictions on CONTINUE Statement
- A CONTINUE statement must be inside a LOOP statement.
- A CONTINUE statement cannot cross a subprogram or method boundary.
EXIT
The EXIT statement exits the current iteration of a loop, either conditionally or unconditionally, and transfers control to the end of either the current loop or an enclosing labeled loop.
Restriction on EXIT Statement
- An EXIT statement must be inside a LOOP statement.
GOTO
The GOTO statement transfers control to a labeled block or statement.
Restrictions on GOTO Statement
- A GOTO statement cannot transfer control into an IF statement, CASE statement, LOOP statement, or sub-block.
- A GOTO statement cannot transfer control from one IF statement clause to another, or from one CASE statement WHEN clause to another.
- A GOTO statement cannot transfer control out of a subprogram.
- A GOTO statement cannot transfer control into an exception handler.
- A GOTO statement cannot transfer control from an exception handler back into the current block.
Basic loop
With each iteration of the basic LOOP statement, its statements run and control returns to the top of the loop. The LOOP statement ends when a statement inside the loop transfers control outside the loop or raises an exception.
Syntax :
LOOP sequence_of_statements; END LOOP;
Basic loop statement with no EXIT keyword will be an INFINITE-LOOP that will never stop. To prevent this, the EXIT condition should be given inside this execution block so that control exit from the loop.
Syntax :
LOOP sequence_of_statements; EXIT; END LOOP;
LOOP sequence_of_statements; EXIT WHEN condition; END LOOP;
Example :
SET SERVEROUTPUT ON; DECLARE counter NUMBER := 0; BEGIN LOOP counter := counter + 1; DBMS_OUTPUT.PUT_LINE('Counter : ' || counter); IF counter = 5 THEN EXIT; END IF; END LOOP; END; /
Counter : 1 Counter : 2 Counter : 3 Counter : 4 Counter : 5 PL/SQL procedure successfully completed.
SET SERVEROUTPUT ON; DECLARE counter NUMBER := 0; BEGIN LOOP counter := counter + 1; DBMS_OUTPUT.PUT_LINE('Counter : ' || counter); EXIT WHEN counter=3; END LOOP; END; /
Counter : 1 Counter : 2 Counter : 3 PL/SQL procedure successfully completed.
FOR loop
PL/SQL FOR LOOP is an iterative statement that allows you to execute a sequence of statements a fixed number of times. The FOR LOOP statement ends when its index reaches a specified value, or when a statement inside the loop transfers control outside the loop or raises an exception.
Syntax :
FOR loop_counter IN [REVERSE] lower_bound .. higher_bound LOOP sequence_of_statements; END LOOP;
Example :
DECLARE n_counter NUMBER := 5; BEGIN FOR i IN 1 .. n_counter LOOP DBMS_OUTPUT.PUT_LINE('Iteration :' || i); END LOOP; END; /
Iteration :1 Iteration :2 Iteration :3 Iteration :4 Iteration :5 PL/SQL procedure successfully completed.
DECLARE n_counter NUMBER := 5; BEGIN FOR i IN 1 .. n_counter LOOP IF i=3 THEN CONTINUE; END IF; DBMS_OUTPUT.PUT_LINE('Iteration :' || i); END LOOP; END; /
Iteration :1 Iteration :2 Iteration :4 Iteration :5 PL/SQL procedure successfully completed.
In above example, CONTINUE statement skipped the iteration when the condition is true and transfer the control to the next iteration of the current loop.
CONTINUE WHEN statement :
DECLARE n_counter NUMBER := 5; BEGIN FOR i IN 1 .. n_counter LOOP CONTINUE WHEN i=3; DBMS_OUTPUT.PUT_LINE('Iteration :' || i); END LOOP; END; /
Reverse FOR LOOP Statements
DECLARE n_counter NUMBER := 5; BEGIN FOR i IN REVERSE 1 .. n_counter LOOP CONTINUE WHEN i=3; DBMS_OUTPUT.PUT_LINE('Iteration :' || i); END LOOP; END; /
Iteration :5 Iteration :4 Iteration :2 Iteration :1 PL/SQL procedure successfully completed.
FOR LOOP with GOTO Statement.
DECLARE n_counter NUMBER := 5; BEGIN FOR i IN 1 .. n_counter LOOP DBMS_OUTPUT.PUT_LINE('Iteration :' || i); IF i=3 THEN GOTO counter_reached; END IF; END LOOP; << counter_reached >> DBMS_OUTPUT.PUT_LINE('Counter reached to 3'); END; /
In above example, when the condition is true, GOTO statement transfer the control out side the loop to the label “counter_reached”.
GOTO Statement Goes to Labeled NULL Statement
DECLARE done BOOLEAN; BEGIN FOR i IN 1..10 LOOP IF done THEN GOTO end_loop; END IF; << end_loop >> NULL; END LOOP; END; /
GOTO Statement Cannot Transfer Control into IF Statement
DECLARE valid BOOLEAN := TRUE; BEGIN GOTO update_row; IF valid THEN << update_row >> NULL; END IF; END; /
Error report - ORA-06550: line 4, column 3: PLS-00375: illegal GOTO statement; this GOTO cannot branch to label 'UPDATE_ROW' ORA-06550: line 6, column 12: PL/SQL: Statement ignored 06550. 00000 - "line %s, column %s:\n%s" *Cause: Usually a PL/SQL compilation error. *Action:
NULL Statement
The NULL statement only passes control to the next statement. Some languages refer to such an instruction as a no-op (no operation).
WHILE loop
The WHILE LOOP statement runs one or more statements while a condition is TRUE. The WHILE LOOP statement ends when the condition becomes FALSE or NULL, or when a statement inside the loop transfers control outside the loop or raises an exception.
Syntax :
WHILE condition LOOP sequence_of_statements; END LOOP;
Example :
DECLARE n_counter NUMBER := 5; n_factorial NUMBER := 1; n_number NUMBER; BEGIN n_number := n_counter; WHILE n_counter > 0 LOOP n_factorial := n_factorial * n_counter; n_counter := n_counter - 1; END LOOP; DBMS_OUTPUT.PUT_LINE('factorial of ' || n_number || ' is ' || n_factorial); END; /
factorial of 5 is 120 PL/SQL procedure successfully completed.
We can include EXIT, EXIT WHEN, CONTINUE and GOTO statements in WHILE LOOP as well as per requirement.
Cursor FOR LOOP
The cursor FOR LOOP statement implicitly declares its loop index as a record variable of the row type that a specified cursor returns, and then opens a cursor.
With each iteration, the cursor FOR LOOP statement fetches a row from the result set into the record. When there are no more rows to fetch, the cursor FOR LOOP statement closes the cursor.
Examples :
Implicit Cursor FOR LOOP Statement
BEGIN FOR item IN ( SELECT last_name, job_id FROM employees WHERE job_id LIKE '%CLERK%' AND manager_id > 120 ORDER BY last_name ) LOOP DBMS_OUTPUT.PUT_LINE ('Name = ' || item.last_name || ', Job = ' || item.job_id); END LOOP; END; /
Explicit Cursor FOR LOOP Statement
DECLARE CURSOR c IS SELECT last_name, job_id FROM employees WHERE job_id LIKE '%CLERK%' AND manager_id > 120 ORDER BY last_name; BEGIN FOR item IN c LOOP DBMS_OUTPUT.PUT_LINE ('Name = ' || item.last_name || ', Job = ' || item.job_id); END LOOP; END; /
Passing Parameters to Explicit Cursor FOR LOOP Statement
DECLARE CURSOR c (job VARCHAR2, max_wage NUMBER) IS SELECT * FROM employees WHERE job_id = job AND salary > max_wage; BEGIN FOR person IN c('ST_CLERK', 3000) LOOP DBMS_OUTPUT.PUT_LINE ( 'Name = ' || person.last_name || ', salary = ' || person.salary || ', Job Id = ' || person.job_id ); END LOOP; END; /
We will see more about cursors in detail in next chapters.
Nested Loops
The loop statements can also be nested. The outer and inner loop can be of different types. In nested loop, for every one iteration value of the outer loop, the inner loop will be executed fully.
Syntax :
LOOP -- outer statements; LOOP -- inner statements; END LOOP; statements; END LOOP;
Example :
Labelling of Loops
In PL/SQL, the loops can be labeled. The label should be enclosed between “<<” and “>>”. The labeling of loops particularly in nested loop codes will give more readability.
The label can be given to GOTO statement to exit from that particular loop. Using label, the control can be made to directly exit the outer loop of the nested loops from anyplace inside the loops.
Syntax :
<< outer_loop >> LOOP statements; << inner_loop >> LOOP statements; END LOOP; statements; END LOOP';
A cursor is a pointer to a private SQL area that stores information about processing a specific SELECT or DML statement. A cursor holds the rows (one or more) returned by a SQL statement. The set of rows the cursor holds is referred to as the active set.
There are two types of cursors:
- Implicit cursors
- Explicit cursors
A cursor that is constructed and managed by PL/SQL is an implicit cursor. A cursor that you construct and manage is an explicit cursor.
Implicit Cursors
Implicit cursors are automatically created by Oracle whenever an SQL statement is executed. We cannot control an implicit cursor, but we can get information from its attributes.
The syntax of an implicit cursor attribute value is SQL%attribute (therefore, an implicit cursor is also called a SQL cursor), it always refers to the most recently run SELECT or DML statement. If no such statement has run, the value of SQL attribute is NULL.
An implicit cursor closes once its associated statement runs and its attribute values remain available until another SELECT or DML statement runs.
To save an attribute value for later use, assign it to a local variable immediately.
The implicit cursor attributes are:
SQL%ISOPEN : Is the Cursor Open ?
SQL%FOUND : Were Any Rows Affected ?
SQL%NOTFOUND : Were No Rows Affected ?
SQL%ROWCOUNT : How Many Rows Were Affected ?
SQL%BULK_ROWCOUNT : Getting Number of Rows Affected by FORALL Statement
SQL%BULK_EXCEPTIONS : Handling FORALL Exceptions After FORALL Statement Completes
Example :
DECLARE total_rows number(4); BEGIN UPDATE emp SET sal = sal + 500; IF sql%notfound THEN dbms_output.put_line('no employees sal updated'); ELSIF sql%found THEN total_rows := sql%rowcount; dbms_output.put_line( total_rows || 'employees sal updated '); END IF; END; /
You can get the number of rows that each DML statement affected from the implicit cursor attribute SQL%BULK_ROWCOUNT.
Showing Number of Rows Affected by Each DELETE in FORALL
DECLARE TYPE NumList IS TABLE OF NUMBER; depts NumList := NumList(30, 40, 50); BEGIN FORALL j IN depts.FIRST..depts.LAST DELETE FROM emp_temp WHERE department_id = depts(j); FOR i IN depts.FIRST..depts.LAST LOOP DBMS_OUTPUT.PUT_LINE ( 'Statement #' || i || ' deleted ' || SQL%BULK_ROWCOUNT(i) || ' rows.' ); END LOOP; DBMS_OUTPUT.PUT_LINE('Total rows deleted: ' || SQL%ROWCOUNT); END; / Statement #1 deleted 6 rows. Statement #2 deleted 45 rows. Statement #3 deleted 5 rows. Total rows deleted: 56
Explicit Cursors
An explicit cursor is a session cursor that you construct and manage. An explicit cursor should be defined in the declaration section of the PL/SQL Block. It is created on a SELECT Statement which returns more than one row. Then you can process the query result set in either of these ways:
- Open the explicit cursor with the OPEN statement, fetch rows from the result set with the FETCH statement, and close the explicit cursor with the CLOSE statement.
- Use the explicit cursor in a cursor FOR LOOP statement
Declaring the Cursor
Declaring the cursor defines the cursor with a name and the associated SELECT statement.
CURSOR cursor_name [ parameter_list ] [ RETURN return_type ] IS select_statement;
Opening the Cursor
After declaring and defining an explicit cursor, you can open it with the OPEN statement.
OPEN cursor_name;
Fetching Data with Explicit Cursors
After opening an explicit cursor, you can fetch the rows of the query result set with the FETCH statement.
FETCH cursor_name INTO into_clause;
Closing the Cursor
Closing the cursor means releasing the allocated memory.
CLOSE cursor_name;
Example :
DECLARE c_id customers.id%type; c_name customerS.No.ame%type; c_addr customers.address%type; CURSOR c_customers is SELECT id, name, address FROM customers; BEGIN OPEN c_customers; LOOP FETCH c_customers into c_id, c_name, c_addr; EXIT WHEN c_customers%notfound; dbms_output.put_line(c_id || ' ' || c_name || ' ' || c_addr); END LOOP; CLOSE c_customers; END; /
Fetching Same Explicit Cursor into Different Variables
DECLARE CURSOR c IS SELECT e.job_id, j.job_title FROM employees e, jobs j WHERE e.job_id = j.job_id AND e.manager_id = 100 ORDER BY last_name; -- Record variables for rows of cursor result set: job1 c%ROWTYPE; job2 c%ROWTYPE; job3 c%ROWTYPE; job4 c%ROWTYPE; job5 c%ROWTYPE; BEGIN OPEN c; FETCH c INTO job1; -- fetches first row FETCH c INTO job2; -- fetches second row FETCH c INTO job3; -- fetches third row FETCH c INTO job4; -- fetches fourth row FETCH c INTO job5; -- fetches fifth row CLOSE c; DBMS_OUTPUT.PUT_LINE(job1.job_title || ' (' || job1.job_id || ')'); DBMS_OUTPUT.PUT_LINE(job2.job_ title || ' (' || job2.job_id || ')'); DBMS_OUTPUT.PUT_LINE(job3.job_ title || ' (' || job3.job_id || ')'); DBMS_OUTPUT.PUT_LINE(job4.job_ title || ' (' || job4.job_id || ')'); DBMS_OUTPUT.PUT_LINE(job5.job_ title || ' (' || job5.job_id || ')'); END; /
Explicit Cursor FOR LOOP Statement
DECLARE CURSOR c1 IS SELECT last_name, job_id FROM employees WHERE job_id LIKE '%CLERK%' AND manager_id > 120 ORDER BY last_name; BEGIN FOR item IN c1 LOOP DBMS_OUTPUT.PUT_LINE ('Name = ' || item.last_name || ', Job = ' || item.job_id); END LOOP; END; /
Passing Parameters to Explicit Cursor FOR LOOP Statement
DECLARE CURSOR c1 (job VARCHAR2, max_wage NUMBER) IS SELECT * FROM employees WHERE job_id = job AND salary > max_wage; BEGIN FOR person IN c1('ST_CLERK', 3000) LOOP -- process data record DBMS_OUTPUT.PUT_LINE ( 'Name = ' || person.last_name || ', salary = ' || person.salary || ', Job Id = ' || person.job_id ); END LOOP; END; /
Subquery in FROM Clause of Parent Query
DECLARE CURSOR c1 IS SELECT t1.department_id, department_name, staff FROM departments t1, ( SELECT department_id, COUNT(*) AS staff FROM employees GROUP BY department_id ) t2 WHERE (t1.department_id = t2.department_id) AND staff >= 5 ORDER BY staff; BEGIN FOR dept IN c1 LOOP DBMS_OUTPUT.PUT_LINE ('Department = ' || dept.department_name || ', staff = ' || dept.staff); END LOOP; END; /
Under Progress………………….
Under Progress………………….
A procedure is a group of PL/SQL statements that you can call by name which performs one or more specific task.
A procedure has a header and a body.
- The header consists of the name of the procedure and the parameters or variables passed to the procedure.
- The body consists or declaration section, execution section and exception section similar to a general PL/SQL Block.
Syntax for creating procedure:
CREATE [OR REPLACE] PROCEDURE procedure_name [(parameter_name [IN | OUT | IN OUT] type [, ...])] {IS | AS} BEGIN < procedure_body > END procedure_name;
How to pass parameters in procedure:
We can pass parameters to procedures in three ways.
1) IN-parameters : The IN parameter can be referenced by the procedure or function. The value of the parameter cannot be overwritten by the procedure or the function.
2) OUT-parameters : The OUT parameter cannot be referenced by the procedure or function, but the value of the parameter can be overwritten by the procedure or function.
3) IN OUT-parameters : The INOUT parameter can be referenced by the procedure or function and the value of the parameter can be overwritten by the procedure or function.
- A procedure may or may not return any value.
- IS – marks the beginning of the body of the procedure and is similar to DECLARE in anonymous PL/SQL Blocks. The code between IS and BEGIN forms the Declaration section.
- The AS keyword is used instead of the IS keyword for creating a standalone procedure.
How to execute a Stored Procedure
There are two ways to execute a procedure.
1) From the SQL prompt.
EXECUTE [or EXEC] procedure_name;
2) Within another procedure – simply use the procedure name.
procedure_name;
PL/SQL Create Procedure
CREATE OR REPLACE PROCEDURE remove_cust (cust_id NUMBER) IS BEGIN DELETE FROM CUSTOMERS WHERE CUSTOMERS.id = remove_cust.cust_id; DBMS_OUTPUT.PUT_LINE('Customer deleted'); END; /
Procedure REMOVE_CUST compiled
SET SERVEROUTPUT ON BEGIN remove_cust(2); END; Customer deleted PL/SQL procedure successfully completed.
To view the Procedure availability
select object_name,object_type,status from USER_OBJECTS WHERE object_name='REMOVE_CUST';
Recompile an Existing Procedure.
ALTER PROCEDURE remove_cust COMPILE;
Dropping Procedure.
Similar to dropping a table, procedure can also be dropped.
DROP PROCEDURE remove_cust;
IN & OUT Mode Example 1
This program finds the minimum of two values. Here, the procedure takes two numbers using the IN mode and returns their minimum using the OUT parameters.
DECLARE a number; b number; c number; PROCEDURE findmin(x IN number, y IN number, z OUT number) IS BEGIN IF x < y THEN z:= x; ELSE z:= y; END IF; END; BEGIN a:= 25; b:= 40; findmin(a, b, c); dbms_output.put_line(' Minimum of (25, 40) : ' || c); END; / Minimum of (25, 40) : 25 PL/SQL procedure successfully completed.
IN & OUT Mode Example 2
This procedure computes the square of value of a passed value. This example shows how we can use the same parameter to accept a value and then return another result.
DECLARE a number; PROCEDURE square(x IN OUT number) IS BEGIN x := x * x; END; BEGIN a:= 25; square(a); dbms_output.put_line(' Square of (25): ' || a); END; / Square of (25): 625 PL/SQL procedure successfully completed.
Methods for Passing Parameters
Actual parameters can be passed in three ways
Positional notation
It is simple an association of the values by POSITION of the argumrnts at call time with that of declaration in the header of the procedure creation. The order of the parameters used when executing the procedure should match the order in the procedure header exactly.
Ex: findmin(a, b, c);
Named notation
It is an explicit association using the symbol =>
In Named notation, the order of the parameters is doesnot matter.
Ex: findmin(x => a, y => b, z => c);
Mixed notation
In mixed notation, you can mix both notations in procedure call; however, the positional notation should precede the named notation.
Ex: findmin(a, b, z => c); — is valid
Ex: findmin(X => a, b, c); — is not valid
A function is a set of PL/SQL statements you can call by name.
User defined functions can be used as part of a SQL expression.
A function has output that needs to be assigned to a variable or it can be used in a SELECT statement.
A function can not call when it has RETURN data type as Boolean. A Function can contain more than one return statement, each exception should have a RETURN statement.
Syntax :
CREATE [OR REPLACE] FUNCTION function_name [(parameter_name [IN | OUT | IN OUT] type [, ...])] RETURN return_datatype {IS | AS} BEGIN < function_body > END [function_name];
Create a Function
CREATE OR REPLACE FUNCTION totalCustomers RETURN number IS total number(2) := 0; BEGIN SELECT count(*) into total FROM customers; RETURN total; END; / Function TOTALCUSTOMERS compiled
Calling a Function
DECLARE t number(2); BEGIN t := totalCustomers(); dbms_output.put_line('Total no.of Customers : ' || t); END; / Total no.of Customers : 6 PL/SQL procedure successfully completed.
Example : 2
CREATE FUNCTION get_bal(acc_no IN NUMBER) RETURN NUMBER IS acc_bal NUMBER(11,2); BEGIN SELECT order_total INTO acc_bal FROM orders WHERE customer_id = acc_no; RETURN(acc_bal); END; / Function GET_BAL compiled
Calling function with select statement
SELECT get_bal(100) FROM DUAL; GET_BAL(100) ------------ 2500
Examples :3
CREARE OR REPLACE FUNCTION employcount ( dept_no emp.deptno%typ) RETURN NUMBER IS emp_count number(4); BEGIN SELECT count(empno) INTO emp_count FROM emp WHERE deptno=dept_no; RETURN emp_count; END; /
SELECT unique deptno,employcount(deptno) FROM emp where employcount(deptno) > 10;
Alter Function
Use the ALTER FUNCTION statement to recompile an invalid standalone stored function. Explicit recompilation eliminates the need for implicit run-time recompilation and prevents associated run-time compilation errors and performance overhead.
ALTER FUNCTION employcount COMPILE;
Drop Function
Use the DROP FUNCTION statement to remove a standalone stored function from the database.
DROP FUNCTION employcount;
A package is a schema object that groups logically related PLSQL types, variables, and subprograms. Package is compiled and stored in database that can be used later.
PLSQL package basically have two components.
- Package Specification
- Package Body
Package Specification :
Specification is the interface to the package. It consists of a declaration of all the variables, constants, cursors, types, procedures, functions, and exceptions that can be referenced from outside the package.
The elements which are all declared declared in the specification are called public elements. Any subprogram not in the package specification but coded in the package body is called a private element.
Syntax :
CREATE [OR REPLACE] PACKAGE < package name > IS < sub programs and element declaration> END < package name >;
Example :
CREATE PACKAGE emp_sal AS PROCEDURE find_sal(e_id emp_id%type); END emp_sal; /
Package Body
The body holds implementation details and private declarations, which are hidden from code outside the package (can be called only from inside the package).
It should contain implementation for all the subprograms/cursors that have been declared in the specification.
It can also have more subprograms or other elements that are not declared in specification. These are called private elements.
It is a dependable object, and it depends on package specification.
The state of the package body becomes ‘Invalid’ every time when the specification is compiled. Therefore, it needs to be recompiled each time after the compilation of specification.
Syntax :
CREATE [OR REPLACE] PACKAGE BODY < package name > IS < global_declaration part > < private element definition > < sub programs and element declaration> < Package Initialization > END < package name >;
Example :
CREATE OR REPLACE PACKAGE BODY emp_sal AS PROCEDURE find_sal(e_id emp_id%TYPE) IS e_sal emp.salary%TYPE; BEGIN SELECT salary INTO e_sal FROM customers WHERE id = e_id; dbms_output.put_line('Salary: '|| e_sal); END find_sal; END emp_sal; /
Referencing Package Contents
To reference the types, items, subprograms, and call specs declared within a package spec, use dot notation:
package_name.type_name package_name.item_name package_name.subprogram_name
Example :
DECLARE id emp_id%type := &e_id; BEGIN emp_sal.find_sal(id); END; /
Creating the emp_admin Package
-- create the audit table to track changes CREATE TABLE emp_audit(date_of_action DATE, user_id VARCHAR2(20), package_name VARCHAR2(30)); CREATE OR REPLACE PACKAGE emp_admin AS -- Declare externally visible types, cursor, exception TYPE EmpRecTyp IS RECORD (emp_id NUMBER, sal NUMBER); CURSOR desc_salary RETURN EmpRecTyp; invalid_salary EXCEPTION; -- Declare externally callable subprograms FUNCTION hire_employee (last_name VARCHAR2, first_name VARCHAR2, email VARCHAR2, phone_number VARCHAR2, job_id VARCHAR2, salary NUMBER, commission_pct NUMBER, manager_id NUMBER, department_id NUMBER) RETURN NUMBER; PROCEDURE fire_employee (emp_id NUMBER); -- overloaded subprogram PROCEDURE fire_employee (emp_email VARCHAR2); -- overloaded subprogram PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER); FUNCTION nth_highest_salary (n NUMBER) RETURN EmpRecTyp; END emp_admin; / CREATE OR REPLACE PACKAGE BODY emp_admin AS number_hired NUMBER; -- visible only in this package -- Fully define cursor specified in package CURSOR desc_salary RETURN EmpRecTyp IS SELECT employee_id, salary FROM employees ORDER BY salary DESC; -- Fully define subprograms specified in package FUNCTION hire_employee (last_name VARCHAR2, first_name VARCHAR2, email VARCHAR2, phone_number VARCHAR2, job_id VARCHAR2, salary NUMBER, commission_pct NUMBER, manager_id NUMBER, department_id NUMBER) RETURN NUMBER IS new_emp_id NUMBER; BEGIN SELECT employees_seq.NEXTVAL INTO new_emp_id FROM dual; INSERT INTO employees VALUES (new_emp_id, last_name, first_name, email, phone_number, SYSDATE, job_id, salary, commission_pct, manager_id, department_id); number_hired := number_hired + 1; DBMS_OUTPUT.PUT_LINE('The number of employees hired is ' || TO_CHAR(number_hired) ); RETURN new_emp_id; END hire_employee; PROCEDURE fire_employee (emp_id NUMBER) IS BEGIN DELETE FROM employees WHERE employee_id = emp_id; END fire_employee; PROCEDURE fire_employee (emp_email VARCHAR2) IS BEGIN DELETE FROM employees WHERE email = emp_email; END fire_employee; -- Define local function, available only inside package FUNCTION sal_ok (jobid VARCHAR2, sal NUMBER) RETURN BOOLEAN IS min_sal NUMBER; max_sal NUMBER; BEGIN SELECT MIN(salary), MAX(salary) INTO min_sal, max_sal FROM employees WHERE job_id = jobid; RETURN (sal >= min_sal) AND (sal < = max_sal); END sal_ok; PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER) IS sal NUMBER(8,2); jobid VARCHAR2(10); BEGIN SELECT job_id, salary INTO jobid, sal FROM employees WHERE employee_id = emp_id; IF sal_ok(jobid, sal + amount) THEN UPDATE employees SET salary = salary + amount WHERE employee_id = emp_id; ELSE RAISE invalid_salary; END IF; EXCEPTION -- exception-handling part starts here WHEN invalid_salary THEN DBMS_OUTPUT.PUT_LINE('The salary is out of the specified range.'); END raise_salary; FUNCTION nth_highest_salary (n NUMBER) RETURN EmpRecTyp IS emp_rec EmpRecTyp; BEGIN OPEN desc_salary; FOR i IN 1..n LOOP FETCH desc_salary INTO emp_rec; END LOOP; CLOSE desc_salary; RETURN emp_rec; END nth_highest_salary; BEGIN -- initialization part starts here INSERT INTO emp_audit VALUES (SYSDATE, USER, 'EMP_ADMIN'); number_hired := 0; END emp_admin; / -- calling the package procedures DECLARE new_emp_id NUMBER(6); BEGIN new_emp_id := emp_admin.hire_employee('Belden', 'Enrique', 'EBELDEN', '555.111.2222', 'ST_CLERK', 2500, .1, 101, 110); DBMS_OUTPUT.PUT_LINE('The new employee id is ' || TO_CHAR(new_emp_id) ); EMP_ADMIN.raise_salary(new_ emp_id, 100); DBMS_OUTPUT.PUT_LINE('The 10th highest salary is '|| TO_CHAR(emp_admin.nth_highest_ salary(10).sal) || ', belonging to employee: ' || TO_CHAR(emp_admin.nth_highest_ salary(10).emp_id) ); emp_admin.fire_employee(new_ emp_id); -- you could also delete the newly added employee as follows: -- emp_admin.fire_employee(' EBELDEN'); END; /
A PL/SQL record is a composite data structure that is a group of related data stored in fields. Each field in the PL/SQL record has its own name and data type.
A record is a group of related data items stored in fields, each with its own name and datatype. Records are composed of a group of fields, similar to the columns in a row. The %ROWTYPE attribute lets you declare a PL/SQL record that represents a row in a database table, without listing all the columns.
PL/SQL can handle the following types of records
- Table-based
- Cursor-based records
- User-defined records
Table Based Records
The %ROWTYPE attribute enables a programmer to create table-based and cursorbased records.
Syntax :
DECLARE table_based_record table_name%ROWTYPE;
Example :
DECLARE r_emp employees%ROWTYPE; n_emp_id employees.employee_id%TYPE := 200; BEGIN SELECT * INTO r_emp FROM employees WHERE employee_id = n_emp_id; dbms_output.put_line('ID: ' || r_emp.id); dbms_output.put_line('Name: ' || r_emp.name); dbms_output.put_line('Salary: ' || r_emp.salary); END; /
Cursor-Based Records
The following example illustrates the concept of cursor-based records.
DECLARE CURSOR customer_cur is SELECT id, name, address FROM customers; customer_rec customer_cur%rowtype; BEGIN OPEN customer_cur; LOOP FETCH customer_cur into customer_rec; EXIT WHEN customer_cur%notfound; DBMS_OUTPUT.put_line(customer_rec.id || ' ' || customer_rec.name); END LOOP; END; /
User-Defined Records
PL/SQL provides a user-defined record type that allows you to define the different record structures. These records consist of different fields.
Defining a Record
The record type is defined as
TYPE type_name IS RECORD ( field_name1 datatype1 [NOT NULL] [:= DEFAULT EXPRESSION], field_name2 datatype2 [NOT NULL] [:= DEFAULT EXPRESSION], ... field_nameN datatypeN [NOT NULL] [:= DEFAULT EXPRESSION); record-name type_name;
Example :
DECLARE TYPE books IS RECORD (title varchar(50), author varchar(50), subject varchar(100), book_id number); book1 books; book2 books;
Accessing Fields
To access any field of a record, we use the dot (.) operator.
Example :
DECLARE type books is record (title varchar(50), author varchar(50), subject varchar(100), book_id number); book1 books; book2 books; BEGIN -- Book 1 specification book1.title := 'C Programming'; book1.author := 'Nuha Ali '; book1.subject := 'C Programming Tutorial'; book1.book_id := 6495407; -- Book 2 specification book2.title := 'Telecom Billing'; book2.author := 'Zara Ali'; book2.subject := 'Telecom Billing Tutorial'; book2.book_id := 6495700; -- Print book 1 record dbms_output.put_line('Book 1 title : '|| book1.title); dbms_output.put_line('Book 1 author : '|| book1.author); dbms_output.put_line('Book 1 subject : '|| book1.subject); dbms_output.put_line('Book 1 book_id : ' || book1.book_id); -- Print book 2 record dbms_output.put_line('Book 2 title : '|| book2.title); dbms_output.put_line('Book 2 author : '|| book2.author); dbms_output.put_line('Book 2 subject : '|| book2.subject); dbms_output.put_line('Book 2 book_id : '|| book2.book_id); END; /
Records as Subprogram Parameters
You can pass a record as a subprogram parameter just as you pass any other variable. You can also access the record fields in the same way as you accessed.
Example :
DECLARE type books is record (title varchar(50), author varchar(50), subject varchar(100), book_id number); book1 books; book2 books; PROCEDURE printbook (book books) IS BEGIN dbms_output.put_line ('Book title : ' || book.title); dbms_output.put_line('Book author : ' || book.author); dbms_output.put_line( 'Book subject : ' || book.subject); dbms_output.put_line( 'Book book_id : ' || book.book_id); END; BEGIN -- Book 1 specification book1.title := 'C Programming'; book1.author := 'Nuha Ali '; book1.subject := 'C Programming Tutorial'; book1.book_id := 6495407; -- Book 2 specification book2.title := 'Telecom Billing'; book2.author := 'Zara Ali'; book2.subject := 'Telecom Billing Tutorial'; book2.book_id := 6495700; -- Use procedure to print book info printbook(book1); printbook(book2); END; /
A collection is an ordered group of elements, all of the same data type and are called elements. Each element is addressed by a unique subscript that represents its position in the collection. You access each element by its unique subscript.
PL/SQL has three collection types:
- Associative array (or index-by table)
- Nested table
- Variable-size array (varray)
Oracle documentation provides the following characteristics for each type of collections
Collection Type | Number of Elements | Subscript Type | Dense or Sparse | Where Created | Can Be Object Type Attribute |
---|---|---|---|---|---|
Associative array (or index-by table) | Unbounded | String or integer | Either | Only in PL/SQL block | No |
Nested table | Unbounded | Integer | Starts dense, can become sparse | Either in PL/SQL block or at schema level | Yes |
Variable-size array (varray) | Bounded | Integer | Always dense | Either in PL/SQL block or at schema level | Yes |
Unbounded : there is no limit to the number of elements in the collection.
Dense : means that the collection has no gaps between elements – every element between the first and last element is defined and has a value (which can be NULL).
We have already discussed varray in the chapter ‘PLSQL Arrays’.
Associative Arrays (Index-By Tables):
An associative array (also called an index-by table) is a set of key-value pairs. Each key is unique, and is used to locate the corresponding value. The key can be either an integer or a string.
Syntax:
TYPE type_name IS TABLE OF element_type [NOT NULL] INDEX BY subscript_type; table_name type_name;
Example :
DECLARE -- Associative array indexed by string TYPE balance IS TABLE OF NUMBER INDEX BY VARCHAR2(20); -- Associative array variable bal_list balance; name VARCHAR2(10); BEGIN -- adding elements to the table bal_list('AAA') := 2000; bal_list('BBB') := 5000; bal_list('CCC') := 10000; bal_list('DDD') := 8000; -- printing the table name := bal_list.FIRST; WHILE name IS NOT null LOOP dbms_output.put_line ('Balance of ' || name || ' is ' || TO_CHAR(bal_list(name))); name := bal_list.NEXT(name); END LOOP; END; /
Balance of AAA is 2000 Balance of BBB is 5000 Balance of CCC is 10000 Balance of DDD is 8000 PL/SQL procedure successfully completed.
Example 2:
DECLARE CURSOR c_cust is select name from customers; TYPE c_list IS TABLE of customers.Name%type INDEX BY binary_integer; name_list c_list; counter integer :=0; BEGIN FOR n IN c_cust LOOP counter := counter +1; name_list(counter) := n.name; dbms_output.put_line('Customer('||counter||'):'|| name_list(counter)); END LOOP; END; /
Customer(1):AAA Customer(2):BBB Customer(3):CCC Customer(4):DDD Customer(5):EEE Customer(6):FFF PL/SQL procedure successfully completed.
Nested Table Collections
A nested table is like a one-dimensional array with an arbitrary number of elements.
A nested table differs from an array in these important ways:
- An array has a declared number of elements, but a nested table does not. The size of a nested table can increase dynamically.
- An array is always dense (that is, it always has consecutive subscripts). A nested array is dense initially, but it can become sparse, because you can delete elements from it.
Syntax:
TYPE type_name IS TABLE OF element_type [NOT NULL]; table_name type_name;
This declaration is similar to the declaration of an index-by table, but there is no INDEX BY clause.
Example:
DECLARE TYPE names_table IS TABLE OF VARCHAR2(10); TYPE grades IS TABLE OF INTEGER; names names_table; marks grades; total integer; BEGIN names := names_table('AAA', 'BBB', 'CCC', 'DDD'); marks:= grades(98, 97, 78, 87); total := names.count; dbms_output.put_line('Total '|| total || ' Students'); FOR i IN 1 .. total LOOP dbms_output.put_line('Student:'||names(i)||', Marks:' || marks(i)); end loop; END; /
Total 4 Students Student:AAA, Marks:98 Student:BBB, Marks:97 Student:CCC, Marks:78 Student:DDD, Marks:87 PL/SQL procedure successfully completed.
Collection Methods
A collection method is a built-in PL/SQL subprogram that returns information about a collection or operates on a collection. Collection methods make collections easier to use, and make your applications easier to maintain.
The following table lists the methods and their purpose
Method | Purpose |
---|---|
EXISTS(n) | Returns TRUE if the nth element in a collection exists; otherwise returns FALSE. |
COUNT | Returns the number of elements that a collection currently contains. |
LIMIT | Checks the maximum size of a collection. |
FIRST | Returns the first (smallest) index numbers in a collection that uses the integer subscripts. |
LAST | Returns the last (largest) index numbers in a collection that uses the integer subscripts. |
PRIOR(n) | Returns the index number that precedes index n in a collection. |
NEXT(n) | Returns the index number that succeeds index n. |
EXTEND | Appends one null element to a collection. |
EXTEND(n) | Appends n null elements to a collection. |
EXTEND(n,i) | Appends n copies of the ith element to a collection. |
TRIM | Removes one element from the end of a collection. |
TRIM(n) | Removes n elements from the end of a collection. |
DELETE | Removes all elements from a collection, setting COUNT to 0. |
DELETE(n) | Removes the nth element from an associative array with a numeric key or a nested table. If the associative array has a string key, the element corresponding to the key value is deleted. If n is null, DELETE(n) does nothing. |
DELETE(m,n) | Removes all elements in the range m..n from an associative array or nested table. If m is larger than n or if m or n is null, DELETE(m,n) does nothing. |
PLSQL Objects
We will learn Object-Oriented in PL/SQL. PL/SQL allows defining an object type, which helps in designing object-oriented database in Oracle. An object type allows you to create composite types. Using objects allow you to implement real world objects with specific structure of data and methods for operating it. Objects have attributes and methods. Attributes are properties of an object and are used for storing an object’s state; and methods are used for modeling its behavior.
Objects are created using the CREATE [OR REPLACE] TYPE statement. Following is an example to create a simple address object consisting of few attributes
CREATE OR REPLACE TYPE address AS OBJECT (house_no varchar2(10), street varchar2(30), city varchar2(20), state varchar2(10), pincode varchar2(10) ); /
Type created.
Instantiating an Object
Defining an object type provides a blueprint for the object. To use this object, you need to create instances of this object. You can access the attributes and methods of the object using the instance name and the access operator (.) as follows
DECLARE residence address; BEGIN residence := address('205', 'Haul Road', 'MN', 'Minnesota','524307'); dbms_output.put_line('House No: '|| residence.house_no); dbms_output.put_line('Street: '|| residence.street); dbms_output.put_line('City: '|| residence.city); dbms_output.put_line('State: '|| residence.state); dbms_output.put_line('Pincode: '|| residence.pincode); END; /
House No: 205 Street: Haul Road City: MN State: Minnesota Pincode: 524307 PL/SQL procedure successfully completed.
Member Methods
Member methods are used for manipulating the attributes of the object. You provide the declaration of a member method while declaring the object type. The object body defines the code for the member methods. The object body is created using the CREATE TYPE BODY statement.
Constructors are functions that return a new object as its value. Every object has a system defined constructor method. The name of the constructor is same as the object type. For example
residence := address('205', 'Haul Road', 'MN', 'Minnesota','524307');
The comparison methods are used for comparing objects. There are two ways to compare objects
Map method
The Map method is a function implemented in such a way that its value depends upon the value of the attributes. For example, for a customer object, if the customer code is same for two customers, both customers could be the same. So the relationship between these two objects would depend upon the value of code.
Order method
The Order method implements some internal logic for comparing two objects. For example, for a rectangle object, a rectangle is bigger than another rectangle if both its sides are bigger.
Using Map method
Let us try to understand the above concepts using the following rectangle object
CREATE OR REPLACE TYPE rectangle AS OBJECT (length number, width number, member function enlarge( inc number) return rectangle, member procedure display, map member function measure return number ); /
Type created.
Creating the type body
CREATE OR REPLACE TYPE BODY rectangle AS MEMBER FUNCTION enlarge(inc number) return rectangle IS BEGIN return rectangle(self.length + inc, self.width + inc); END enlarge; MEMBER PROCEDURE display IS BEGIN dbms_output.put_line('Length: '|| length); dbms_output.put_line('Width: '|| width); END display; MAP MEMBER FUNCTION measure return number IS BEGIN return (sqrt(length*length + width*width)); END measure; END; /
Type body created.
Now using the rectangle object and its member functions
DECLARE r1 rectangle; r2 rectangle; r3 rectangle; inc_factor number := 5; BEGIN r1 := rectangle(3, 4); r2 := rectangle(5, 7); r3 := r1.enlarge(inc_factor); r3.display; IF (r1 > r2) THEN -- calling measure function r1.display; ELSE r2.display; END IF; END; /
Length: 8 Width: 9 Length: 5 Width: 7 PL/SQL procedure successfully completed.
Using Order method
Now, the same effect could be achieved using an order method. Let us recreate the rectangle object using an order method
CREATE OR REPLACE TYPE rectangle AS OBJECT (length number, width number, member procedure display, order member function measure(r rectangle) return number ); /
Type created.
Creating the type body
CREATE OR REPLACE TYPE BODY rectangle AS MEMBER PROCEDURE display IS BEGIN dbms_output.put_line('Length: '|| length); dbms_output.put_line('Width: '|| width); END display; ORDER MEMBER FUNCTION measure(r rectangle) return number IS BEGIN IF(sqrt(self.length*self.length + self.width*self.width)> sqrt(r.length*r.length + r.width*r.width)) then return(1); ELSE return(-1); END IF; END measure; END; /
Type body created.
Using the rectangle object and its member functions
DECLARE r1 rectangle; r2 rectangle; BEGIN r1 := rectangle(23, 44); r2 := rectangle(15, 17); r1.display; r2.display; IF (r1 > r2) THEN -- calling measure function r1.display; ELSE r2.display; END IF; END; /
Length: 23 Width: 44 Length: 15 Width: 17 Length: 23 Width: 44 PL/SQL procedure successfully completed.
Inheritance for PL/SQL Objects
PL/SQL allows creating object from the existing base objects. To implement inheritance, the base objects should be declared as NOT FINAL. The default is FINAL.
The following programs illustrate the inheritance in PL/SQL Objects. Let us create another object named TableTop, this is inherited from the Rectangle object. For this, we need to create the base rectangle object
CREATE OR REPLACE TYPE rectangle AS OBJECT (length number, width number, member function enlarge( inc number) return rectangle, NOT FINAL member procedure display) NOT FINAL /
Type created.
Creating the base type body
CREATE OR REPLACE TYPE BODY rectangle AS MEMBER FUNCTION enlarge(inc number) return rectangle IS BEGIN return rectangle(self.length + inc, self.width + inc); END enlarge; MEMBER PROCEDURE display IS BEGIN dbms_output.put_line('Length: '|| length); dbms_output.put_line('Width: '|| width); END display; END; /
Type body created.
Creating the child object tabletop
CREATE OR REPLACE TYPE tabletop UNDER rectangle ( material varchar2(20), OVERRIDING member procedure display ) /
Type created.
Creating the type body for the child object tabletop
CREATE OR REPLACE TYPE BODY tabletop AS OVERRIDING MEMBER PROCEDURE display IS BEGIN dbms_output.put_line('Length: '|| length); dbms_output.put_line('Width: '|| width); dbms_output.put_line('Material: '|| material); END display; /
Type body created.
Using the tabletop object and its member functions
DECLARE t1 tabletop; t2 tabletop; BEGIN t1:= tabletop(20, 10, 'Wood'); t2 := tabletop(50, 30, 'Steel'); t1.display; t2.display; END; /
Length: 20 Width: 10 Material: Wood Length: 50 Width: 30 Material: Steel PL/SQL procedure successfully completed.
Abstract Objects in PL/SQL
The NOT INSTANTIABLE clause allows you to declare an abstract object. You cannot use an abstract object as it is; you will have to create a subtype or child type of such objects to use its functionalities.
For example
CREATE OR REPLACE TYPE rectangle AS OBJECT (length number, width number, NOT INSTANTIABLE NOT FINAL MEMBER PROCEDURE display) NOT INSTANTIABLE NOT FINAL /
Type created.
Under Progress………………….
An exception is a PLSQL error that is raised during program execution. PLSQL supports programmers to catch errors using Exception block in the program and appropriate action is taken against the error condition.
Exception types
There are three types of exceptions:
- Predefined exceptions are error conditions that are defined by PL/SQL.
- Non-predefined exceptions include any standard TimesTen errors.
- User-defined exceptions are exceptions specific to your application.
Syntax for Exception Handling.
DECLARE < declarations section> BEGIN < executable command(s)> EXCEPTION < exception handling goes here > WHEN exception1 THEN exception1-handling-statements WHEN exception2 THEN exception2-handling-statements WHEN exception3 THEN exception3-handling-statements ........ WHEN others THEN others-handling-statements END;
Example 1 :
DECLARE n_empno emp.empno%type := &empno; v_ename emp.ename%type; v_job emp.job%type; BEGIN SELECT ename,job into v_ename,v_job from emp where empno=n_empno; DBMS_OUTPUT.PUT_LINE('The emp details are' || v_ename || v_job); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('The empno is not available'); END; /
Example 2 :
DECLARE n_accno accounts.accno%type:=&accno; v_name accounts.name%type:='&name'; v_bal accounts.bal%type:=&bal; BEGIN INSERT INTO accounts(accno,name,bal) VALUES(v_accno,v_name,v_bal); DBMS_OUTPUT.PUT_LINE('Account details inserted successfully'); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN DBMS_OUTPUT.PUT_LINE('Account aleady exists'); END; /
Example 3 :
DECLARE n_empno emp.empno%type; v_ename emp.ename%type; n_deptno emp.deptno%type; BEGIN SELECT empno,ename,deptno INTO n_empno,v_ename,n_deptno FROM emp WHERE empno=115 and ename='John'; DBMS_OUTPUT.PUT_LINE(v_name || 'Works in ' || n_deptno || 'department'); SELECT empno,ename,deptno INTO n_empno,v_ename,n_deptno FROM emp WHERE deptno=20; DBMS_OUTPUT.PUT_LINE('Employee no is' || n_empno); DBMS_OUTPUT.PUT_LINE('Employee name is' || v_ename); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('No data found'); WHEN TOO_MANY_ROWS THEN DBMS_OUTPUT.PUT_LINE('More than one employee works in dept no 20'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('An error occured while processing the program'); END; /
Predefined Exceptions :
Predefined exceptions are internally defined exceptions that have predefined names, which PL/SQL declares globally in the package STANDARD. The runtime system raises predefined exceptions implicitly (automatically). Because predefined exceptions have names, you can write exception handlers specifically for them.
lists the names and error codes of the predefined exceptions:
Exception Name | Error Code |
---|---|
ACCESS_INTO_NULL | -6530 |
CASE_NOT_FOUND | -6592 |
COLLECTION_IS_NULL | -6531 |
CURSOR_ALREADY_OPEN | -6511 |
DUP_VAL_ON_INDEX | -1 |
INVALID_CURSOR | -1001 |
INVALID_NUMBER | -1722 |
LOGIN_DENIED | -1017 |
NO_DATA_FOUND | +100 |
NO_DATA_NEEDED | -6548 |
NOT_LOGGED_ON | -1012 |
PROGRAM_ERROR | -6501 |
ROWTYPE_MISMATCH | -6504 |
SELF_IS_NULL | -30625 |
STORAGE_ERROR | -6500 |
SUBSCRIPT_BEYOND_COUNT | -6533 |
SUBSCRIPT_OUTSIDE_LIMIT | -6532 |
SYS_INVALID_ROWID | -1410 |
TIMEOUT_ON_RESOURCE | -51 |
TOO_MANY_ROWS | -1422 |
VALUE_ERROR | -6502 |
ZERO_DIVIDE | -1476 |
Example 4 :
DECLARE stock_price NUMBER := 9.73; net_earnings NUMBER := 0; pe_ratio NUMBER; BEGIN pe_ratio := stock_price / net_earnings; -- raises ZERO_DIVIDE exception DBMS_OUTPUT.PUT_LINE('Price/earnings ratio = ' || pe_ratio); EXCEPTION WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT_LINE('Company had zero earnings.'); pe_ratio := NULL; END; /
User-Defined Exceptions
A user defined exception is an error that is defined by the program. The developer to handle the business situations define use-defined exceptions during the execution of the PL/SQL block.
An exception name declaration has this syntax:
exception_name EXCEPTION;
Steps
- Declare exception
- Raise in executable section explicitly using RAISE < exception_name >;
- Handle the raised exception.
Raising Exceptions
Syntax :
DECLARE exception_name EXCEPTION; BEGIN IF condition THEN RAISE exception_name; END IF; EXCEPTION WHEN exception_name THEN statement; END; /
Example 5 :
DECLARE c_id customers.id%type := &cc_id; c_name customers.name%type; c_addr customers.address%type; -- user defined exception ex_invalid_id EXCEPTION; BEGIN IF c_id < = 0 THEN RAISE ex_invalid_id; ELSE SELECT name, address INTO c_name, c_addr FROM customers WHERE id = c_id; DBMS_OUTPUT.PUT_LINE ('Name: '|| c_name); DBMS_OUTPUT.PUT_LINE ('Address: ' || c_addr); END IF; EXCEPTION WHEN ex_invalid_id THEN dbms_output.put_line('ID must be greater than zero!'); WHEN no_data_found THEN dbms_output.put_line('No such customer!'); WHEN others THEN dbms_output.put_line('Error!'); END; /
RAISE_APPLICATION_ERROR
This built-in procedure is used to create your own error message, which can be more descriptive than named Exceptions.
It is used to communicate a predefined exception interactively by returning a non standard error code and error message.
Using this procedure we can report error to application and avoid returning unhandled exception.
Syntax :
RAISE_APPLICATION_ERROR (error_number,'error_message');
Note :
- Error number must exists between -20000 and -20999
- Error_message is the text associate with this error, and keep_errors is Boolean value.
- The error_message parameter must less than 512 characters.
Example 6 :
DECLARE i emp%rowtype; BEGIN i.empno:= &eno; SELECT ename,sal INTO i.ename,i.sal FROM emp WHERE empno=i.empno; IF i.sal < 2000 THEN RAISE_APPLICATION_ERROR(-20345,'The emp sal is less than 2000'); ELSE i.sal := i.sal+i.sal*0.35; UPDATE emp set sal=i.sal WHERE empno=i.empno; dbms_output.put_line('Salary updated to' || i.sal); END IF; END; /
Non-Predefined exceptions
We can associate an exception with an error code. The EXCEPTION_INIT pragma associates a user-defined exception name with an error code. The EXCEPTION_INIT pragma can appear only in the same declarative part as its associated exception, anywhere after the exception declaration.
Syntax :
PRAGMA EXCEPTION_INIT (exception_name, error_code);
DECLARE exception_name EXCEPTION; PRAGMA EXCEPTION_INIT(exception_name, -60); BEGIN ... EXCEPTION WHEN exception_name THEN ... END; /
Example 7 :
CREATE PROCEDURE account_status ( due_date DATE, today DATE ) AUTHID DEFINER IS BEGIN IF due_date < today THEN -- explicitly raise exception RAISE_APPLICATION_ERROR(-20000, 'Account past due.'); END IF; END; / DECLARE past_due EXCEPTION; -- declare exception PRAGMA EXCEPTION_INIT (past_due, -20000); -- assign error code to exception BEGIN account_status ('1-JUL-10', '9-JUL-10'); -- invoke procedure EXCEPTION WHEN past_due THEN -- handle exception DBMS_OUTPUT.PUT_LINE(TO_CHAR( SQLERRM(-20000))); END; /
SQLCODE Function
In an exception handler, the SQLCODE function returns the numeric code of the exception being handled. (Outside an exception handler, SQLCODE returns 0.)
For an internally defined exception, the numeric code is the number of the associated Oracle Database error. This number is negative except for the error “no data found”, whose numeric code is +100.
For a user-defined exception, the numeric code is either +1 (default) or the error code associated with the exception by the EXCEPTION_INIT pragma.
A SQL statement cannot invoke SQLCODE.
SQLERRM
The SQLERRM function returns the error message associated with an error code.
Like SQLCODE, SQLERRM without error_code is useful only in an exception handler. Outside an exception handler, or if the value of error_code is zero, SQLERRM returns ORA-0000.
If the value of error_code is +100, SQLERRM returns ORA-01403.
If the value of error_code is a positive number other than +100, SQLERRM returns this message:
-error_code: non-ORACLE exception
Example :
BEGIN DBMS_OUTPUT.PUT_LINE ('SQLERRM(-6511): ' || TO_CHAR(SQLERRM(-6511))); END; / SQLERRM(-6511): ORA-06511: PL/SQL: cursor already open
Example 8:
CREATE TABLE errors ( code NUMBER, message VARCHAR2(64) ); CREATE OR REPLACE PROCEDURE p AUTHID DEFINER AS name EMPLOYEES.LAST_NAME%TYPE; v_code NUMBER; v_errm VARCHAR2(64); BEGIN SELECT last_name INTO name FROM EMPLOYEES WHERE EMPLOYEE_ID = -1; EXCEPTION WHEN OTHERS THEN v_code := SQLCODE; v_errm := SUBSTR(SQLERRM, 1, 64); DBMS_OUTPUT.PUT_LINE ('Error code ' || v_code || ': ' || v_errm); /* Invoke another procedure, declared with PRAGMA AUTONOMOUS_TRANSACTION, to insert information about errors. */ INSERT INTO errors (code, message) VALUES (v_code, v_errm); RAISE; END; / Error code 100: ORA-01403: no data found
A trigger is a named PL/SQL unit that is stored in the database and can be invoked repeatedly when some events occur.A trigger is a named PL/SQL unit that is stored in the database and can be invoked repeatedly when some events occur.
You can enable and disable a trigger, but you cannot explicitly invoke it. While a trigger is enabled, the database automatically invokes it that is, the trigger fires whenever its triggering event occurs. While a trigger is disabled, it does not fire.
You can write triggers that fire whenever one of the following operations occurs.
- DML statements (INSERT, UPDATE, DELETE)
- DDL statements (CREATE, ALTER or DROP)
- Database events, such as logon/logoff, errors, or startup/shutdown
Use of Triggers:
- Automatically generate derived column values
- Provide auditing
- Prevent invalid transactions
- Gather statistics on table access
- Modify table data when DML statements are issued against views
- Enforce complex security authorizations
- Enforce referential integrity
- Enforce complex business rules
- Provide transparent event logging
- Publish information about database events, user events, and SQL statements to subscribing applications
Types of Triggers
- Row Triggers and Statement Triggers
- BEFORE and AFTER Triggers
- INSTEAD OF Triggers
- Triggers on System Events and User Events
Row Triggers:
A row trigger is fired each time the table is affected by the triggering statement.
For example,
If an UPDATE statement updates multiple rows of a table, then row trigger is fired for each row affected by the UPDATE statement.If a triggering statement affects zero rows, then trigger won’t run.
Statement Triggers:
A statement trigger is fired only once when the table is affected by the triggering statement, regardless of the number of rows affected the triggering statement. Even if zero rows are affected, trigger will run.
BEFORE Triggers:
BEFORE triggers run the trigger action before the triggering statement is run.
AFTER Triggers:
AFTER triggers run the trigger action after the triggering statement is run.
INSTEAD OF Triggers:
INSTEAD OF triggers provide a transparent way of modifying views that cannot be modified directly through DML statements.
These triggers are called INSTEAD OF triggers because, unlike other types of triggers, Oracle fires the trigger instead of executing the triggering statement.
You can write normal INSERT, UPDATE, and DELETE statements against the view and the INSTEAD OF trigger is fired to update the underlying tables appropriately. INSTEAD OF triggers are activated for each row of the view that gets modified.
Triggers on System Events and User Events
You can use triggers to publish information about database events to subscribers. Applications can subscribe to database events just as they subscribe to messages from other applications. These database events can include:
System events:
- Database startup and shutdown
- Data Guard role transitions
- Server error message events
- User events
- User logon and logoff
Triggers on system events can be defined at the database level or schema level. The DBMS_AQ package is one example of using database triggers to perform certain actions. For example, a database shutdown trigger is defined at the database level:
CREATE TRIGGER register_shutdown ON DATABASE SHUTDOWN BEGIN ... DBMS_AQ.ENQUEUE(...); ... END;
Syntax of Triggers
CREATE [OR REPLACE ] TRIGGER trigger_name {BEFORE | AFTER | INSTEAD OF } {INSERT [OR] | UPDATE [OR] | DELETE} [OF col_name] ON table_name [REFERENCING OLD AS o NEW AS n] [FOR EACH ROW] WHEN (condition) BEGIN --- sql statements END;
Example 1 :
BEFORE UPDATE FOR EACH ROW
1) Create the ‘product’ and ‘product_price_history’ table
CREATE TABLE product (id number(5), name varchar2(32), supplier varchar2(32), price number(7,2) );
CREATE TABLE product_price_history (product_id number(5), product_name varchar2(32), supplier_name varchar2(32), unit_price number(7,2)
);
Table PRODUCT created. Table PRODUCT_PRICE_HISTORY created.
2) Create the price_history_trigger and execute it.
CREATE or REPLACE TRIGGER price_history_trigger BEFORE UPDATE OF price ON product FOR EACH ROW BEGIN INSERT INTO product_price_history VALUES( :old.id, :old.name, :old.supplier, :old.price
); END;
/
3) Lets update the price of a product.
UPDATE PRODUCT SET price = 500 WHERE id = 101;
Once the above update query is executed, the trigger fires and updates the ‘product_price_history’ table.
4)If you ROLLBACK the transaction before committing to the database, the data inserted to the table is also rolled back.
Example 2:
AFTER UPDATE FOR EACH ROW
1. Create Emp_log table.
CREATE TABLE Emp_log (Emp_id NUMBER, Log_date DATE, New_salary NUMBER, Action VARCHAR2(20) );
2. Create the log_salary_increase trigger and execute it.
CREATE OR REPLACE TRIGGER log_salary_increase AFTER UPDATE OF salary ON employees FOR EACH ROW BEGIN INSERT INTO Emp_log (Emp_id, Log_date, New_salary, Action) VALUES (:NEW.employee_id, SYSDATE, :NEW.salary, 'New Salary'); END; /
3. Update salary of an employee
UPDATE employeesSET salary = salary + 1000.0WHERE Department_id = 20;
4. Check Emg_log data.
SELECT * FROM Emp_log; EMP_ID LOG_DATE NEW_SALARY ACTION ---------- --------- ---------- -------------------- 101 22-SEP-16 10049.50 New Salary 102 22-SEP-16 6900.00 New Salary 2 rows selected.
Example 3:
Conditional Trigger Prints Salary Change Information
CREATE OR REPLACE TRIGGER print_salary_changes BEFORE DELETE OR INSERT OR UPDATE ON employees FOR EACH ROW WHEN (NEW.job_id <> 'AD_PRES') -- do not print information about President DECLARE sal_diff NUMBER; BEGIN sal_diff := :NEW.salary - :OLD.salary; DBMS_OUTPUT.PUT(:NEW.last_name || ': '); DBMS_OUTPUT.PUT('Old salary = ' || :OLD.salary || ', '); DBMS_OUTPUT.PUT('New salary = ' || :NEW.salary || ', '); DBMS_OUTPUT.PUT_LINE('Difference: ' || sal_diff); END; /
UPDATE employeesSET salary = salary * 1.05 WHERE department_id IN (10, 20, 90);
Whalen: Old salary = 2800, New salary = 2940, Difference: 140 Hartstein: Old salary = 13000, New salary = 13650, Difference: 650 Fay: Old salary = 6000, New salary = 6300, Difference: 300 Kochhar: Old salary = 17000, New salary = 17850, Difference: 850 De Haan: Old salary = 17000, New salary = 17850, Difference: 850 6 rows updated.
Example 4:
CREATE TABLE product_check (Message varchar2(50), Current_Date number(32));
BEFORE UPDATE, Statement Level:
This trigger will insert a record into the table ‘product_check’ before a sql update statement is executed, at the statement level.
CREATE or REPLACE TRIGGER Before_Update_Stat_product BEFORE UPDATE ON product_check Begin INSERT INTO product_check Values('Before update, statement level',sysdate); END; /
Example 5:
BEFORE UPDATE, Row Level:
This trigger will insert a record into the table ‘product_check’ before each row is updated.
CREATE or REPLACE TRIGGER Before_Upddate_Row_product BEFORE UPDATE ON product_check FOR EACH ROW BEGIN INSERT INTO product_check Values('Before update row level',sysdate); END; /
Example 6:
AFTER UPDATE, Statement Level:
This trigger will insert a record into the table ‘product_check’ after a sql update statement is executed, at the statement level.
CREATE or REPLACE TRIGGER After_Update_Stat_product AFTER UPDATE ON product_check BEGIN INSERT INTO product_check Values('After update, statement level', sysdate); End; /
Example 7:
AFTER UPDATE, Row Level:
This trigger will insert a record into the table ‘product_check’ after each row is updated.
CREATE or REPLACE TRIGGER After_Update_Row_product AFTER insert On product_check FOR EACH ROW BEGIN INSERT INTO product_check Values('After update, Row level',sysdate); END; /
Now lets execute a update statement on table product.
UPDATE PRODUCT SET unit_price = 800 WHERE product_id in (100,101);
Lets check the data in ‘product_check’ table to see the order in which the trigger is fired.
SELECT * FROM product_check; Mesage Current_Date --------------------------------- ------------- Before update, statement level 22-SEP-2016 Before update, row level 22-SEP-2016 After update, Row level 22-SEP-2016 Before update, row level 22-SEP-2016 After update, Row level 22-SEP-2016 After update, statement level 22-SEP-2016
Example 7:
Trigger with REFERENCING Clause
creates a table with the same name as a correlation name, new, and then creates a trigger on that table. To avoid conflict between the table name and the correlation name, the trigger references the correlation name as Newest.
CREATE TABLE new (field1 NUMBER, field2 VARCHAR2(20)); CREATE OR REPLACE TRIGGER Print_salary_changes BEFORE UPDATE ON new REFERENCING new AS Newest FOR EACH ROW BEGIN :Newest.Field2 := TO_CHAR (:newest.field1); END; /
How To know Information about Triggers.
We can use the data dictionary view ‘USER_TRIGGERS’ to obtain information about any trigger.
This view stores information about header and body of the trigger.
SELECT * FROM user_triggers WHERE trigger_name = 'Before_Update_Stat_product';
The above sql query provides the header and body of the trigger ‘Before_Update_Stat_product’.
You can drop a trigger using the following command.
DROP TRIGGER trigger_name;
Under Progress………………….
Under Progress………………….
Under Progress………………….