You are on page 1of 54

PL/SQL Examples

PL/SQL useful Examples

 
What is PL/SQL?

Procedural Language extension to SQL


It integrates procedural constructs with SQL

SET SERVEROUTPUT ON
This command is used to send the output from the server to the screen

Lesson 1-2 Introduction & Declaring PL/SQL Identifiers

SQL> SET SERVEROUTPUT ON

Program1: Printing a String

BEGIN
dbms_output.put_line ('Welcome to PL/SQL');
END;
/

Output:
Welcome to PL/SQL

Program2:
Declaring the variable & Initializing the variable in the declare section

DECLARE
v_name VARCHAR2 (10) := 'Star';
BEGIN
dbms_output.put_line (v_name);
END;
/

Output:
Star

Program3:
Declaring the variable in declare section & initializing the variable in the executable section

DECLARE
v_name VARCHAR2 (10);
BEGIN
dbms_output.put_line ('Name is ' || v_name);
v_name := 'Star';
dbms_output.put_line ('Name is ' || v_name);
END;
/

Output:
Name is
Name is Star

Program4: Modifying the variable value in the Executable Section

DECLARE
v_name VARCHAR2 (10) := 'Star';
BEGIN
dbms_output.put_line ('Previous Value ' || v_name);
v_name := 'PLSQL Star';
dbms_output.put_line ('Modified to '|| v_name);
END;
/

Output:
Previous Value Star
Modified to PLSQL Star

Program5: Defining a Variable with Not Null

DECLARE
v_no NUMBER (4) NOT NULL :=10;
BEGIN
dbms_output.put_line (v_no);
END;
/

Output:
10

Program6: Defining a Variable with a Constant Value

DECLARE
v_pi CONSTANT NUMBER (5, 2) := 3.14;
BEGIN
dbms_output.put_line (v_pi);
END;
/

Output:
3.14

Program7: Defining a Variable with DEFAULT

DECLARE
v_no NUMBER (5) default 10;
BEGIN
dbms_output.put_line (v_no);
END;
/

Output:
10

Program8: Writing a PL/SQL Statement (INTO Clause)

DECLARE
v_sal NUMBER (5);
BEGIN
SELECT salary
INTO v_sal
FROM employees
WHERE employee_id=101;
dbms_output.put_line (v_sal);
END;
/

Output:
17000

Program9: Importance of %type with Scalar Variable (Variable Size is less)

DECLARE
v_sal NUMBER (2);
BEGIN
SELECT salary
INTO v_sal
FROM employees
WHERE employee_id=101;
dbms_output.put_line (v_sal);
END;
/

Output:
DECLARE
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: number precision too large
ORA-06512: at line 4

Program10: Usage of %type with Scalar Variable

DECLARE
v_sal employees.salary%type;
BEGIN
SELECT salary INTO v_sal
FROM employees
WHERE employee_id=101;
dbms_output.put_line (v_sal);
END;
/

Output:
17000

Program11: Assigning a Previously defined variable data type to a new variable by using %TYPE

DECLARE
v_name VARCHAR2 (10) := 'star';
v_job v_name%type := 'clerk';
BEGIN
dbms_output.put_line (v_name);
dbms_output.put_line (v_job);
END;
/

Output:
star
clerk

Program12: Bind Variable

VARIABLE g_sal NUMBER

BEGIN
SELECT salary
INTO :g_sal
FROM employees
WHERE employee_id=101;
END;
/

PRINT g_sal

Output:
     G_SAL
----------
     17000

SET AUTOPRINT ON

BEGIN
SELECT salary
INTO :g_sal
FROM employees
WHERE employee_id=101;
END;
/
Output:
     G_SAL
----------
     17000

Program13: Usage of Substitution Variable (&) with Scalar Variable

DECLARE
v_sal employees.salary%type;
BEGIN
SELECT salary
INTO v_sal
FROM employees
WHERE employee_id=&NO;
dbms_output.put_line (v_sal);
END;
/

Output:
Enter value for no: 100
old   7: WHERE employee_id=&NO;
new   7: WHERE employee_id=100;
24000

Program14: Usage of Substitution Variable (&&) with Scalar Variable

DECLARE
v_sal employees.salary%type;
BEGIN
SELECT salary
INTO v_sal
FROM employees
WHERE employee_id=&&NO2;
dbms_output.put_line (v_sal);
END;
/

Output:
Enter value for no2: 102
old   7: WHERE employee_id=&&NO2;
new   7: WHERE employee_id=102;
17000

Program15: DEFINE Variable

DEFINE no3=110

DECLARE
v_sal employees.salary%type;
BEGIN
SELECT salary INTO v_sal
FROM employees
WHERE employee_id=&no3;
dbms_output.put_line (v_sal);
END;
/

Output:
old   6: WHERE employee_id=&no3;
new   6: WHERE employee_id=110;
8200

Lesson 3-4: Writing Executable Statements & Interacting with the Oracle Server

Program1: Usage of a Single Row Function (LOWER) with a Scalar Variable

DECLARE
v_in_name VARCHAR2 (20):= 'STAR';
v_out_name VARCHAR2 (20);
BEGIN
v_out_name:= LOWER (v_in_name);
dbms_output.put_line (v_out_name);
END;
/

Output:
star

Program2: Usage of a Single Row Function (LENGTH) with a Scalar Variable

DECLARE
v_in_name VARCHAR2 (20) := 'STAR';
BEGIN
v_in_name := LENGTH(v_in_name);
dbms_output.put_line(v_in_name);
END;
/

Output:
4

Program3: Usage of a Single Row Function (TO_CHAR) with a Scalar Variable

 DECLARE
v_date DATE := sysdate;
v_out VARCHAR2 (50);
BEGIN
v_out := TO_CHAR(sysdate,'dd-mon-year');
dbms_output.put_line (v_out);
END;
/

Output:
27-jan-twenty eleven

Or

SQL> begin
2 dbms_output.put_line(to_char(sysdate,'dd-mon-yy'));
3 end;
4 /
18-may-16

PL/SQL procedure successfully completed.

Or

SQL> declare
2 vout varchar2(20);
3 begin
4 vout:=to_char(sysdate,'dd-mon-yy');
5 dbms_output.put_line(vout);
6 end;
7 /
18-may-16

PL/SQL procedure successfully completed.

Program4: NESTED BLOCK

DECLARE
outer_block VARCHAR2 (30) := 'Global_Variable';
BEGIN
        DECLARE
        inner_block VARCHAR2 (30) := 'Inner_Variable';
        BEGIN
        dbms_output.put_line (outer_block);
        dbms_output.put_line (inner_block);
        END;
dbms_output.put_line (outer_block);
END;
/

Output:
Global_Variable
Inner_Variable
Global_Variable

Program5: NESTED BLOCK with Label

<<OUTER>>
DECLARE
outer_block VARCHAR2 (30) := 'Global_Variable';
BEGIN
         DECLARE
         inner_block VARCHAR2 (30) := 'Inner_Variable';
         outer_block VARCHAR2 (30) := 'Inner_Variable without label';
         BEGIN
         dbms_output.put_line (outer_block);
         dbms_output.put_line (inner_block);
         dbms_output.put_line (OUTER.outer_block);
         END;
dbms_output.put_line (outer_block);
END;
/

Output:
Inner_Variable without label
Inner_Variable
Global_Variable
Global_Variable

Program6: Comments (Single Line Comment)

DECLARE
-- Single Line Comment
v_no NUMBER (4);
BEGIN
v_no:= 5*6;
dbms_output.put_line (v_no);
END;
/

Output:
30

Program7: Multiple Line Comment

DECLARE
v_no NUMBER(4);
BEGIN
/* Multiple Line Commenting here we will multiply
And place the result in the v_no */
v_no:= 5*6;
dbms_output.put_line (v_no);
END;
/

Output:
30
Program23: Using the Group Function in PL/SQL Statement
DECLARE
v_sal employees.salary%type;
BEGIN
SELECT SUM (salary)
INTO v_sal
FROM employees
WHERE department_id=60;
dbms_output.put_line(v_sal);
END;
/

Output:
28800
Program24: Usage of %ROWCOUNT Cursor Attribute

DROP TABLE emp PURGE;

CREATE TABLE emp AS


SELECT * FROM EMPLOYEES;

SELECT COUNT (*) FROM emp;

Output:

 COUNT (*)
----------
       107

DECLARE
v_del_rows NUMBER (4);
BEGIN
DELETE emp;
v_del_rows:= SQL%rowcount;
dbms_output.put_line (v_del_rows);
END;
/

Output:
107

Lesson 5-6 Writing Control Structures & Working with Composite Data types

Program1: Usage of IF statement

DECLARE
v_myage NUMBER := 31;
BEGIN
IF v_myage <11 THEN
dbms_output.put_line (' I am a child ');
END IF;
END;
/

Output:

Program2: Usage of IF THEN ELSE statement

DECLARE
v_myage NUMBER := 31;
BEGIN
IF v_myage < 11 THEN
dbms_output.put_line (' I am a child ');
ELSE
dbms_output.put_line (' I am not a child');
END IF;
END;
/
Output:
I am not a child

Program3: Usage of IF ELSIF ELSE Clause

DECLARE
v_myage NUMBER := 31;
BEGIN
IF v_myage < 11 THEN
dbms_output.put_line (' I am a child ');
ELSIF v_myage < 20 THEN
dbms_output.put_line (' I am young');
ELSIF v_myage < 30 THEN
dbms_output.put_line (' I am in twenties');
ELSIF v_myage < 40 THEN
dbms_output.put_line (' I am in fourties');
ELSE
dbms_output.put_line (' I am always young');
END IF;
END;
/

Output:
I am in four ties

Program4: CASE statement


s
DECLARE
v_grade CHAR (1) := UPPER ('&grade');
v_appraisal VARCHAR2 (20);
BEGIN
v_appraisal :=
        CASE v_grade
        WHEN 'A' then 'Excellent'
        WHEN 'B' then 'Very Good'
        WHEN 'C' then 'Good'
        ELSE
  'no such grade'
        END;
dbms_output.put_line (v_appraisal);
END;
/

Output:
Enter value for grade: A
old   2: v_grade CHAR (1) := UPPER ('&grade');
new   2: v_grade CHAR (1) := UPPER ('A');
Excellent

Program5: SEARCHED CASE

DECLARE
v_grade CHAR (1) := UPPER ('&grade');
v_appraisal VARCHAR2 (20);
BEGIN
v_appraisal :=
        CASE
        WHEN v_grade='A' then 'Excellent'
        WHEN v_grade='B' then 'Very Good'
        WHEN v_grade IN ('C','D') then 'Good'
        ELSE 'no such grade'
        END;
dbms_output.put_line (v_appraisal);
END;
/

Output:
Enter value for grade: A
old   2: v_grade CHAR (1) := UPPER ('&grade');
new   2: v_grade CHAR (1) := UPPER ('A');
Excellent

Program6: Handing NULL Value

DECLARE
v_name VARCHAR2 (10);
BEGIN
IF NOT (v_name) = 'star' then
dbms_output.put_line ('Welcome');
else
Dbms_ output.put_line ('working');
END IF;
END;
/

Output:
working

Program7: Usage of Simple LOOP

DECLARE
v_count NUMBER (2):= 1;
BEGIN
LOOP
dbms_output.put_line (v_count);
v_count := v_count+1;
EXIT WHEN v_count>5;
END LOOP;
END;
/

Output:
1
2
3
4
5

Program8: Usage of Simple LOOP

DECLARE
v_count NUMBER (2):= 10;
BEGIN
LOOP
dbms_output.put_line (v_count);
v_count := v_count+1;
exit when v_count>5;
END LOOP;
END;
/

Output:
10

Program9: Usage of a WHILE Loop

DECLARE
v_count NUMBER (2) :=1;
BEGIN
WHILE v_count < 3 loop
dbms_output.put_line (v_count);
v_count := v_count+1;
END LOOP;
END;
/
Output:
1
2

Program10: Usage of a FOR Loop in Ascending Order


BEGIN
FOR I in 1..5 LOOP
dbms_output.put_line (I);
END LOOP;
END;
/
Output:
1
2
3
4
5

