PLSQL
PLSQL
Features of PL/SQL
The PL/SQL programming language was developed by Oracle
Corporation in the late 1980s as procedural extension
language for SQL and the Oracle relational database
Ex:
sales number(10, 2);
pi CONSTANT double precision := 3.1415;
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';
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 := 95;
num2 number := 85;
BEGIN
dbms_output.put_line('Outer Variable num1: ' || num1);
dbms_output.put_line('Outer Variable num2: ' || num2);
DECLARE
-- Local variables
num1 number := 195;
num2 number := 185;
BEGIN
dbms_output.put_line('Inner Variable num1: ' || num1);
dbms_output.put_line('Inner Variable num2: ' || num2);
END;
END;
/
Assigning SQL Query Results to PL/SQL
Variables
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;
/
PL/SQL Literals
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
Character Literals
'A' '%' '9' ' ' 'z' '('
String Literals
'Hello, world!'
'Tutorials Point'
'19-NOV-12'
BOOLEAN Literals
TRUE, FALSE, and NULL.
Date and Time Literals
DATE '1978-12-25';
TIMESTAMP '2012-10-29 12:01:01';
PL/SQL - Operators
Types of operators −
Arithmetic operators : +, -, *, /, **
Relational operators : =, !=, <>,~=, >, <, >=, <=
Comparison operators : LIKE, BETWEEN, IN, IS NULL
Logical operators : and, or, not
The precedence of operators goes as follows: =, <, >, <=, >=, <>, !=, ~=, ^=,
IS NULL, LIKE, BETWEEN, IN.
** exponentiation
+, - identity, negation
*, / multiplication, division
+, -, || addition, subtraction, concatenation
Comparison
NOT logical negation
AND conjunction
OR inclusion
PL/SQL - Conditions
Syntax
Ex:
DECLARE
x number := 10;
BEGIN
LOOP
dbms_output.put_line(x);
x := x + 10;
IF x > 50 THEN
exit;
END IF;
END LOOP; -- after exit, control resumes here
dbms_output.put_line('After Exit x is: ' || x);
END;
/
Ex:
DECLARE
a number(2);
BEGIN
FOR a in 10 .. 20 LOOP
dbms_output.put_line('value of a: ' || a);
END LOOP;
END;
/
Reverse FOR LOOP Statement
DECLARE
a number(2) ;
BEGIN
FOR a IN REVERSE 10 .. 20 LOOP
dbms_output.put_line('value of a: ' || a);
END LOOP;
END; /
PL/SQL - Nested Loops
Syntax
LOOP
Sequence of statements1
LOOP
Sequence of statements2
END LOOP;
END LOOP;
END;
/
DECLARE
CURSOR c_revenue (in_year NUMBER :=2017 , in_customer_id NUMBER := 1)
IS
SELECT SUM(quantity * unit_price) revenue
FROM order_items
INNER JOIN orders USING (order_id)
WHERE status = 'Shipped' AND EXTRACT( YEAR FROM order_date) = in_year
GROUP BY customer_id
HAVING customer_id = in_customer_id;
r_revenue c_revenue%rowtype;
BEGIN
OPEN c_revenue;
LOOP
FETCH c_revenue INTO r_revenue;
EXIT WHEN c_revenue%notfound;
-- show the revenue
dbms_output.put_line(r_revenue.revenue);
END LOOP;
CLOSE c_revenue;
END;
CURSOR FOR UPDATE
Syntax
CURSOR cursor_name IS
SELECT select_clause
FROM from_clause
WHERE where_clause
FOR UPDATE;
CURSOR cursor_name IS
SELECT select_clause
FROM from_clause
WHERE where_clause
FOR UPDATE OF column_name;
DECLARE
-- customer cursor
CURSOR c_customers IS
SELECT
customer_id,
name,
credit_limit
FROM
customers
WHERE
credit_limit > 0
FOR UPDATE OF credit_limit;
-- local variables
l_order_count PLS_INTEGER := 0;
l_increment PLS_INTEGER := 0;
CURSOR FOR UPDATE…
BEGIN
FOR r_customer IN c_customers
LOOP
-- get the number of orders of the customer
SELECT COUNT(*)
INTO l_order_count
FROM orders
WHERE customer_id = r_customer.customer_id;
--
IF l_order_count >= 5 THEN
l_increment := 5;
ELSIF l_order_count < 5 AND l_order_count >=2 THEN
l_increment := 2;
ELSIF l_increment = 1 THEN
l_increment := 1;
ELSE
l_increment := 0;
END IF;
IF l_increment > 0 THEN
-- update the credit limit
UPDATE
customers
SET
credit_limit = credit_limit * ( 1 + l_increment/ 100)
WHERE
customer_id = r_customer.customer_id;
-- show the customers whose credits are increased
dbms_output.put_line('Increase credit for customer '
|| r_customer.NAME || ' by '
|| l_increment || '%' );
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Error code:' || SQLCODE);
dbms_output.put_line('Error message:' || sqlerrm);
RAISE;
END;
/
PL/SQL Exception
Syntax:
BEGIN
-- executable section
...
-- exception-handling section
EXCEPTION
WHEN e1 THEN
-- exception_handler1
WHEN e2 THEN
-- exception_handler1
WHEN OTHERS THEN
-- other_exception_handler
END;
PL/SQL Exception
DECLARE
l_name customers.NAME%TYPE;
l_customer_id customers.customer_id%TYPE := &customer_id;
BEGIN
-- get the customer name by id
SELECT name INTO l_name
FROM customers
WHERE customer_id = l_customer_id;
END;
/
PL/SQL Exception…
DECLARE
l_name customers.NAME%TYPE;
l_customer_id customers.customer_id%TYPE := &customer_id;
BEGIN
-- get the customer
SELECT NAME INTO l_name
FROM customers
WHERE customer_id = l_customer_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('Customer ' || l_customer_id || ' does not exist');
WHEN TOO_MANY_ROWS THEN
dbms_output.put_line('The database returns more than one customer');
END;
/
PL/SQL Procedure
PL/SQL procedure syntax
A PL/SQL procedure is a reusable unit that encapsulates
specific business logic of the application. Technically
speaking, a PL/SQL procedure is a named block stored as a
schema object in the Oracle Database.
CREATE [OR REPLACE ] PROCEDURE procedure_name (pa
rameter_list)
IS
[declaration statements]
BEGIN
[execution statements]
EXCEPTION
[exception handler]
END [procedure_name ];
PL/SQL Procedure…
How to pass parameters to Procedures and Functions in
PL/SQL?
1) IN type parameter: These types of parameters are
used to send values to stored procedures.
2) OUT type parameter: These types of parameters are
used to get values from stored procedures. This is similar
to a return type in functions.
3) IN OUT parameter: These types of parameters are
used to send values and get values from stored
procedures.
NOTE: If a parameter is not explicitly defined a
parameter type, then by default it is an IN type
parameter.
PL/SQL Procedure…
Let’s create a procedure which gets the name of the
employee when the employee id is passed.
CREATE OR REPLACE PROCEDURE emp_name (id IN
NUMBER, emp_name OUT NUMBER)
IS
BEGIN
SELECT first_name INTO emp_name
FROM emp_tbl WHERE empID = id;
END;
/
PL/SQL Procedure…
We can call the procedure ‘emp_name’ in this way from a
PL/SQL Block.
DECLARE
empName varchar(20);
CURSOR id_cur SELECT id FROM emp_ids;
BEGIN
FOR emp_rec in id_cur
LOOP
emp_name(emp_rec.id, empName);
dbms_output.putline('The employee ' || empName || ' has id
' || emp-rec.id);
END LOOP;
END;
/
PL/SQL Procedure…
Using IN OUT parameter in procedures:
CREATE OR REPLACE PROCEDURE emp_salary_increase
(emp_id IN emptbl.empID%type, salary_inc IN OUT
emptbl.salary%type)
IS
tmp_sal number;
BEGIN
SELECT salary INTO tmp_sal FROM emp_tblWHERE empID = emp_id;
IF tmp_sal between 10000 and 20000 THEN 11> salary_inout := tmp_sal *
1.2;
ELSIF tmp_sal between 20000 and 30000 THEN
salary_inout := tmp_sal * 1.3;
ELSIF tmp_sal > 30000 THEN
salary_inout := tmp_sal * 1.4;
END IF;
END;
/
PL/SQL Procedure…
The below PL/SQL block shows how to execute the above
'emp_salary_increase' procedure.
DECLARE
CURSOR updated_sal is
SELECT empID,salary FROM emp_tbl;
pre_sal number;
BEGIN
FOR emp_rec IN updated_sal LOOP
pre_sal := emp_rec.salary;
emp_salary_increase(emp_rec.empID, emp_rec.salary);
dbms_output.put_line('The salary of ' || emp_rec.empID || '
increased from '|| pre_sal || ' to '||emp_rec.salary);
END LOOP;
END;
/
PL/SQL Function
Similar to a procedure, a PL/SQL function is a reusable program unit stored as a schema object in the Oracle Database.
CREATE [OR REPLACE] FUNCTION function_name (parameter_list)
RETURN return_type
IS
[declarative section]
BEGIN
[executable section]
[EXCEPTION]
[exception-handling section]
END;
You can embed functions within a Stored procedures cannot be embedded within
SELECT statement. a SELECT statement.
TEMP tables can’t be used in Both TEMP tables and Table variables can be
functions. used in stored procedures.
PL/SQL Cursor Variables with REF
CURSOR
A cursor variable is a variable that references to a cursor.
Different from implicit and explicit cursors, a cursor
variable is not tied to any specific query. Meaning that a
cursor variable can be opened for any query.
The most important benefit of a cursor variable is that it
enables passing the result of a query between PL/SQL
programs. Without a cursor variable, you have to fetch all
data from a cursor, store it in a variable e.g., a collection,
and pass this variable as an argument. With a cursor
variable, you simply pass the reference to that cursor.
To declare a cursor variable, you use the REF CURSOR is
the data type. PL/SQL has two forms of REF
CURSOR types: strong typed and weak typed REF
CURSOR.
PL/SQL Cursor Variables with REF
CURSOR
Example of a strong REF CURSOR.
DECLARE
TYPE customer_t IS REF CURSOR RETURN customers%ROWT
YPE;
c_customer customer_t;
Example of a weak typed REF CURSOR declaration that is not
associated with any specific structure:
DECLARE
TYPE customer_t IS REF CURSOR;
c_customer customer_t;
Starting from Oracle 9i, you can use SYS_REFCURSOR, which is a
predefined weak typed REF CURSOR, to declare a weak REF CURS
OR
DECLARE
c_customer SYS_REFCURSOR;
PL/SQL Cursor Variables with REF
CURSOR
CREATE OR REPLACE FUNCTION get_direct_reports(
in_manager_id IN employees.manager_id%TYPE)
RETURN SYS_REFCURSOR
AS
c_direct_reports SYS_REFCURSOR;
BEGIN
RETURN c_direct_reports;
END;
PL/SQL Cursor Variables with REF
CURSOR
anonymous block calls the get_direct_reports() function and processes the cursor variable to display the direct reports of the manager with id of
46.
DECLARE
c_direct_reports SYS_REFCURSOR;
l_employee_id employees.employee_id%TYPE;
l_first_name employees.first_name%TYPE;
l_last_name employees.last_name%TYPE;
l_email employees.email%TYPE;
BEGIN
-- get the ref cursor from function
c_direct_reports := get_direct_reports(46);
sql_11g> begin
2 without_serially_reusable_pkg.v_without_sr := 100;
3 with_serially_reusable_pkg.v_with_sr := 100;
4 end;
5 /
PL/SQL procedure successfully completed.
with_serially_reusable_pkg.v_with_sr is showing 0 because the package is marked as serially_reusable which resets the packaged variable global values to default
immediately after its call.
Now, clubbing assignment of variables & displaying the values of variables in single plsql block.
sql_11g> begin
2 without_serially_reusable_pkg.v_without_sr := 100;
3 with_serially_reusable_pkg.v_with_sr := 100;
4 dbms_output.put_line ('without_serially_reusable_pkg.v_without_sr value is -> ' || without_serially_reusable_pkg.v_without_sr );
5 dbms_output.put_line ('with_serially_reusable_pkg.v_with_sr values is ->' || with_serially_reusable_pkg.v_with_sr );
6 end;
7 /
COMMIT;
END;/
Raising an internally defined
exception
Typically, the runtime system raises internally defined exceptions i
mplicitly when they occur. Besides, you can explicitly raise an inter
nally defined exception with the RAISE statement if the exception
has a name:
DECLARE
l_customer_id customers.customer_id%TYPE := &customer_id;
BEGIN
-- get the meax credit limit
IF l_customer_id < 0 THEN
RAISE invalid_number;
END IF;
END;
/
Reraising the current exception
DECLARE
e_credit_too_high EXCEPTION;
PRAGMA exception_init( e_credit_too_high, -20001 );
l_max_credit customers.credit_limit%TYPE;
l_customer_id customers.customer_id%TYPE := &customer_id;
l_credit customers.credit_limit%TYPE := &credit_limit;
BEGIN
BEGIN
-- get the max credit limit
SELECT MAX(credit_limit)
INTO l_max_credit
FROM customers;
COMMIT;
END;
/
PL/SQL Exception Propagation
When an exception occurs, PL/SQL looks for an exception
handler in the current block e.g., anonymous
block, procedure, or function of the exception. If it does
not find a match, PL/SQL propagates the exception to the
enclosing block of the current block.
PL/SQL then attempts to handle the exception by raising it
once more in the enclosing block. This process continues
in each successive enclosing block until there is no
remaining block in which to raise the exception. If there is
no exception handler in all blocks, PL/SQL returns an
unhandled exception to the application environment that
executed the outermost PL/SQL block.
Note that an unhandled exception stops the execution of
the block.
PL/SQL Exception Propagation…
DECLARE
e1 EXCEPTION;
PRAGMA exception_init (e1, -20001);
e2 EXCEPTION;
PRAGMA exception_init (e2, -20002);
e3 EXCEPTION;
PRAGMA exception_init (e2, -20003);
l_input NUMBER := &input_number;
BEGIN
-- inner block
BEGIN
IF l_input = 1 THEN
raise_application_error(-20001,'Exception: the input number is 1');
ELSIF l_input = 2 THEN
raise_application_error(-20002,'Exception: the input number is 2');
ELSE
raise_application_error(-20003,'Exception: the input number is not 1 or 2');
END IF;
-- exception handling of the inner block
EXCEPTION
WHEN e1 THEN
dbms_output.put_line('Handle exception when the input number is 1');
END;
-- exception handling of the outer block
EXCEPTION
WHEN e2 THEN
dbms_output.put_line('Handle exception when the input number is 2');
END;
/
Pre-defined Exceptions
Differentiate between Syntax and
runtime errors
A syntax error can be easily detected by a PL/SQL
compiler. For eg, incorrect spelling.
A runtime error is handled with the help of exception-
handling section in an PL/SQL block. For eg, SELECT
INTO statement, which does not return any rows.
SQLCODE and SQLERRM
SQLCODE returns the value of the number of error for
the last encountered error whereas SQLERRM returns
the message for the last error.
PL/SQL Packages
PL/SQL Packages is schema object and collection of related data type (variables,
constants), cursors, procedures, functions are defining within a single context.
Package are divide into two part,
Package Specification
Package Body
Package specification block you can define variables, constants, exceptions and
package body you can create procedure, function, subprogram.