Professional Documents
Culture Documents
What is PL/SQL?
SET SERVEROUTPUT ON
This command is used to send the output from the server to the screen
SQL> SET SERVEROUTPUT ON
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
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
DECLARE
v_no NUMBER (4) NOT NULL :=10;
BEGIN
dbms_output.put_line (v_no);
END;
/
Output:
10
DECLARE
v_pi CONSTANT NUMBER (5, 2) := 3.14;
BEGIN
dbms_output.put_line (v_pi);
END;
/
Output:
3.14
DECLARE
v_no NUMBER (5) default 10;
BEGIN
dbms_output.put_line (v_no);
END;
/
Output:
10
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
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
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
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
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
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
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
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
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
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
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
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
<<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
DECLARE
-- Single Line Comment
v_no NUMBER (4);
BEGIN
v_no:= 5*6;
dbms_output.put_line (v_no);
END;
/
Output:
30
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
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
DECLARE
v_myage NUMBER := 31;
BEGIN
IF v_myage <11 THEN
dbms_output.put_line (' I am a child ');
END IF;
END;
/
Output:
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
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
Output:
Enter value for grade: A
old 2: v_grade CHAR (1) := UPPER ('&grade');
new 2: v_grade CHAR (1) := UPPER ('A');
Excellent
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
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
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
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
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
BEGIN
FOR I in REVERSE 1..5 LOOP
dbms_output.put_line (I);
END LOOP;
END;
/
Output:
5
4
3
2
1
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
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
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
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
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
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
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:
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
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:
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:
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
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
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
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
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
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
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
Output:
Donald
Douglas
Jennifer
Michael
Pat
Susan
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
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
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:
SALARY
---------
9000
6000
4800
4800
4200
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> ROLLBACK;
Rollback complete.
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;
/
SALARY
-----------------
9000
6000
5799
5799
5199
SQL> rollback;
Rollback complete.
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.
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
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
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
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
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:
-6502
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
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:
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
Table created.
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:
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:
Output:
Procedure created.
Executing a procedure
Output:
SQL> BEGIN
hello_again;
END;
Output:
jobid jobs.job_id%TYPE,
jobtitle jobs.job_title%TYPE
IS
BEGIN
COMMIT;
END add_job;
Executing a Procedure
SQL> SELECT *
FROM jobs
JOB_ID JOB_TITLE MIN_SALARY MAX_SALARY
IT_DBA Database Administrator
jobid IN jobs.job_id%TYPE,
jobtitle IN jobs.job_title%TYPE
IS
BEGIN
UPDATE jobs
END IF;
END upd_job;
Output:
Procedure created.
Executing a Procedure:
JOB_ID JOB_TITLE MIN_SALARY MAX_SALARY
IT_DBA Data Administrator
ERROR at line 1:
ORA-06512: at line 1
Program: 68
jobid jobs.job_id%TYPE
IS
BEGIN
END IF;
END DEL_JOB;
ERROR at line 1:
ORA-06512: at line 1
P_NO IN NUMBER
IS
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);
IS
BEGIN
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)
IS
BEGIN
FROM employees
W HERE employee_id=P_NO;
END p1;
Procedure Created
Program72: Procedure with OUT Parameter (Use Bind variable for OUT Parameter) With Exception Handling
IS
BEGIN
FROM employees
WHERE employee_id=P_NO;
EXCEPTION
END p1;
Note:
Example1:
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
END;
EXEC f1;
Example:2
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 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
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;
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;
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.
1 row selected.
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.
IF-THEN-ELSE
If-then-else
Case
2. Labeled case
3. Searched case
Simple loop
While loop
For loop
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
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;
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.
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.
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.
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
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.
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;
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
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,
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.
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 <= 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.
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:
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:
l_values values_t;
BEGIN
EXECUTE IMMEDIATE
'SELECT '
|| column_in
|| ' FROM '
|| table_in
|| ' WHERE '
|| where_in
BULK COLLECT INTO l_values;
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
Here is a block that uses the names_for function to display all the names in the departments table:
DECLARE
l_names SYS_REFCURSOR;
BEGIN
l_names := names_for('DEPT'); LOOP
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:
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
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;
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;
1: wt is the diff b/w greast and max ,least and min? Wt is the diff b/w case and decod?
Answer
Date
22-may-2010
26-jun-2010
Id name
101 dinesh
102 suresh
103 prakesh
Name age
John 30
Sharp 35
Select * from test_1 where name is not null and age is not
Null;
Syntex:-
A.oltp
B.dss
C.where clause has functions
D.tables that have only one record
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
Src tgt
Select
'Quarter to '||to_char((to_date('01/01/'||substr(src,2),
'Dd/mm/rrrr')),'rrrr') as "target"
From table
Begin
Call p1()
End tri_call;
The view can be created without base table then the view is
Called force view. The view must be crated with force
Option.
Table created.
No rows selected
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.
Losses
What are nested tables? How will u delete 5 rows from nested
Tables
Answer
Addresstype;
Can you have multiple sps with the same name on a database?
Answer
Select field1
From table_1 a, table_2 b
Where a.field1=b.field2
Does anyone have any ideas why this is happening.
15 rows selected.
1 row created.
Sql> commit
2 /
Commit complete.