Program11: Usage of a FOR Loop in Descending Order

BEGIN
FOR I in REVERSE 1..5 LOOP
dbms_output.put_line (I);
END LOOP;
END;
/

Output:
5
4
3
2
1

Program12: Usage of a PL/SQL Record


DECLARE
TYPE emp_rec IS RECORD
(
v_name VARCHAR2 (10),
v_date DATE
);
v_rec emp_rec;
BEGIN
SELECT last_name, hire_date
INTO v_rec
FROM employees
WHERE employee_id=101;
dbms_output.put_line (v_rec.v_name);
dbms_output.put_line (v_rec.v_date);
END;
/
Output:
Kochhar
21-SEP-89

Program13: Usage of a %ROWTYPE

DECLARE
emp_rec employees%ROWTYPE;
BEGIN
SELECT *
INTO emp_rec
FROM employees
WHERE employee_id=101;
dbms_output.put_line (emp_rec.last_name);
dbms_output.put_line (emp_rec.salary);
END;
/

Output:
17000

 Program14:  Usage of a DEFINE command


DEFINE countryid = CA

DECLARE
country_record countries%ROWTYPE;
BEGIN
SELECT *
INTO country_record
FROM countries
WHERE country_id=UPPER('&countryid');
DBMS_OUTPUT.PUT_LINE('Country Id: ' || country_record.country_id || 'Country Name: ' || country_record.country_name || '
Region: ' || country_record.region_id);
END;
/

Output:
old   7: WHERE country_id=UPPER('&countryid');
new   7: WHERE country_id=UPPER('CA');
Country Id: CACountry Name: Canada Region: 2

Program15: Usage of INDEX BY TABLE with %TYPE

DECLARE
TYPE emp_tab IS TABLE OF
employees.last_name%type
INDEX BY PLS_INTEGER;
v_emp emp_tab;
BEGIN
SELECT last_name INTO v_emp (1)
FROM employees
WHERE employee_id=101;
dbms_output.put_line (v_emp (1));
END;
/

Output:  Kochhar

Program16: Usage of INDEX BY TABLE with %TYPE

DECLARE
TYPE dept_table_type
IS TABLE OF
departments.department_name%TYPE
INDEX BY PLS_INTEGER;
my_dept_table   dept_table_type;
loop_count NUMBER(2) :=10;
deptno NUMBER(4) := 0;
BEGIN
FOR i IN 1..loop_count LOOP
deptno:=deptno+10;
SELECT department_name
INTO my_dept_table(i)
FROM departments
WHERE department_id = deptno;
 dbms_output.put_line(my_dept_table(i));
END LOOP;
END;
/

Output:

Administration
Marketing
Purchasing
Human Resources
Shipping
IT
Public Relations
Sales
Executive
Finance

Program17: Usage of INDEX BY TABLE with %TYPE

DECLARE
TYPE dept_table_type
IS TABLE OF
departments.department_name%TYPE
INDEX BY PLS_INTEGER;
my_dept_table   dept_table_type;
loop_count NUMBER(2) :=10;
deptno NUMBER(4) := 0;
BEGIN
FOR i IN 1..loop_count LOOP
deptno:=deptno+10;
SELECT department_name
INTO my_dept_table(i)
FROM departments
WHERE department_id = deptno;
END LOOP;
FOR i IN 1..loop_count
LOOP
dbms_output.put_line(my_dept_table(i));
END LOOP;
END;
/

Output:

Administration
Marketing
Purchasing
Human Resources
Shipping
IT
Public Relations
Sales
Executive
Finance

Program18: Usage of INDEX BY TABLE with %ROWTYPE

DECLARE
TYPE dept_table_type
IS TABLE OF
departments%ROWTYPE
INDEX BY PLS_INTEGER;
my_dept_table   dept_table_type;
loop_count NUMBER(2) :=10;
deptno NUMBER(4) := 0;
BEGIN
FOR i IN 1..loop_count LOOP
deptno:=deptno+10;
SELECT *
INTO my_dept_table(i)
FROM departments
WHERE department_id = deptno;
dbms_output.put_line(my_dept_table(i).department_name);
END LOOP;
END;
/
Output:

Administration
Marketing
Purchasing
Human Resources
Shipping
IT
Public Relations
Sales
Executive
Finance

Program19: Usage of INDEX BY TABLE with %ROWTYPE

DECLARE
TYPE dept_table_type
IS TABLE OF
departments%ROWTYPE
INDEX BY PLS_INTEGER;
my_dept_table   dept_table_type;
loop_count NUMBER(2) :=10;
deptno NUMBER(4) := 0;
BEGIN
FOR i IN 1..loop_count LOOP
deptno:=deptno+10;
SELECT *
INTO my_dept_table(i)
FROM departments
WHERE department_id = deptno;
END LOOP;
FOR i IN 1..loop_count
LOOP
dbms_output.put_line('Department Number: ' || my_dept_table(i).department_id || ' Department Name: ' ||
my_dept_table(i).manager_id || ' Location Id: ' || my_dept_table(i).location_id);
END LOOP;
END;
/

Output:

Department Number: 10 Department Name: 200 Location Id: 1700


Department Number: 20 Department Name: 201 Location Id: 1800
Department Number: 30 Department Name: 114 Location Id: 1700
Department Number: 40 Department Name: 203 Location Id: 2400
Department Number: 50 Department Name: 121 Location Id: 1500
Department Number: 60 Department Name: 103 Location Id: 1400
Department Number: 70 Department Name: 204 Location Id: 2700
Department Number: 80 Department Name: 145 Location Id: 2500
Department Number: 90 Department Name: 100 Location Id: 1700
Department Number: 100 Department Name: 108 Location Id: 1700

Program20: Usage of INDEX BY TABLE with %TYPE

DECLARE
TYPE emp_tab IS TABLE OF
employees.last_name%type
INDEX BY PLS_INTEGER;
v_emp emp_tab;
BEGIN
SELECT last_name INTO v_emp (1)
FROM employees
WHERE employee_id=101;
SELECT last_name INTO v_emp (2)
FROM employees
WHERE employee_id=102;
dbms_output.put_line (v_emp (1));
dbms_output.put_line (v_emp (2));
END;
/

Output:
Kochhar
De Haan

Program21: Usage of INDEX BY TABLE with %ROWTYPE

DECLARE
TYPE emp_tab IS TABLE OF
employees%rowtype
INDEX BY PLS_INTEGER;
v_emp emp_tab;
BEGIN
SELECT * INTO v_emp (1)
FROM employees
WHERE employee_id=101;
SELECT * INTO v_emp (2)
FROM employees
WHERE employee_id=102;
dbms_output.put_line (v_emp (1).last_name || ' job ' || v_emp (1).job_id);
dbms_output.put_line (v_emp (2).last_name || ' job ' || v_emp (2).job_id);
END;
/

Output:

Kochhar job AD_VP


De Haan job AD_VP
Program22: Usage of INDEX BY TABLE with %ROWTYPE with EXISTS Method

DECLARE
TYPE emp_tab IS TABLE OF
employees%rowtype
INDEX BY PLS_INTEGER;
v_emp emp_tab;
BEGIN
SELECT * INTO v_emp (1)
FROM employees
WHERE employee_id=101;
SELECT * INTO v_emp (2)
FROM employees
WHERE employee_id=102;
IF v_emp.EXISTS (1) THEN
dbms_output.put_line (v_emp (1).last_name || ' job ' || v_emp (1).job_id);
END IF;
dbms_output.put_line (v_emp (2).last_name || ' job ' || v_emp (2).job_id);
END;
/

Output:

Kochhar job AD_VP


De Haan job AD_VP

Program23: Usage of INDEX BY TABLE with %ROWTYPE with COUNT Method

DECLARE
TYPE emp_tab IS TABLE OF
employees%rowtype
INDEX BY PLS_INTEGER;
v_emp emp_tab;
BEGIN
SELECT * INTO v_emp (1)
FROM employees
WHERE employee_id=101;
SELECT * INTO v_emp (2)
FROM employees
WHERE employee_id=102;
dbms_output.put_line (' counting ' || v_emp.count);
dbms_output.put_line (v_emp (1).last_name || ' job ' || v_emp (1).job_id);
dbms_output.put_line (v_emp (2).last_name || ' job ' || v_emp (2).job_id);
END;
/

Output:
Counting 2
Kochhar job AD_VP
De Haan job AD_VP

Program24: Usage of INDEX BY TABLE with %ROWTYPE with PRIOR Method

DECLARE
TYPE emp_tab IS TABLE OF
employees%rowtype
INDEX BY PLS_INTEGER;
v_emp emp_tab;
BEGIN
SELECT * INTO v_emp (1)
FROM employees
WHERE employee_id=101;
SELECT * INTO v_emp (2)
FROM employees
WHERE employee_id=102;
dbms_output.put_line (' prior ' || v_emp.prior (2));
dbms_output.put_line (v_emp (1).last_name || ' job ' || v_emp (1).job_id);
dbms_output.put_line (v_emp (2).last_name || ' job ' || v_emp (2).job_id);
END;
/

Output:

Prior 1
Kochhar job AD_VP
De Haan job AD_VP

Program25: Usage of INDEX BY TABLE with %ROWTYPE with NEXT Method

DECLARE
TYPE emp_tab IS TABLE OF
employees%rowtype
INDEX BY PLS_INTEGER;
v_emp emp_tab;
BEGIN
SELECT * INTO v_emp (1)
FROM employees
WHERE employee_id=101;
SELECT * INTO v_emp (2)
FROM employees
WHERE employee_id=102;
dbms_output.put_line (' Next ' || v_emp.next(1));
dbms_output.put_line (v_emp (1).last_name || ' job ' || v_emp (1).job_id);
dbms_output.put_line (v_emp (2).last_name || ' job ' || v_emp (2).job_id);
END;
/

Output:

Next 2
Kochhar job AD_VP
De Haan job AD_VP

Program26: Usage of INDEX BY TABLE with %ROWTYPE with FIRST..LAST Method

DECLARE
TYPE emp_tab IS TABLE OF
employees%rowtype
INDEX BY PLS_INTEGER;
v_emp emp_tab;
BEGIN
FOR I in 100..104 LOOP
SELECT * INTO v_emp (I) FROM employees WHERE employee_id=I;
END LOOP;
FOR I IN v_emp.FIRST..v_emp.LAST LOOP
dbms_output.put_line (v_emp (i).first_name || ' last name is ' || v_emp (i).last_name);
END LOOP;
END;
/

Output:
Steven last name is King
Neena last name is Kochhar
Lex last name is De Haan
Alexander last name is Hunold
Bruce last name is Ernst

Lesson 7-8 (Cursors & Exceptions)

Program1: Usage of %NOTFOUND Cursor Attribute

DECLARE
v_name VARCHAR2 (20);
CURSOR v_cur IS
SELECT first_name
FROM employees;
BEGIN
OPEN v_cur;
LOOP
FETCH v_cur INTO v_name;
dbms_output.put_line (v_name);
EXIT WHEN v_cur%NOTFOUND;
END LOOP;
CLOSE v_cur;
END;
/

Output:

Alana
Matthew
Jennifer
Eleni
Eleni

Program2: Usage of %FOUND Cursor Attribute

DECLARE
v_name VARCHAR2 (20);
CURSOR v_cur IS
SELECT first_name
FROM employees;
BEGIN
OPEN v_cur;
LOOP
FETCH v_cur INTO v_name;
dbms_output.put_line (v_name);
EXIT WHEN v_cur%FOUND;
END LOOP;
CLOSE v_cur;
END;
/

Output:          Ellen

Program3: Usage of %ROWCOUNT Cursor Attribute

DECLARE
v_name VARCHAR2 (20);
CURSOR v_cur IS
SELECT first_name
FROM employees;
BEGIN
OPEN v_cur;
LOOP
FETCH v_cur INTO v_name;
dbms_output.put_line (v_name);
EXIT WHEN v_cur%ROWCOUNT>5;
END LOOP;
CLOSE v_cur;
END;
/

Output:
Ellen
Sundar
Mozhe
David
Hermann
Shelli

Program4: Usage of RECORD with Cursor


DECLARE
CURSOR v_cur IS
SELECT * FROM employees;
v_emp v_cur%ROWTYPE;
BEGIN
OPEN v_cur;
LOOP
FETCH v_cur INTO v_emp;
dbms_output.put_line (v_emp.first_name);
EXIT WHEN v_cur%ROWCOUNT>5;
END LOOP;
CLOSE v_cur;
END;
/

Output:

Donald
Douglas
Jennifer
Michael
Pat
Susan

Program5: Usage of CURSOR FOR LOOP

DECLARE
CURSOR v_cur IS SELECT * FROM employees;
BEGIN
FOR v_emp IN v_cur LOOP
dbms_output.put_line (v_emp.first_name);
END LOOP;
END;
/

Output:

Samuel
Vance
Alana
Kevin

Program6: Usage of CURSOR FOR LOOP WITH sub query

BEGIN
FOR v_emp IN (SELECT * FROM EMPLOYEES) LOOP
dbms_output.put_line (v_emp.first_name);
END LOOP;
END;
/

Output:

Samuel
Vance
Alana
Kevin

Program7: Usage of Cursor with Parameters

DECLARE
CURSOR c1 (p_deptno IN NUMBER, p_job IN VARCHAR2) IS
SELECT employee_id, last_name
FROM employees
WHERE department_id=p_deptno AND job_id=p_job;
v1 c1%rowtype;
BEGIN
OPEN c1 (10,'AD_ASST');
LOOP
FETCH C1 INTO v1;
EXIT WHEN c1%notfound;
dbms_output.put_line ('dept10 details ' || v1.last_name);
END LOOP;
CLOSE C1;
OPEN c1 (20,'MK_MAN');
LOOP
FETCH C1 INTO v1;
EXIT WHEN c1%notfound;
dbms_output.put_line ('dept20 details ' || v1.last_name);
END LOOP;
CLOSE C1;
END;
/

Output:

dept10 details Whalen


dept20 details Hartstein

Program8: Usage of Cursor with FOR UPDATE OF Clause

SQL> select salary from employees where department_id=60;

 SALARY
---------
      9000
      6000
      4800
      4800
      4200

SQL> UPDATE employees


SET salary=4000
WHERE department_id=60;

Output:
5 rows updated.

DECLARE
CURSOR c1 IS
SELECT employee_id, salary
FROM employees
WHERE department_id=60
FOR UPDATE OF SALARY NOWAIT;
BEGIN
FOR emp_rec IN c1 LOOP
IF emp_rec.salary<5000 then
UPDATE employees
SET salary=3000;
END IF;
END LOOP;
END;
/

SQL> select salary from employees where department_id=60;


    SALARY
---------------
      3000
      3000
      3000
      3000
      3000

Note :         Cursor can't move backward

SQL> ROLLBACK;
Rollback complete.

 SQL> select salary from employees where department_id=60;

    SALARY
-----------------
      9000
      6000
      4800
      4800
      4200

Program9: Usage of Cursor with FOR UPDATE OF, WHERE CURRENT OF Clause

DECLARE
CURSOR c1 IS
SELECT employee_id, salary
FROM employees
WHERE department_id=60
FOR UPDATE OF SALARY NOWAIT;
BEGIN
FOR emp_rec IN c1 LOOP
IF emp_rec.salary<5000 then
UPDATE employees
SET salary=salary+999
WHERE CURRENT OF c1;
END IF;
END LOOP;
END;
/

SQL> select salary from employees where department_id=60;

   SALARY
-----------------
      9000
      6000
      5799
      5799
      5199

SQL> rollback;
Rollback complete.

Program10: Printing the ROWID value

DECLARE
v_row rowid;
v_empno employees.employee_id%type;
v_sal employees.salary%type;
CURSOR c1 IS
SELECT ROWID,employee_id, salary
FROM employees
WHERE department_id=60;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO v_row,v_empno,v_sal;
exit when c1%NOTFOUND;
dbms_output.put_line(v_row ||'-'||'-'||v_empno||'-'||v_sal);
END LOOP;
CLOSE c1;
END;
/

Output:   
 AAAMg3AAFAAAABYAAD--103-9000
AAAMg3AAFAAAABYAAE--104-6000
AAAMg3AAFAAAABYAAF--105-4800
AAAMg3AAFAAAABYAAG--106-4800
AAAMg3AAFAAAABYAAH--107-4200

SQL> rollback;
Rollback complete.

Program11: Implementing with ROWID (9i)

DECLARE
v_row rowid;
v_empno employees.employee_id%type;
v_sal employees.salary%type;
CURSOR c1 IS
SELECT ROWID,employee_id, salary
FROM employees
WHERE department_id=60;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO v_row,v_empno,v_sal;
dbms_output.put_line(v_row ||'-'||'-'||v_empno||'-'||v_sal);
IF V_SAL < 5000 THEN
update employees
set salary=salary+999
where rowid=v_row;
END IF;
exit when c1%NOTFOUND;
END LOOP;
CLOSE c1;
END;
/

Output:
AAAMg3AAFAAAABYAAD--103-9000
AAAMg3AAFAAAABYAAE--104-6000
AAAMg3AAFAAAABYAAF--105-4800
AAAMg3AAFAAAABYAAG--106-4800
AAAMg3AAFAAAABYAAH--107-4200
AAAMg3AAFAAAABYAAH--107-4200

Program12: Raising the Implicit Exception

SELECT first_name
FROM employees
WHERE first_name='John';
FIRST_NAME
-------------------
John
John
John

DECLARE
v_name VARCHAR2 (10);
BEGIN
SELECT first_name INTO v_name
FROM employees
WHERE first_name='John';
dbms_output.put_line (v_name);
END;
/

Output:
ERROR at line 1:
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 4

Program13: Usage of TOO_MANY_ROWS Exception

DECLARE
v_name VARCHAR2 (10);
BEGIN
SELECT first_name INTO v_name
FROM employees
WHERE first_name='John';
dbms_output.put_line (v_name);
EXCEPTION
WHEN TOO_MANY_ROWS THEN
dbms_output.put_line ('Returning more than one row');
END;
/
Output:   Returning more than one row

Program14: Usage of VALUE_ERROR Exception

DECLARE
v_name VARCHAR2(3);
BEGIN
SELECT last_name INTO v_name
FROM employees
WHERE employee_id=101;
dbms_output.put_line (v_name);
EXCEPTION
WHEN VALUE_ERROR THEN
dbms_output.put_line ('Data type size is small');
END;
/
Output:  Data type size is small

Program15: Usage of ZERO_DIVIDE Exception

DECLARE
v_sal NUMBER;
BEGIN
SELECT salary/0 INTO v_sal
FROM employees
WHERE employee_id=101;
dbms_output.put_line (v_sal);
EXCEPTION
WHEN ZERO_DIVIDE THEN
dbms_output.put_line ('We cant divide by zero');
END;
/

Output:           We cant divide by zero

Program16: Usage of Non Predefined Exception


DECLARE
v_excep EXCEPTION;
PRAGMA EXCEPTION_INIT (v_excep,-6502);
v_name VARCHAR2 (2);
BEGIN
SELECT last_name INTO v_name
FROM employees
WHERE employee_id=101;
EXCEPTION
WHEN v_excep THEN
dbms_output.put_line ('Check the Variable Size');
END;
/

Output:               Check the Variable Size

Program17: Usage of Tracking the Error Number, Error Message


DECLARE
v_name VARCHAR2 (2);
v_err_num NUMBER;
v_err_mess VARCHAR2 (250);
BEGIN
SELECT last_name INTO v_name
FROM employees WHERE employee_id=101;
EXCEPTION
WHEN OTHERS THEN
v_err_num := SQLCODE;
v_err_mess := SQLERRM;
dbms_output.put_line (v_err_num);
dbms_output.put_line (v_err_mess);
END;
/

Output:

-6502
ORA-06502: PL/SQL: numeric or value error: character string buffer too small

Program18: Usage of User Defined Exception

DECLARE
v_excep EXCEPTION;
BEGIN
UPDATE employees
SET salary=8000
WHERE employee_id=1;
IF SQL%NOTFOUND THEN
RAISE v_excep;
END IF;
EXCEPTION
WHEN v_excep THEN
dbms_output.put_line ('Explicitly Raised Exception');
END;
/

Output:

Explicitly Raised Exception

Program19: Usage of RAISE_APPLICATION_ERROR

BEGIN
UPDATE employees
SET salary=8000
WHERE employee_id=1;
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR (-20000,'Raising Error');
END IF;
END;
/

Output:

BEGIN
*
ERROR at line 1:
ORA-20000: Raising Error
ORA-06512: at line 6

Program20: Handling Multiple Exceptions in Exception Section

DROP TABLE messages PURGE;

SQL>CREATE TABLE MESSAGES


(
MESSAGE VARCHAR2 (250)
);

Table created.

SQL> DELETE FROM MESSAGES;


0 rows deleted.

SQL> DEFINE sal = 6000

DECLARE
ename employees.last_name%TYPE;
emp_sal employees.salary%TYPE := &sal;
BEGIN
SELECT last_name
INTO ename
FROM employees
WHERE salary = emp_sal;
INSERT INTO messages VALUES (ename || ' - ' || emp_sal);
EXCEPTION
WHEN no_data_found THEN
INSERT INTO messages VALUES
('No employee with a salary of '|| TO_CHAR (emp_sal));
WHEN too_many_rows THEN
INSERT INTO messages VALUES
('More than one employee with a salary of '||TO_CHAR (emp_sal));
WHEN others THEN
INSERT INTO messages VALUES
('Some other error occurred.');
END;
/

Output:

old   3: emp_sal employees.salary%TYPE := &sal;


new   3: emp_sal employees.salary%TYPE := 6000;

SQL> SELECT * FROM messages;


MESSAGE
--------------------------------------------------------
More than one employee with a salary of 6000

Program21: Child Record found exception

DECLARE
childrecord_exists EXCEPTION;
PRAGMA EXCEPTION_INIT (childrecord_exists, -02292);
BEGIN
DBMS_OUTPUT.PUT_LINE (' Deleting department 40........');
DELETE FROM departments
WHERE department_id=40;
EXCEPTION
WHEN childrecord_exists THEN
DBMS_OUTPUT.PUT_LINE (' Cannot delete this department. There are employees in this department
(child records exist.) ');
END;
/

Output:

Deleting department 40........


Cannot delete this department. There are employees in this department
(child records exist.)

Lesson 9-10 Procedures & Functions

Program1: Simple Procedure to display a String

CREATE OR REPLACE PROCEDURE p1


IS
BEGIN
dbms_output.put_line ('welcome to 1st Procedure');
END p1;
/

Output:
Procedure created.

Executing a procedure

SQL> exec p1;


welcome to 1st Procedure

Program2: Simple Procedure to display a String

CREATE PROCEDURE hello_again IS


BEGIN
DBMS_OUTPUT.PUT_LINE ('Hello World again');
END;
/

Output:

CREATE PROCEDURE hello_again IS


BEGIN
DBMS_OUTPUT.PUT_LINE ('Hello World again');
END;
/

Executing a procedure in a PL/SQL block

SQL> BEGIN

hello_again;

END;

Output:

Hello World again

Program66: Creating a Procedure with IN parameters (INSERT)


CREATE OR REPLACE PROCEDURE add_job

jobid jobs.job_id%TYPE,

jobtitle jobs.job_title%TYPE

IS

BEGIN

INSERT INTO jobs (job_id, job_title) VALUES (jobid, jobtitle);

COMMIT;

END add_job;

Executing a Procedure

SQL> EXECUTE add_job ('IT_DBA', 'Database Administrator');

PL/SQL procedure successfully completed.

SQL> SELECT *

FROM jobs

WHERE job_id = 'IT_DBA';

JOB_ID     JOB_TITLE                           MIN_SALARY MAX_SALARY

---------- ----------------------------------- ---------- ----------

IT_DBA     Database Administrator

Program67: Creating a Procedure with IN parameters (UPDATE) with Exception Handling

CREATE OR REPLACE PROCEDURE upd_job

jobid IN jobs.job_id%TYPE,

jobtitle IN jobs.job_title%TYPE

IS

BEGIN

UPDATE jobs

SET job_title = jobtitle


WHERE job_id = jobid;

          IF SQL%NOTFOUND THEN

          RAISE_APPLICATION_ERROR (-20202, 'No job updated.');

          END IF;

END upd_job;

Output:

Procedure created.

Executing a Procedure:

SQL> EXECUTE upd_job ('IT_DBA', 'Data Administrator');

PL/SQL procedure successfully completed.

SQL> SELECT * FROM jobs WHERE job_id = 'IT_DBA';

JOB_ID     JOB_TITLE                           MIN_SALARY MAX_SALARY

---------- ----------------------------------- ---------- ----------

IT_DBA     Data Administrator

SQL> EXEC upd_job ('IT_WEB', 'Web Master');

SQL> EXEC upd_job ('IT_WEB', 'Web Master');

BEGIN upd_job ('IT_WEB', 'Web Master'); END;

ERROR at line 1:

ORA-20202: No job updated.

ORA-06512: at "HR.UPD_JOB", line 12

ORA-06512: at line 1

Program: 68

Creating a Procedure with IN parameters (DELETE) with Exception Handling

CREATE OR REPLACE PROCEDURE del_job


(

jobid jobs.job_id%TYPE

IS

BEGIN

DELETE FROM jobs

WHERE job_id = jobid;

          IF SQL%NOTFOUND THEN

          RAISE_APPLICATION_ERROR (-20203, 'No jobs deleted.');

          END IF;

END DEL_JOB;

EXECUTE del_job ('IT_DBA')

SELECT * FROM jobs WHERE job_id = 'IT_DBA';

SQL> EXECUTE del_job ('IT_WEB');

BEGIN del_job ('IT_WEB'); END;

ERROR at line 1:

ORA-20203: No jobs deleted.

ORA-06512: at "HR.DEL_JOB", line 11

ORA-06512: at line 1

Program69: Creating a Procedure with IN parameter along with local variables

CREATE OR REPLACE PROCEDURE P1

P_NO IN NUMBER

IS

v_name VARCHAR2 (10);

BEGIN

SELECT first_name

INTO v_name

FROM employees

WHERE employee_id=P_NO;

dbms_output.put_line (v_name);
END;

Output:

Executing a procedure:
EXEC P1 (100);

Program70: Procedure with Cursors

CREATE OR REPLACE PROCEDURE P1

IS

CURSOR emp_cursor IS SELECT * FROM employees;

BEGIN

FOR emp_rec IN emp_cursor LOOP

dbms_output.put_line (emp_rec.employee_id);

END LOOP;

END;

EXEC p1;

Program: 71

Procedure with OUT Parameter (Use Bind variable for OUT Parameter)

CREATE OR REPLACE PROCEDURE p1

(P_NO IN NUMBER, P_JOB OUT VARCHAR2, P_SAL OUT NUMBER)

IS

BEGIN

SELECT job_id, salary

INTO P_JOB, P_SAL

FROM employees

W HERE employee_id=P_NO;

END p1;

Procedure Created

SQL> variable g_job VARCHAR2 (20)

SQL> variable g_sal varchar2 (20)

SQL> exec p1 (100, :g_job, :g_sal);

SQL> print g_job g_sal

Program72: Procedure with OUT Parameter (Use Bind variable for OUT Parameter) With Exception Handling

CREATE OR REPLACE PROCEDURE p1


(
P_NO IN NUMBER,

P_JOB OUT VARCHAR2,

P_SAL OUT NUMBER

IS

BEGIN

SELECT job_id, salary

INTO P_JOB, P_SAL

FROM employees

WHERE employee_id=P_NO;

EXCEPTION

WHEN NO_DATA_FOUND THEN

dbms_output.put_line ('Check the Parameter Value');

END p1;

Variable g_job VARCHAR2(20)

Variable g_sal VARCHAR2(20)

EXEC p1 (10, :g_job, :g_sal);

Note:

A procedure can become invalid if the table it is based on is deleted or changed

We can recompile an invalid procedure using this command:

ALTER PROCEDURE procedure_name COMPILE;

Example1:

CREATE OR REPLACE FUNCTION f1

p_empno employee.employee_id%TYPE

RETURN varchar2

AS

v_name varchar2(50);

BEGIN
SELECT first_name

INTO v_name

FROM employees

WHERE employee_id=p_empno;

RETURN v_name;

EXCEPTION

WHEN NO_DATA_FOUND THEN

RETURN(‘The employee_id is not in the database');

WHEN OTHERS THEN

RETURN(‘Error in running function');

END;

EXEC f1;

Example:2

CREATE OR REPLACE FUNCTION id_is_good


(
p_empno IN NUMBER
)
RETURN BOOLEAN
AS
v_id_cnt NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_id_cnt
FROM employees
WHERE employee_id = p_empno;
RETURN 1 = v_id_cnt;
EXCEPTION
WHEN OTHERS THEN
RETURN FALSE;
END id_is_good;
/

DECLARE
v_id number;
BEGIN
v_id := &id;
IF id_is_good(v_id) THEN
DBMS_OUTPUT.PUT_LINE ('Student ID: '||v_id||' is a valid.');
ELSE
DBMS_OUTPUT.PUT_LINE ('Student ID: '||v_id||' is not valid.');
END IF;
END;/

Flashback in Oracle 10g

Flashback query was introduced in Oracle 9i. It provides a mechanism for viewing data as it existed at a particular point in time (a
timestamp or SCN). With 10g Release 1, Oracle has extended flashback query to enable us to view different versions of our data in
a given range between two timestamps or SCNs.
This article introduces the new flashback version query. It assumes that readers are familiar with flashback query concepts. For an
overview, including the necessary privileges required, read this oracle-developer.net article.
sample data
For the examples in this article, we will use a scratch table and some dummy data. The table is created as follows.  
SQL> CREATE TABLE fbt
  2  ( x INTEGER
  3  , y TIMESTAMP
  4  , z VARCHAR2(30) );

Table created
Used to retrieve the data which has been already committed with out going for recovery.
Flashbacks are of two types
Ø  Time base flashback
Ø  SCN based flashback (SCN stands for System Change Number)
Ex:
1) Using time based flashback
     a) SQL> Select *from student;
          -- This will display all the rows
     b) SQL> Delete student;
     c) SQL> Commit;              -- this will commit the work.
     d) SQL> Select *from student;
         -- Here it will display nothing
     e) Then execute the following procedures
         SQL> Exec dbms_flashback.enable_at_time(sysdate-2/1440)
     f) SQL> Select *from student;
         -- Here it will display the lost data
         -- The lost data will come but the current system time was used
     g) SQL> Exec dbms_flashback.disable
          -- Here we have to disable the flashback to enable it again

2) Using SCN based flashback


     a) Declare a variable to store SCN
          SQL> Variable s number
     b) Get the SCN
          SQL> Exec :s := exec dbms_flashback.get_system_change_number
     c) To see the SCN
         SQL> Print s
     d) Then execute the following procedures
          SQL> Exec dbms_flashback.enable_at_system_change_number(:s)
          SQL> Exec dbms_flashback.disable

We can now populate the table and make several changes to the data. We will add a row, update it a couple of times and finally
delete it. We will include a short pause between each DML operation, but capture the timestamp before and after the updates for
use in later examples. All DML operations will be committed, because new versions of data are only recorded following a commit.
We'll begin with a single-row insert.
SQL> EXEC DBMS_LOCK.SLEEP(10);
PL/SQL procedure successfully completed.
SQL> INSERT INTO fbt VALUES (1, LOCALTIMESTAMP, 'Initial population');
1 row created.
SQL> COMMIT;
Commit complete.
Note that we included a sleep before we added any data to the table. This is recommended by Oracle to avoid ORA-01466: unable
to read data - table definition has changed (this will only be an issue if the table is subject to flashback queries as soon as it is
created).
We can now update our sample data. Before we do, however, we'll capture the timestamp to use later in this article. We will also
sleep for ten seconds. Again, our DML must be committed.
SQL> ALTER SESSION SET nls_timestamp_format = 'DD-MON-YYYY HH24:MI:SS.FF3';
Session altered.
SQL> EXEC DBMS_LOCK.SLEEP(10);
PL/SQL procedure successfully completed.
SQL> SELECT LOCALTIMESTAMP AS lower_bound FROM dual;
LOWER_BOUND
------------------------
10-AUG-2005 18:01:07.109
1 row selected.
SQL> UPDATE fbt SET y = LOCALTIMESTAMP, z = 'First update';
1 row updated.
SQL> COMMIT;
Commit complete.
Next, we update the data a second time and capture the timestamp after the commit.
SQL> EXEC DBMS_LOCK.SLEEP(10);
PL/SQL procedure successfully completed.
SQL> UPDATE fbt SET y = LOCALTIMESTAMP, z = 'Second update';
1 row updated.
SQL> COMMIT;
Commit complete.
SQL> SELECT LOCALTIMESTAMP AS upper_bound FROM dual;
UPPER_BOUND
------------------------
10-AUG-2005 18:01:17.125
1 row selected.
Finally, we will delete the data and commit the change.
SQL> EXEC DBMS_LOCK.SLEEP(10);
PL/SQL procedure successfully completed.
SQL> DELETE FROM fbt WHERE x = 1;
1 row deleted.
SQL> COMMIT;
Commit complete.
Following the sample data population, we should have no data in the FBT table, which we can verify as follows.
SQL> SELECT * FROM fbt;
no rows selected
flashback version query
Despite the fact that there is no data in FBT, we can now run some flashback version queries against it. This will enable us to view
the data as it evolved between commits. Flashback version query is invoked using the new VERSIONS BETWEEN extension to the
FROM clause. It takes two forms as follows:
•        VERSIONS BETWEEN TIMESTAMP [lower bound] AND [upper bound]; or
•        VERSIONS BETWEEN SCN [lower bound] AND [lower bound].
The lower and upper boundaries can either be specific timestamps/SCNs or the keywords MINVALUE and MAXVALUE. These
keywords instruct Oracle to retrieve all available data versions. The age of the data available is determined by the undo_retention
parameter. For our first flashback version query, we will attempt to retrieve all available data.
SQL> ALTER SESSION SET nls_timestamp_format = 'DD-MON-YYYY HH24:MI:SS.FF3';
Session altered.
SQL> SELECT x, y, z
  2  FROM   fbt VERSIONS BETWEEN TIMESTAMP MINVALUE AND MAXVALUE
  3  ORDER  BY
  4         y;

         X Y                         Z
---------- ------------------------- -----------------------
         1 10-AUG-2005 18:00:57.078  Initial population
         1 10-AUG-2005 18:01:07.109  First update
         1 10-AUG-2005 18:01:17.125  Second update
         1 10-AUG-2005 18:01:17.125  Second update
4 rows selected.
We have seemingly generated four rows of data from one source record. What we are seeing, however, is the evolution of this
single record in terms of all the values it has held over time (depending on its presence in the undo segments). However, we can
see that the second update entry appears twice, yet we have no way of identifying why from the above output. Flashback version
query therefore includes several pseudo-columns to describe each version of our data, which we can now use to determine the
actual operations and change times.
versions pseudo-columns
As stated above, Oracle provides a variety of metadata with each version of our data. The metadata is exposed via a number of
pseudo-columns that we can use with our flashback version queries. These pseudo-columns are as follows:
•        VERSIONS_STARTTIME (start timestamp of version);
•        VERSIONS_STARTSCN (start SCN of version);
•        VERSIONS_ENDTIME (end timestamp of version);
•        VERSIONS_ENDSCN (end SCN of version);
•        VERSIONS_XID (transaction ID of version); and
•        VERSIONS_OPERATION (DML operation of version).
We can now include some of these pseudo-columns in our flashback version query as follows. Note the SCN metadata is excluded
as we are using timestamps for the examples.
SQL> SELECT z
  2  ,      VERSIONS_STARTTIME
  3  ,      VERSIONS_ENDTIME
  4  ,      VERSIONS_XID
  5  ,      VERSIONS_OPERATION
  6  FROM   fbt VERSIONS BETWEEN TIMESTAMP MINVALUE AND MAXVALUE
  7  ORDER  BY
  8         VERSIONS_ENDTIME;
Z                    VERSIONS_STARTTIME        VERSIONS_ENDTIME          VERSIONS_XID     VERSIONS_OPERATION
-------------------- ------------------------- ------------------------- ---------------- ------------------
Initial population   10-AUG-2005 18:00:53.000  10-AUG-2005 18:01:05.000  040026008A010000 I
First update         10-AUG-2005 18:01:05.000  10-AUG-2005 18:01:14.000  040029008A010000 U
Second update        10-AUG-2005 18:01:14.000  10-AUG-2005 18:01:26.000  040027008A010000 U
Second update        10-AUG-2005 18:01:26.000                            040028008A010000 D

4 rows selected.
This explains why we were seeing the second update row twice. If we look at the VERSIONS_OPERATION column, we can see
that the second appearance of the final update record is actually the delete operation against it (specified by 'D'). Without the
versions metadata, the Y timestamp column was actually confusing us into thinking we had two versions of the same record at the
same time.
The metadata also gives us an indication that the delete operation was the final version of this data. The end timestamp of the
version is NULL which tells us that there is no superceding record.
Interestingly, if we had not included a sleep between creating the FBT table and adding the single record, it would be likely (based
on observations) that all VERSIONS_* pseudo-columns (except the ENDTIME and ENDSCN) would be NULL for the insert record.
flashback transaction query
Oracle has provided a new view, FLASHBACK_TRANSACTION_QUERY, to provide more information about the data versions. This
includes the SQL required to reverse each change. Queries against this view are documented as "flashback transaction queries"
and require the SELECT ANY TRANSACTION system privilege. The view definition is as follows.
SQL> desc flashback_transaction_query
 Name                          Null?    Type
 ----------------------------- -------- --------------------
 XID                                    RAW(8)
 START_SCN                              NUMBER
 START_TIMESTAMP                        DATE
 COMMIT_SCN                             NUMBER
 COMMIT_TIMESTAMP                       DATE
 LOGON_USER                             VARCHAR2(30)
 UNDO_CHANGE#                           NUMBER
 OPERATION                              VARCHAR2(32)
 TABLE_NAME                             VARCHAR2(256)
 TABLE_OWNER                            VARCHAR2(32)
 ROW_ID                                 VARCHAR2(19)
 UNDO_SQL                               VARCHAR2(4000)
The VERSIONS_XID pseudo-column gives us the key to this view (the XID column) for specific versions of data. Rather than filter
on XID, we will look at all records currently available for our FBT table, concentrating on the more interesting UNDO_SQL column.
SQL> SELECT xid
  2  ,      operation
  3  ,      undo_sql
  4  FROM   flashback_transaction_query
  5  WHERE  table_owner = USER
  6  AND    table_name = 'FBT'
  7  ORDER  BY
  8         start_timestamp;

XID              OPERATION UNDO_SQL


---------------- --------- ------------------------------------------------------------
040026008A010000 INSERT    delete from "SCOTT"."FBT" where ROWID = 'AAANCeAAEAAAAuXAAA'
                           ;

040029008A010000 UPDATE    update "SCOTT"."FBT" set "Y" = TO_TIMESTAMP('10-AUG-2005 18:


                           00:57.000'), "Z" = 'Initial population' where ROWID = 'AAANC
                           eAAEAAAAuXAAA';

040027008A010000 UPDATE    update "SCOTT"."FBT" set "Y" = TO_TIMESTAMP('10-AUG-2005 18:


                           01:07.000'), "Z" = 'First update' where ROWID = 'AAANCeAAEAA
                           AAuXAAA';

040028008A010000 DELETE    insert into "SCOTT"."FBT"("X","Y","Z") values ('1',TO_TIMEST


                           AMP('10-AUG-2005 18:01:17.000'),'Second update');

4 rows selected.
The UNDO_SQL column shows us the reversal of every change we made to our sample record. Remember that we followed a
sequence of INSERT-UPDATE-UPDATE-DELETE. The values in the OPERATION column show us this, reading from the top-
down. The reversal of this sequence can be viewed by reading the UNDO_SQL column from the bottom-up. For recovery purposes
this can be quite useful. Note that the user responsible for the change is also available.
versions between explicit ranges
So far, we have issued flashback version queries using the MINVALUE and MAXVALUE range boundaries. As noted earlier in the
article, we can also supply specific timestamp or SCN ranges. Remember that we captured the timestamps before and after the
updates of our data? We will use these to limit the data versions requested below. One restriction with using specific timestamps or
SCNs is that they must be within the boundaries of the undo_retention parameter. Attempting to flashback to a version older than
approximately query time-undo_retention will result in ORA-30052: invalid lower limit snapshot expression.
Note that in the following examples, the ALTER SESSION statement is included for convenience, to save having to supply a long
format mask in the TO_TIMESTAMP calls.
SQL> ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'DD-MON-YYYY HH24:MI:SS.FF3';

Session altered.

SQL> SELECT x
  2  ,      y
  3  ,      z
  4  ,      VERSIONS_OPERATION
  5  FROM   fbt VERSIONS BETWEEN TIMESTAMP TO_TIMESTAMP('10-AUG-2005 18:01:07.109')
  6                                    AND TO_TIMESTAMP('10-AUG-2005 18:01:17.125')
  7  ORDER  BY
  8         VERSIONS_ENDTIME;

  X Y                         Z                    VERSIONS_OPERATION


--- ------------------------- -------------------- ------------------
  1 10-AUG-2005 18:00:57.078  Initial population
  1 10-AUG-2005 18:01:07.109  First update         U
  1 10-AUG-2005 18:01:17.125  Second update        U

3 rows selected.
Remember that we took the timestamp for the lower boundary before our first update. We can see this from the above version
query. At this time, there was already a version of our data in existence (the initial insert). The VERSIONS_OPERATION pseudo-
column is NULL for this record because the change had already occurred prior to the lower timestamp boundary. Our two updates
occurred within the timestamp range, however, and the VERSIONS_OPERATION column shows us this. This is a useful tracking
mechanism that enables us to distinguish between changes and existing data.
a note on timestamps and scns
As a final note, it is very simple to switch between SCNs and timestamps, should the need arise. Oracle 10g provides two
conversion functions, TIMESTAMP_TO_SCN and SCN_TO_TIMESTAMP for this purpose. Timestamps are mapped to SCNs with a
precision of approximately 3 seconds (in 9i this was 5 minutes). Most readers will be aware of the SYSTIMESTAMP and
LOCALTIMESTAMP functions to capture timestamps, but we can also capture SCNs using the
DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER function. We will complete this article with an example of each.
SQL> ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'DD-MON-YYYY HH24:MI:SS.FF3';

Session altered.

SQL> SELECT SYSTIMESTAMP


  2  ,      LOCALTIMESTAMP
  3  ,      DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER AS scn
  4  FROM   dual;

SYSTIMESTAMP                        LOCALTIMESTAMP                   SCN


----------------------------------- ------------------------- ----------
10-AUG-05 18.08.04.078000 +00:00    10-AUG-2005 18:08:04.078     1408640

1 row selected.

SQL> SELECT SCN_TO_TIMESTAMP(1408640) AS ts


  2  FROM   dual;

TS
-------------------------
10-AUG-2005 18:08:03.000
1 row selected.
SQL> SELECT TIMESTAMP_TO_SCN('10-AUG-2005 18:08:03.000') AS scn
  2  FROM   dual;
    SCN
----------
   1408640
1 row selected.

PL/SQL Control Structures


PL/SQL has a variety of control structures that allow you to control the behavior of the block as it runs. These structures include
conditional statements and loops

IF-THEN-ELSE
 If-then-else
 Case

1. Case with no else

2. Labeled case

3. Searched case

 Simple loop

 While loop

 For loop

 Goto and Labels

Syntax:
            If <condition1> then
                Sequence of statements;
            Elsif <condition1> then
                   Sequence of statements;
            ……
            Else
                  Sequence of statements;
            End if;

Ex:
DECLARE
       dno number(2);
BEGIN
       select deptno into dno from dept where dname = 'ACCOUNTING';
       if dno = 10 then
          dbms_output.put_line('Location is NEW YORK');
       elsif dno = 20 then
               dbms_output.put_line('Location is DALLAS');
       elsif dno = 30 then
               dbms_output.put_line('Location is CHICAGO');
       else
               dbms_output.put_line('Location is BOSTON');
       end if;
END;

Output:
                        Location is NEW YORK

CASE

Syntax:
            Case test-variable
                        When value1 then sequence of statements;
                        When value2 then sequence of statements;
                        ……
                        When valuen then sequence of statements;
                        Else sequence of statements;
            End case;

Ex:
DECLARE
      dno number(2);
BEGIN
       select deptno into dno from dept where dname = 'ACCOUNTING';
       case dno
               when 10 then
                         dbms_output.put_line('Location is NEW YORK');
               when 20 then
                         dbms_output.put_line('Location is DALLAS');
               when 30 then
                         dbms_output.put_line('Location is CHICAGO');
               else
                         dbms_output.put_line('Location is BOSTON');
        end case;
END;

Output:
                        Location is NEW YORK

CASE WITHOUT ELSE

Syntax:
            Case test-variable
                        When value1 then sequence of statements;
                        When value2 then sequence of statements;
                        ……
                        When valuen then sequence of statements;
            End case;

Ex:
DECLARE
       dno number(2);
BEGIN
       select deptno into dno from dept where dname = 'ACCOUNTING';
       case dno
               when 10 then
                         dbms_output.put_line('Location is NEW YORK');
               when 20 then
                         dbms_output.put_line('Location is DALLAS');
               when 30 then
                         dbms_output.put_line('Location is CHICAGO');
               when 40 then
                         dbms_output.put_line('Location is BOSTON');
               end case;
END;

Output:
                        Location is NEW YORK

LABELED CASE

Syntax:
            <<label>>
Case test-variable
                        When value1 then sequence of statements;
                        When value2 then sequence of statements;
                        ……
                        When valuen then sequence of statements;
            End case;

Ex:
DECLARE
       dno number(2);
BEGIN
       select deptno into dno from dept where dname = 'ACCOUNTING';
       <<my_case>>
       case dno
               when 10 then
                         dbms_output.put_line('Location is NEW YORK');
               when 20 then
                         dbms_output.put_line('Location is DALLAS');
               when 30 then
                         dbms_output.put_line('Location is CHICAGO');
               when 40 then
                         dbms_output.put_line('Location is BOSTON');
        end case my_case;
END;

Output:
                        Location is NEW YORK

SEARCHED CASE

Syntax:
            Case
                        When <condition1> then sequence of statements;
                        When <condition2> then sequence of statements;
                        ……
                        When <conditionn> then sequence of statements;
            End case;

Ex:
DECLARE
        dno number(2);
BEGIN
        select deptno into dno from dept where dname = 'ACCOUNTING';
        case dno
                 when dno = 10 then
                           dbms_output.put_line('Location is NEW YORK');
                 when dno = 20 then
                           dbms_output.put_line('Location is DALLAS');
                 when dno = 30 then
                           dbms_output.put_line('Location is CHICAGO');
                 when dno = 40 then
                           dbms_output.put_line('Location is BOSTON');
        end case;
END;

Output:
                        Location is NEW YORK

SIMPLE LOOP

Syntax:
            Loop
            Sequence of statements;
            Exit when <condition>;
            End loop;

In the syntax exit when <condition> is equivalent to


            If <condition> then
                        Exit;
            End if;

Ex:
                        DECLARE
      i number := 1;
BEGIN
      loop
          dbms_output.put_line('i = ' || i);
          i := i + 1;
          exit when i > 5;
      end loop;
END;

Output:
                        i = 1
i=2
i=3
i=4
i=5
WHILE LOOP

Syntax:
            While <condition> loop
            Sequence of statements;
            End loop;

Ex:
                        DECLARE
     i number := 1;
BEGIN
     While i <= 5 loop
               dbms_output.put_line('i = ' || i);
               i := i + 1;
      end loop;
END;

Output:
                        i = 1
i=2
i=3
i=4
i=5

FOR LOOP

Syntax:
            For <loop_counter_variable> in low_bound..high_bound loop
            Sequence of statements;
            End loop;

Ex1:
BEGIN
     For i in 1..5 loop
            dbms_output.put_line('i = ' || i);
      end loop;
END;
Output:
                        i = 1
i=2
i=3
i=4
i=5

Ex2:
BEGIN
     For i in reverse 1..5 loop
            dbms_output.put_line('i = ' || i);
     end loop;
END;
Output:
                        i = 5
i=4
i=3
i=2
i=1

NULL STATEMENT

Usually when you write a statement in a program, you want it to do something. There are cases, however, when you want to
tell PL/SQL to do absolutely nothing, and that is where the NULL comes.

The NULL statement deos nothing except pass control to the next executable statement.
You can use NULL statement in the following situations.

 Improving program readability.

Sometimes, it is helpful to avoid any ambiguity inherent in an IF statement that doesn’t cover all possible cases. For example, when
you write an IF statement, you do not have to include an ELSE clause.
 Nullifying a raised exception.

When you don’t want to write any special code to handle an exception, you can use the NULL statement to make sure that a raised
exception halts execution of the current PL/SQL block but does not propagate any exceptions to enclosing blocks.

 Using null after a label.

In some cases, you can pair NULL with GOTO to avoid having to execute additional statements. For example, I use
a GOTO statement to quickly move to the end of my program if the state of my data indicates that no further processing is required.
Because I do not have to do anything at the termination of the program, I place a NULL statement after the label because at least
one executable statement is required there. Even though NULL deos nothing, it is still an executable statement.

GOTO AND LABELS

Syntax:
            Goto label;

Where label is a label defined in the PL/SQL block. Labels are enclosed in double angle brackets. When a goto statement is
evaluated, control immediately passes to the statement identified by the label.
Ex:
BEGIN
     For i in 1..5 loop
            dbms_output.put_line('i = ' || i);
            if i = 4 then
               goto exit_loop;
            end if;
     end loop;
     <<exit_loop>>
     Null;
END;

Output:
                      i = 1
i=2
i=3
i=4

RESTRICTIONS ON GOTO

 It is illegal to branch into an inner block, loop.


 At least one executable statement must follow.

 It is illegal to branch into an if statement.

 It is illegal to branch from one if statement to another if statement.

 It is illegal to branch from exception block to the current block.

Oracle Cursors

The central purpose of the Oracle PL/SQL language is to make it as easy and efficient as possible to query and change the contents
of tables in a database. You must, of course, use the SQL language to access tables, and each time you do so, you use a cursor to
get the job done. A cursor is a pointer to a private SQL area that stores information about the processing of a SELECT or data
manipulation language (DML) statement (INSERT, UPDATE, DELETE, or MERGE). Cursor management of DML statements is
handled by Oracle Database, but PL/SQL offers several ways to define and manipulate cursors to execute SELECT statements.
This article focuses on the most-common ways programmers execute SELECT statements in PL/SQL, namely 
    Using the SELECT-INTO statement
    Fetching from an explicit cursor
    Using a cursor FOR loop
    Using EXECUTE IMMEDIATE INTO for dynamic queries
    Using cursor variables 

At the end of the article, I offer some quick tips to help you figure out which of these techniques you should use for different
scenarios.
SELECT-INTO
SELECT-INTO offers the fastest and simplest way to fetch a single row from a SELECT statement. The syntax of this statement is
SELECT select_list INTO variable_list FROM remainder_of_query;  
where remainder_of_query contains the list of tables or views, the WHERE clause, and other clauses of the query. The number and
types of elements in the variable_list must match those of the select_list.
If the SELECT statement identifies more than one row to be fetched, Oracle Database will raise the TOO_MANY_ROWS exception.
If the statement doesn’t identify any rows to be fetched, Oracle Database will raise the NO_DATA_FOUND exception.
Here are some examples of using SELECT-INTO:
Get the last name for a specific employee ID (the primary key in the employees table): 

DECLARE
  l_last_name  employees.last_name%TYPE;
BEGIN
  SELECT last_name
    INTO l_last_name
    FROM employees
   WHERE employee_id = 138;

  DBMS_OUTPUT.put_line (
     l_last_name);
END; 

If there is a row in the employees table with ID 138, this block will display the last name of that employee. If there is no such row, the
block will fail with an unhandled NO_DATA_FOUND exception. Assuming that a unique index is defined on the employee_id
column, this block will never raise the TOO_MANY_ROWS exception.

Fetch an entire row from the employees table for a specific employee ID: 

DECLARE
   l_employee   employees%ROWTYPE;
BEGIN
   SELECT *
     INTO l_employee
     FROM employees
    WHERE employee_id = 138;

   DBMS_OUTPUT.put_line (l_employee.last_name);
END; 

Again, if an employee exists for that ID, the last name will be displayed. In this case, I declare a record based on the employees
table and fetch all columns (with a SELECT *) into that record for the specified row.

Fetch columns from different tables: 

DECLARE
  l_last_name         
     employees.last_name%TYPE;
  l_department_name   
     departments.department_name%TYPE;
BEGIN
  SELECT last_name, department_name
    INTO l_last_name, l_department_name
    FROM employees e, departments d
   WHERE e.department_id=d.department_id
         AND e.employee_id=138;

  DBMS_OUTPUT.put_line ( l_last_name || ' in ' || l_department_name);


END;

In this case, I need more than one column value but not all the column values in either or both of the tables. So I declare two
variables and fetch the two column values into those variables.

What happens if the list of variables in the INTO clause does not match the SELECT list of the query? You will see one of the error
messages shown in Table 1.

ORA-00947: not enough values    The INTO list contains fewer variables than the SELECT list.
ORA-00913: too many values    The INTO list contains more variables than the SELECT list.
ORA-06502: PL/SQL: numeric or value error    The number of variables in the INTO and SELECT lists matches, but the datatypes
do not match and Oracle Database was unable to convert implicitly from one type to the other.

Table 1: Possible error messages if INTO and SELECT lists do not match

Fetching from Explicit Cursors

A SELECT-INTO is also referred to as an implicit query, because Oracle Database implicitly opens a cursor for the SELECT
statement, fetches the row, and then closes the cursor when it finishes doing that (or when an exception is raised).

You can, alternatively, explicitly declare a cursor and then perform the open, fetch, and close operations yourself.

Suppose I need to write a block that fetches employees in ascending salary order and gives them a bonus from a total pool of funds
by calling the assign_bonus procedure, whose header is 

PROCEDURE assign_bonus (

employee_id_in IN

employees.employee_id%TYPE,

bonus_pool_io IN OUT INTEGER)

Each time assign_bonus is called, the procedure subtracts the bonus given from the total and returns that reduced total. When that
bonus pool is exhausted, it stops fetching and commits all changes.
Listing 1 includes a block that uses an explicit cursor to implement this logic, and it describes the operations in the block at specified
line numbers.

Code Listing 1: Block and description of explicit cursor implementation 

 1  DECLARE
 2     l_total       INTEGER := 10000;
 3
 4     CURSOR employee_id_cur
 5     IS
 6          SELECT employee_id
 7            FROM plch_employees
 8        ORDER BY salary ASC;
 9
10     l_employee_id   employee_id_cur%ROWTYPE;
11  BEGIN
12     OPEN employee_id_cur;
13
14     LOOP
15        FETCH employee_id_cur INTO l_employee_id;
16        EXIT WHEN employee_id_cur%NOTFOUND;
17
18        assign_bonus (l_employee_id, l_total);
19        EXIT WHEN l_total &lt;= 0;
20     END LOOP;
21
22     CLOSE employees_cur;
23  END; 

Line(s) Description
4–8    The explicit cursor declaration. Move the query from the executable section (where the SELECT-INTO must reside), and use
the CURSOR keyword to declare (give a name to) that query.
10    Declare a record based on the row of data returned by the query. In this case, there is just a single column value, so you could
just as easily have declared l_employee_id as employees.employee_id%TYPE. But whenever you use an explicit cursor, it is best to
declare a record by using %ROWTYPE, so that if the SELECT list of the cursor ever changes, that variable will change with it.
12    Open the cursor, so that rows can now be fetched from the query. Note: This is a step Oracle Database performs with the
SELECT-INTO statement.
14    Start a loop to fetch rows.
15    Fetch the next row for the cursor, and deposit that row’s information into the record specified in the INTO clause.  Note: This is
a step Oracle Database performs with the SELECT-INTO statement.
16    If the FETCH does not find a row, exit the loop.
18    Call assign_bonus, which applies the bonus and also decrements the value of the l_total variable by that bonus amount.
19    Exit the loop if all the bonus funds have been exhausted.
22    Close the cursor.  Note: This is a step Oracle Database performs with the SELECT-INTO statement.
Here are some things to keep in mind when working with explicit cursors:

    If the query does not identify any rows, Oracle Database will not raise NO_DATA_FOUND. Instead, the cursor_name
%NOTFOUND attribute will return TRUE.
    Your query can return more than one row, and Oracle Database will not raise TOO_MANY_ROWS.
    When you declare a cursor in a package (that is, not inside a subprogram of the package) and the cursor is opened, it will stay
open until you explicitly close it or your session is terminated.
    When the cursor is declared in a declaration section (and not in a package), Oracle Database will also automatically close it when
the block in which it is declared terminates. It is still, however, a good idea to explicitly close the cursor yourself. If the cursor is
moved to a package, you will have the now necessary CLOSE already in place. And if it is local, then including a CLOSE statement
will also show other developers and your manager that you are paying attention. 

Using the Cursor FOR Loop

The cursor FOR loop is an elegant and natural extension of the numeric FOR loop in PL/SQL. With a numeric FOR loop, the body of
the loop executes once for every integer value between the low and high values specified in the range. With a cursor FOR loop, the
body of the loop is executed for each row returned by the query.

The following block uses a cursor FOR loop to display the last names of all employees in department 10: 

BEGIN
   FOR employee_rec IN (
        SELECT *
          FROM employees
         WHERE department_id = 10)
   LOOP
      DBMS_OUTPUT.put_line (
         employee_rec.last_name);
   END LOOP;
END;
You can also use a cursor FOR loop with an explicitly declared cursor:

DECLARE
   CURSOR employees_in_10_cur
   IS
      SELECT *
        FROM employees
       WHERE department_id = 10;
BEGIN
   FOR employee_rec 
   IN employees_in_10_cur
   LOOP
      DBMS_OUTPUT.put_line (
         employee_rec.last_name);
   END LOOP;
END;
The nice thing about the cursor FOR loop is that Oracle Database opens the cursor, declares a record by using %ROWTYPE
against the cursor, fetches each row into a record, and then closes the loop when all the rows have been fetched (or the loop
terminates for any other reason).
Best of all, Oracle Database automatically optimizes cursor FOR loops to perform similarly to BULK COLLECT queries. So even
though your code looks as if you are fetching one row at a time, Oracle Database will actually fetch 100 rows at a time—and enable
you to work with each row individually.
Dynamic Queries with EXECUTE IMMEDIATE

Dynamic SQL means that at the time you write (and then compile) your code, you do not have all the information you need for
parsing a SQL statement. Instead, you must wait for runtime to complete the SQL statement and then parse and execute it.

Oracle Database makes it easy to execute SQL statements (and PL/SQL blocks) dynamically with the EXECUTE IMMEDIATE
statement. And querying data is the easiest dynamic SQL operation of all!

You can fetch a single row or multiple rows. Here is a generic function that fetches the value of a numeric column in any table, for
the specified WHERE clause:

CREATE OR REPLACE FUNCTION 


single_number_value (
   table_in    IN VARCHAR2,
   column_in   IN VARCHAR2,
   where_in    IN VARCHAR2)
   RETURN NUMBER
IS
   l_return   NUMBER;
BEGIN
   EXECUTE IMMEDIATE
         'SELECT '
      || column_in
      || ' FROM '
      || table_in
      || ' WHERE '
      || where_in
      INTO l_return;
   RETURN l_return;
END; 
As you can see, instead of SELECT-INTO, I use EXECUTE IMMEDIATE-INTO and construct the SELECT statement from the
arguments passed to the function. Here’s an example of calling the function:

BEGIN
   DBMS_OUTPUT.put_line (
      single_number_value (
                'employees',
                'salary',
                'employee_id=138'));
END; 

As with SELECT-INTO, EXECUTE IMMEDIATE-INTO will raise NO_DATA_FOUND if no rows are found and TOO_MANY_ROWS
if more than one row is found.

You can also use EXECUTE IMMEDIATE to fetch multiple rows of data, which means that you will populate a collection, so you
must use BULK COLLECT. The following is a procedure that will display the values of any numeric column for all rows specified in
the WHERE clause: 

CREATE OR REPLACE PROCEDURE 


show_number_values (
   table_in    IN VARCHAR2,
   column_in   IN VARCHAR2,
   where_in    IN VARCHAR2)
IS
   TYPE values_t IS TABLE OF NUMBER;

   l_values   values_t;
BEGIN
   EXECUTE IMMEDIATE
         'SELECT '
      || column_in
      || ' FROM '
      || table_in
      || ' WHERE '
      || where_in
      BULK COLLECT INTO l_values;

   FOR indx IN 1 .. l_values.COUNT


   LOOP
      DBMS_OUTPUT.put_line(l_values (indx));
   END LOOP;
END; 

And when I call the procedure for the standard employees table 

BEGIN
   show_number_values (
      'employees',
      'salary',
      'department_id = 10 
       order by salary desc');
END; 
I see the following two rows of output: 

4400
3200

A general note of caution regarding dynamic SQL and the preceding examples in particular: whenever you concatenate text to
execute a dynamically executed statement, you run the risk ofSQL injection. This occurs when a malicious user “injects,” or inserts
into the statement, code that changes the behavior of that SQL statement.

Cursor Variables
A cursor variable is, as you might guess from its name, a variable that points to a cursor or a result set. Unlike with an explicit
cursor, you can pass a cursor variable as an argument to a procedure or a function. There are several excellent use cases for
cursor variables, including the following: 

    Pass a cursor variable back to the host environment that called the program unit—the result set can be “consumed” for display or
other processing.
    Construct a result set inside a function, and return a cursor variable to that set. This is especially handy when you need to use
PL/SQL, in addition to SQL, to build the result set.
    Pass a cursor variable to a pipelined table function—a powerful but quite advanced optimization technique. A full explanation of
cursor variables, including the differences between strong and weak REF CURSOR types, is beyond the scope of this article.
    Instead, I will show the basic syntax for working with cursor variables and identify situations in which you might consider using this
feature.
Cursor variables can be used with either embedded (static) or dynamic SQL. Listing 2 includes the names_for function, which
returns a cursor variable that fetches either employee or department names, depending on the argument passed to the function.

Code Listing 2: Block and description of the names_for function, which returns a cursor variable 

1  CREATE OR REPLACE FUNCTION names_for (


 2        name_type_in IN VARCHAR2)
 3     RETURN SYS_REFCURSOR
 4  IS
 5     l_return   SYS_REFCURSOR;
 6  BEGIN
 7     CASE name_type_in
 8        WHEN 'EMP'
 9        THEN
10           OPEN l_return FOR
11                SELECT last_name
12                  FROM employees
13              ORDER BY employee_id;
14        WHEN 'DEPT'
15        THEN
16           OPEN l_return FOR
17                SELECT department_name
18                  FROM departments
19              ORDER BY department_id;
20     END CASE;
21
22     RETURN l_return;
23  END names_for;
Line(s) Description
3    Return a piece of data whose type is SYS_REFCURSOR.
5    Declare a cursor variable to be returned by the function.
7    Use a CASE statement driven by the value of name_type_in to determine which query should be opened.
10–13    Open a cursor variable for a query from the employees table.
16–19    Open a cursor variable for a query from the departments table.

Here is a block that uses the names_for function to display all the names in the departments table: 

DECLARE

l_names SYS_REFCURSOR;

l_name VARCHAR2 (32767);

BEGIN
l_names := names_for('DEPT'); LOOP

FETCH l_names INTO l_name;


DBMS_OUTPUT.put_line (l_name);

EXIT WHEN l_names%NOTFOUND;    


   END LOOP;
   CLOSE l_names;

END;
As you can see, all the information about the query being opened is “hidden” behind the function header. You simply ask to get the
“names for” a given table. The function picks the appropriate SELECT statement, opens the cursor variable for that statement, and
then returns the variable pointing to that result set.

Once the cursor variable has been opened and passed back to the block, I use the same code with a cursor variable that I would
use with an explicit cursor: 

    FETCH from the cursor (variable) INTO one or more variables (I can even FETCH-BULK COLLECT INTO with a cursor variable,
populating a collection with multiple rows)
    Check the %NOTFOUND attribute of the cursor variable to see if I am done fetching all rows
    CLOSE the cursor variable when done 
The OPEN-FOR statement is unique to cursor variables and enables me to specify at runtime, without having to switch to dynamic
SQL, which data set will be fetched through the cursor variable.
Nevertheless, you can use OPEN-FOR with a dynamic SELECT statement. Here is a very simple example: 

CREATE OR REPLACE FUNCTION 


numbers_from (
      query_in IN VARCHAR2)
   RETURN SYS_REFCURSOR
IS
   l_return   SYS_REFCURSOR;
BEGIN
   OPEN l_return FOR query_in;
   RETURN l_return;
END numbers_from; 

And here is a block—virtually identical to the one that calls names_for, above—that displays all the salaries for employees in
department 10: 

DECLARE
  l_salaries   SYS_REFCURSOR;
  l_salary     NUMBER;
BEGIN
  l_salaries :=
    numbers_from (
      'select salary 
        from employees 
       where department_id = 10');
  LOOP
    FETCH l_salaries INTO l_salary;
    EXIT WHEN l_salaries%NOTFOUND;
    DBMS_OUTPUT.put_line (l_salary);
  END LOOP;
  CLOSE l_salaries;
END;
Choosing the Right Way to Query

This article has shown that the PL/SQL language offers many different ways, ranging from the simplest SELECT-INTO implicit query
to the much more complicated cursor variable, to use cursors to fetch data from relational tables into local variables.

Here are some guidelines to help you decide which technique to use: 

    When fetching a single row, use SELECT-INTO or EXECUTE IMMEDIATE-INTO (if your query is dynamic). Do not use an explicit
cursor or a cursor FOR loop.
    When fetching all the rows from a query, use a cursor FOR loop unless the body of the loop executes one or more DML
statements (INSERT, UPDATE, DELETE, or MERGE). In such a case, you will want to switch to BULK COLLECT and FORALL.
    Use an explicit cursor when you need to fetch with BULK COLLECT, but limit the number of rows returned with each fetch.
    Use an explicit cursor when you are fetching multiple rows but might conditionally exit before all rows are fetched.
    Use a cursor variable when the query you are fetching from varies at runtime (but isn’t necessarily dynamic) and especially when
you need to pass a result back to a non-PL/SQL host environment.
    Use EXECUTE IMMEDIATE to query data only when you cannot fully construct the SELECT statement while writing your code.

PL/SQL Table

 a) Objects of type TABLE are called PL/SQL tables.


        b) They are modeled as database tables, but are not same.
        c) PL/SQL tables use a Primary key to give array like access to Rows.
 
Things To Note:
     
i) It is similar to an Array.
ii) It shouls contains two components.
                        * A PRIMARY KEY of datatype BINARY_INTEGER, that Indexes the PL/SQL Table.
                        * A Column of a scalar or Record datatyoe which stores the PL/SQL TABLE elements.
            iii) It can increase dynamically as it is unconstrained.

 CREATING A PL/SQL TABLE:


=========================

         a) There are two steps involved in creating a PL/SQL Table.


                   -> Declare a TABLE Datatype.
                   -> Declare a Variable of that Datatype.

                         Syntax:
                               TYPE TypeName IS TABLE OF
                               { ColumnType | Variable%TYPE
                 | Table.Column%TYPE} [NOT NULL]
                [INDEX BY BINARY_INTEGER];
                              
                               Identifier TypeName;
                      
                         Example:
                               TYPE Ename_Table_TYOE IS TABLE OF Emp.Ename%TYPE INDEX BY BINARY INTEGER;
                               EnameTable Ename_Table_Type;

PL/SQL TABLE STRUCTURE:


          
            PRIMARY KEY              COLUMN
           
                      1                              LATEESH
                      2                              KAVYA
                      3                              SANDEEP
                
               BINARY_INTEGER             SCALAR
  
         a) The number of rows in PL/SQL table can increase dynamically, Hence a PL/SQL table can grow as new rows are added.
         b) PL/SQL tables can have one column and a PRIMARY KEY, neither of which can be named.
         c) The column can belong to any scalar or record datatype, but the PRIMARY KEY must belong to Typr BINARY_INTEGER.
         d) We cannot initialize a PL/SQL table in its declaration.

REFERENCING A PL/SQL TABLE:

         Syntax:
                PL/SQL_Table_Name(Primary_Key_Value);
         a) Primary_Key_Value belongs to type BINARY_INTEGER.
         b) The Primary Key value can be negative Indexing need not start with 1.
         c) The Methods that make PL/SQL Tables easier to use are

                * EXISTS(n)   -> Returns TRUE if the nth element in a PL/SQL table exists.
             * COUNT       -> Returns the number of elements that PL/SQL TABLE currently contains.
                * FIRST and
            LAST  -> Returns the FIRST and Last index numbers in PL/SQL Table. Returns NULL if the PL/SQL table is empty.
                * Prior(n)    -> Returns the Index number that preceeds index n in a PL/SQL Table.
                * NEXT(n)     -> Returns the Index number taht succeeds Index n in a PL/SQL Table.
                * EXTEND(n.i) -> It increases the size of a PL/SQL Table.
                    
                                   i) It appends one NULL element to a PL/SQL Table.
                                  ii) EXTEND(n) appends n NULL elements into a PL/SQL table.
                                 iii) EXTEND(n.i) appends n copies of the ith element to a PL/SQL Table.
                * TRIM       -> It removes one element from the end of a PL/SQL Table.
                * DELETE     -> It removes all elements from a PL/SQL Table.
              
                        DELETE(n), DELET(m,n)
         d) To Reference PL/SQL Table of Records we use
                        -> Table(index).Field
            Example:
                      SQL > DECLARE
                                   TYPE Dept_Table_Type IS TABLE OF Dept.DName%TYPE INDEX BY BINARY_INTEGER;
                                   My_Dept_Table Dept_Table_Type;
                                   V_Count NUMBER(2);
                            BEGIN
                                   SELECT COUNT(*) INTO V_Count FROM DEPT;
                                   FOR MyIndex IN 1...V_Count
                                   LOOP
                                       SELECT Dname INTO My_Dept_Table(MyIndex) FROM DEPT WHERE Deptno=MyIndex*10;
                                   END LOOP;
                                   FOR MyIndex IN 1....V_Count
                                   LOOP
                                       DBMS_OUTPUT_PUTLINE(My_Dept_Table(MyIndex));
                                   ENDLOOP;
                            END;

                      SQL > DECLARE


                                   TYPE Dept_Table_Type IS TABLE OF Dept%ROWTYPE INDEX BY BINARY_INTEGER;
                                   My_Dept_Table Dept_Table_Type;
                                   V_Count NUMBER(2);
                            BEGIN
                                   SELECT COUNT(*) INTO V_Count FROM DEPT;
                                   FOR MyIndex IN 1...V_Count
                                   LOOP
                                       SELECT *INTO My_Dept_Table(MyIndex) FROM DEPT WHERE Deptno=MyIndex*10;
                                   END LOOP;
                                   FOR MyIndex IN 1....V_Count
                                   LOOP
            DBMS_OUTPUT_PUTLINE('Department NUmber'||My_Dept_Table(MyIndex).DeptNO||'Department Name'||
My_Dept_Table(MyIndex).DeptName ||'Department Location'||My_Dept_Table(MyIndex).LOC);
                                   ENDLOOP;
                            END;

HP PL/SQL Interview Questions

1: wt is the diff b/w greast and max ,least and min? Wt is the diff b/w case and decod?
Answer

Greatest/least: we can pass any no. Of exp


Max/min: we can pass one exp that may a column

Case: is a statement function, cannot process null


Decode: is a function, can process null, we can use in
Update statement.

2: how to print * ** *** **** ***** by using sql query?

(may be using script)


Answr

Select lpad('*',rownum,'*') star from person where rownum<6


*
**
***
****
*****

Write a query to filter improper date format following table?


Date
20-apr
22-may-2010
26-jun-2010
Feb-2009
I want the output

Date
22-may-2010
26-jun-2010

Select to date('column_name','dd-mon-yyyy') from table_name;

Write a query to remove  null following table are


Id    name
101   dinesh
Null  jyothi
Null  bharathi
102   suresh
Null  shilpha
103   prakesh
Null   suma

I want the output format like

Id     name
101    dinesh
102    suresh
103    prakesh

Select * from table where id is not null;


Wirte a query  to remove null? Following table are

Col1   col2   col3


Dinesh null   null
Null   suresh  null
Null   null    prakesh
           I want the output like

 Col1    col2    col3


Dinesh  suresh   prkaesh

Select distinct((select col1 from coll where

Col1<>'null')),(select col2 from coll where col2<>'null'),

(select col3 from coll where col3<>'null')


From coll

Select max(col1) col1,max(col2) col2,max(col3) col3


From table;

Write a query   filter the null value data following source?


Name  age
John  30
Smith null
Null  24
Sharp 35
I want output

Name age
John 30
Sharp 35

Select * from test_1 where name is not null and age is not
Null;

 When do we create bitmap indexes


Answer

If there is very less cardinality (i.e. 1% of total column


Values) or distinct values in column then we have to create
Bitmap index for that column like gender column will always
Have 2 values male or female.

Syntex:-

Create bitmap index index_name on table_name(column_name);

A.oltp
B.dss
C.where clause has functions
D.tables that have only one record

Can we use out parameter in function?


Answer

1.yes we can use out parameters in a function.but we have to assign the out parameter  value to the return datatype.
2.but when we use out parameters in function we can't call the function from a select statement

Write a query to genarate target column.please answer me.


Advance thanks.

Src  tgt

Q10  quarter to 2010


Q90  quarter to 1990
Q80  quarter to 1980
Q74  quarter to 1974

Select
'Quarter to '||to_char((to_date('01/01/'||substr(src,2),

'Dd/mm/rrrr')),'rrrr') as "target"
From table

How to call the function and procedure in trigger?


Answer

With the help of call statement..without semicolon

Create or replace trigger tri_call

Begin
Call p1()
End tri_call;

What is different between union and minus?


Answer

Unoin:- this operator returns from all the queries(combined


Through union) but not duplicate record will be display.
Ex- a={1,2,3,4}
    B={2,3,4,5}
Aub={1,2,3,4,5}............

Minus:- this operator displays records which belongs to only


The first query.
Ex:- a={1,2,3,4}
     B= {2,3,5}
A-b={1,4}...................

Can we interchange parameters in procedure while calling


Answer
 If you name the parameter then you can interchange
Otherwise you cannot. Like in the following example the
First case is positional and you cannot interchange. In the
Second one its named and you can interchange.

Regular oradatabase.standproc(102,'ram'); -- positional


Oradatabase.standproc(empno=>102,ename=>'ram'); --named
Oradatabase.standproc(ename=>'ram',empno=>102);

Re: what is 'force view'?


Answer

The view can be  created without base table then the view is
Called force view. The view must be crated with force

Option.

Sql> create or replace view test_view


  2  as
  3  select * from non_existent_table;
Select * from non_existent_table
              *
Error at line 3:
Ora-00942: table or view does not exist

/* hence, the view does not exists */

Sql> select * from test_view;


Select * from test_view
              *
Error at line 1:
Ora-00942: table or view does not exist

/* specifying force creates the view object (albeit with


Errors) */

Sql> create or replace force view test_view


  2  as
  3  select * from non_existent_table;

Warning: view created with compilation errors.

/* trying to select from the view implies it's been created


*/

Sql> select * from test_view;


Select * from test_view
              *
Error at line 1:
Ora-04063: view "orauser.test_view" has errors

/* creating the missing object then allows us to select


From it */

Sql> create table non_existent_table


  2  (
  3    a  varchar2(10)
  4  );

Table created.

Sql> select * from test_view;

No rows selected

What are global temporary tables


Answer
 Global temporary tables are session dependant tables which Could be used as temporary storage for calculations, sorting
Etc. What i mean by session dependant is, the data being Stored in the global temporary table is not written into the   Database or
stored anywhere. Once the session ends (in which The global temporary table is used), the data also vanishes.

However the structure would still be available even after the session is logged out. Also, the structure is available to other sessions
even when one session is using it, but not the data. I.e multiple sessions could use the same global temporary table without
interfering the data. Each session could insert/update/delete their own data into the same Global temporary table as if the table is
available to only that session. Any data inserted in one session is not available to another.
Now, why do we need global temporary tables? Well, imagine a requirement where you need to fetch some data from the database,
do some kind of calculations, aggregations and provide the result set (many records) to a front end. Again, in the front end, you
need to fetch the result set may times, for some purpose. Then you could make use of the Global temporary table. Until the user
gets disconnected from that database session, the data is available for him in the memory.

Global temporary tables belongs to that session only

Create global temporary table test_gbl


( l_number_no number,
  L_char_vc   varchar2(100)
 ) [on commit delete rows]

On commit delete rows:- it's a default one


If any commit will issued that total data of a table will
Losses. But table is exit

To overcome this we have option


On commit preserve rows:-
Means, if commit will issue the data of a table willn't loss
Up to end of the session. Is session ends the data will

Losses

What are nested tables? How will u delete 5 rows from nested

Tables
Answer

Create or replace type addresstype as object (


      Street varchar2(15),
    City   varchar2(15),
     State  char(2),
      Zip    varchar2(5)
    );

Create or replace type nested_table_addresstype as table of

Addresstype;

Create table employee (


      Id         integer primary key,
      First_name varchar2(10),
      Last_name  varchar2(10),
      Addresses  nested_table_addresstype
    )
    Nested table
      Addresses
    Store as
     Nested_addresses;

Insert into employee values (


      1, 'steve', 'brown',
      Nested_table_addresstype(
        Addresstype('2 ave', 'city', 'ma', '12345'),
        Addresstype('4 ave', 'city', 'ca', '54321')
      )
    );

Delete from table (


      Select addresses from employee where id = 1
    ) addr
    Where
      Value(addr) = addresstype(
        '4 ave', 'city', 'ca', '54321'
      );

Re: select top 3 sal from each dept?


Answer
 Select* from(select ename,deptno,sal,row_number()
 Over(partiton by deptno order by sal)num from emp)
Where num<=3
Order by deptno;

Re: what is difference between having and where clause?


Answer

Having clause is used for filtering grouped data and where


Clause is used for filtering rows.

Where clause filter the data before gruop by clause and


Having clause clause filter the data after group by clause

Can you have multiple sps with the same name on a database?
Answer

Both above answers are correct,but situvationd diff


1) we can't create same db obj with same name,
2) we want that go for packages ,with overloading concept

I want to create a materialized view from a left join of 2

Tables. However the following gives me an error:

    Select field1


     From table_1 a
     Left join table_2 b
     On a.field1=b.field2
Ora-12054: cannot set the on commit refresh attribute for

The materialized view

However the following works:

Select field1
 From table_1 a, table_2 b
 Where a.field1=b.field2
Does anyone have any ideas why this is happening.

Thx for the help

Oracle views join materialized


Share|improve this question
There are two conditions that are not satisfied to make that
Materialized view refresh fast. First one is that you did
Not specify the rowid columns of every table involved. And
The second one is an undocumented restriction: ansi-joins
Are not supported.
Here is an example with dept being table_1, alias a and emp

Being table_2, alias b:

Sql> create materialized view log on emp with rowid


  2  /

Materialized view log created.

Sql> create materialized view log on dept with rowid


  2  /

Materialized view log created.

Sql> create materialized view empdept_mv


  2    refresh fast on commit
  3  as
  4  select a.deptno
  5    from dept a
  6         left join emp b on (a.deptno = b.deptno)
  7  /
  From dept a
       *
Error at line 5:
Ora-12054: cannot set the on commit refresh attribute for

The materialized view


That mimics your situation. First add the rowid's:

Sql> create materialized view empdept_mv


  2    refresh fast on commit
  3  as
  4  select a.rowid dept_rowid
  5       , b.rowid emp_rowid
  6       , a.deptno
  7    from dept a
  8         left join emp b on (a.deptno = b.deptno)
  9  /
  From dept a
       *
Error at line 7:
Ora-12054: cannot set the on commit refresh attribute for

The materialized view


Still it cannot fast refresh, because of the ansi joins.

Converting to old-style outer join syntax:

Sql> create materialized view empdept_mv


  2    refresh fast on commit
  3  as
  4  select a.rowid dept_rowid
  5       , b.rowid emp_rowid
  6       , a.deptno
  7    from dept a
  8       , emp b
  9   where a.deptno = b.deptno (+)
 10  /

Materialized view created.


And to prove that it works:

Sql> select * from empdept_mv


  2  /

Dept_rowid         emp_rowid              deptno


------------------ ------------------ ----------
Aaarhmaaeaaaai/aab aaarhlaaeaaaai3aaa         20
Aaarhmaaeaaaai/aac aaarhlaaeaaaai3aab         30
Aaarhmaaeaaaai/aac aaarhlaaeaaaai3aac         30
Aaarhmaaeaaaai/aab aaarhlaaeaaaai3aad         20
Aaarhmaaeaaaai/aac aaarhlaaeaaaai3aae         30
Aaarhmaaeaaaai/aac aaarhlaaeaaaai3aaf          30
Aaarhmaaeaaaai/aaa aaarhlaaeaaaai3aag         10
Aaarhmaaeaaaai/aab aaarhlaaeaaaai3aah         20
Aaarhmaaeaaaai/aaa aaarhlaaeaaaai3aai          10
Aaarhmaaeaaaai/aac aaarhlaaeaaaai3aaj          30
Aaarhmaaeaaaai/aab aaarhlaaeaaaai3aak         20
Aaarhmaaeaaaai/aac aaarhlaaeaaaai3aal          30
Aaarhmaaeaaaai/aab aaarhlaaeaaaai3aam        20
Aaarhmaaeaaaai/aaa aaarhlaaeaaaai3aan         10
Aaarhmaaeaaaai/aad                                       40

15 rows selected.

Sql> insert into dept values (50,'it','utrecht')


  2  /

1 row created.

Sql> commit
  2  /
Commit complete.

Sql> select * from empdept_mv


  2  /

Dept_rowid         emp_rowid              deptno


------------------ ------------------ ----------
Aaarhmaaeaaaai/aab aaarhlaaeaaaai3aaa         20
Aaarhmaaeaaaai/aac aaarhlaaeaaaai3aab         30
Aaarhmaaeaaaai/aac aaarhlaaeaaaai3aac         30
Aaarhmaaeaaaai/aab aaarhlaaeaaaai3aad         20
Aaarhmaaeaaaai/aac aaarhlaaeaaaai3aae         30
Aaarhmaaeaaaai/aac aaarhlaaeaaaai3aaf          30
Aaarhmaaeaaaai/aaa aaarhlaaeaaaai3aag         10
Aaarhmaaeaaaai/aab aaarhlaaeaaaai3aah         20
Aaarhmaaeaaaai/aaa aaarhlaaeaaaai3aai          10
Aaarhmaaeaaaai/aac aaarhlaaeaaaai3aaj          30
Aaarhmaaeaaaai/aab aaarhlaaeaaaai3aak         20
Aaarhmaaeaaaai/aac aaarhlaaeaaaai3aal          30
Aaarhmaaeaaaai/aab aaarhlaaeaaaai3aam        20
Aaarhmaaeaaaai/aaa aaarhlaaeaaaai3aan         10
Aaarhmaaeaaaai/aad                                       40
Aaarhmaaeaaaai7aaa                                       50

You might also like