Beruflich Dokumente
Kultur Dokumente
-----------------------
It is useful for several reasons:
It helps to build a structure that is logical and easy to maintain.
Normalized databases are the industry standard.
Retrieving data will be easier.
First Normal Form means that the database doesn't contain any repeating attribut
es.
Violations of Second Normal Form occur when the table contains attributes that d
epend on a portion of the primary key.
Second Normal Form violations can exist only when you have a multi-column primar
y key.
Third Normal Form violations occur when a transitive dependency exists.
All attributes in entities (columns in tables) must be dependent upon the primar
y key or one of the candidate keys and not on other attributes.
==========================================================================
Simple Queries:
--------------
We can not use group function with where clause.
The WHERE clause first filters the rows,then the remaining rows are grouped into
blocks by the GROUP BY clause,
and finally the row groups are filtered by the HAVING clause.
For example, the following query uses
A WHERE clause to filter the rows from those whose salary is less than $50000
A GROUP BY clause to group the remaining rows by the city column
A HAVING clause to filter the row groups to those whose average salary is greate
r than $30000
select *
from dept
where deptno in
(select deptno from emp);
DEPTNO DNAME LOC
------ -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
select *
from dept
where exists
(select deptno from emp)
/
DEPTNO DNAME LOC
----- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
select *
from dept
a where exists
(select deptno from emp b
where a.deptno=b.deptno);
DEPTNO DNAME LOC
------ -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
select *
from dept
where deptno not in
(select deptno
from emp)
/
DEPTNO DNAME LOC
------ -------------- -------------
40 OPERATIONS BOSTON
select *
from dept
where not exists
(select deptno
from emp)
/
no rows selected
select *
from dept a
where not exists
(select deptno
from emp b
where a.deptno = b.deptno)
/
DEPTNO DNAME LOC
------ -------------- -------------
40 OPERATIONS BOSTON
==> Exists and not exists give only whether the condition is true or false.
Operator Description
---------------------------
UNION ALL Returns all the rows retrieved by the queries, including duplica
te rows.
UNION Returns all non-duplicate rows retrieved by the queries.
INTERSECT Returns rows that are retrieved by both queries.
MINUS Returns the remaining rows when the rows retrieved by the second
query are subtracted from the rows retrieved by the first query.
select *
2 from (select empno, sal
3 from history
4 order by sal desc)
5 where rownum <= 3;
DESC keeps null in the begining and asc keeps null in the end.
--------------------------------------------------------------------------
Multi column
select *
from myTable myTable1, ( select owner, max(last_ddl_time) max_time from my
Table group by owner ) myTable2
where myTable1.owner = myTable2.owner
SELECT * FROM product_order
WHERE (product_name, order_date)
IN (SELECT product_name, MAX(order_date) FROM product_order GROUP BY pro
duct_name);
==========================================================================
insert into Employee(ID, First_Name) values('08','J"a"mes')
08 J"a"mes
INSERT INTO employee (start_date) VALUES (trunc(sysdate));
SQL>
SQL> ALTER TABLE department
2 ADD CONSTRAINT pk_dept PRIMARY KEY (dept_id);
SQL> create table myTable(
2 id number(6) not null,
3 salary number(8,2),
4 hire_date date default sysdate,
5 termination_date date,
6 termination_desc varchar2(4000),
7 constraint emphistory_pk primary key (id, hire_date)
8 );
You can also choose to apply a constraint to new data only by specifying ENABLE
NOVALIDATE.
The default is ENABLE VALIDATE.
SQL> ALTER TABLE myTable
2 ADD CONSTRAINT uq UNIQUE (id) DISABLE;
Table altered.
SQL>
SQL>
SQL> ALTER TABLE myTable
2 DISABLE CONSTRAINT uq;
Table altered.
SQL>
SQL> create force view invalid_view as
2 select * from table_that_does_not_exist;
Warning: View created with compilation errors.
SQL>
SQL> drop view invalid_view;
Materialized Views in Oracle:
----------------------------
A materialized view is a database object that contains the results of a query.
They are local copies of data located remotely, or are used to create summary ta
bles based on aggregations of a table's data.
Materialized views, which store data based on remote tables are also, know as sn
apshots.
A materialized view can query tables, views, and other materialized views.
Collectively these are called master tables (a replication term) or detail table
s (a data warehouse term).
For replication purposes, materialized views allow you to maintain copies of rem
ote data on your local node. These copies are read-only.
If you want to update the local copies, you have to use the Advanced Replication
feature.
You can select data from a materialized view as you would from a table or view
For data warehousing purposes, the materialized views commonly created are aggre
gate views, single-table aggregate views, and join views.
In this article, we shall see how to create a Materialized View and discuss Refr
esh Option of the view.
In replication environments, the materialized views commonly created are primary
key, rowid, and subquery materialized views.
Primary Key Materialized Views
The following statement creates the primary-key materialized view on the table e
mp located on a remote database.
CREATE MATERIALIZED VIEW mv_emp_pk
REFRESH FAST
START WITH SYSDATE
NEXT SYSDATE + 2
WITH PRIMARY KEY
AS SELECT * FROM emp@remote_db;
[refresh [fast|complete|force]
[on demand | commit]
[start with date] [next date]
[with {primary key|rowid}]]
Refresh Method - COMPLETE Clause
The complete refresh re-creates the entire materialized view. If you request a c
omplete refresh, Oracle performs a complete refresh even if a fast refresh is po
ssible.
Refresh Method - FORCE Clause
When you specify a FORCE clause, Oracle will perform a fast refresh if one is po
ssible or a complete refresh otherwise.
If you do not specify a refresh method (FAST, COMPLETE, or FORCE), FORCE is the
default.
PRIMARY KEY and ROWID Clause
WITH PRIMARY KEY is used to create a primary key materialized view i.e. the mate
rialized view is based on the primary key of the master table instead of
ROWID (for ROWID clause). PRIMARY KEY is the default option.
To use the PRIMARY KEY clause you should have defined PRIMARY KEY on the master
table or else you should use ROWID based materialized views.
Rowid materialized views should have a single master table and cannot contain an
y of the following:
Distinct or aggregate functions
GROUP BY Subqueries , Joins & Set operations
Note: When you create a materialized view using the FAST option you will need to
create a view log on the master tables(s) as shown below:
SQL> CREATE MATERIALIZED VIEW LOG ON emp;
MONTHS_BETWEEN
ADD_MONTHS
NEXT_DAY
LAST_DAY
ROUND
TRUNC
Number of months
between two dates
Add calendar months to
date
Next day of the date
specified
Last day of the month
Round date
Truncate date
. MONTHS_BETWEEN (’01-SEP-95’,’11-JAN-94’)
• ADD_MONTHS (’11-JAN-94’,6)
• NEXT_DAY (’01-SEP-95’,’FRIDAY’)
• LAST_DAY(’01-FEB-95’)
19.6774194
’11-JUL-94’
’08-SEP-95’
’28-FEB-95’
• ROUND(SYSDATE,’MONTH’) 01-AUG-95
• ROUND(SYSDATE ,’YEAR’) 01-JAN-96
• TRUNC(SYSDATE ,’MONTH’) 01-JUL-95
• TRUNC(SYSDATE ,’YEAR’) 01-JAN-95
Function Purpose
TO_CHAR(number|date,[ fmt],
[nlsparams])
Date Conversion: The nlsparams parameter
specifies the language in which month and day names
and abbreviations are returned. If this parameter is
omitted, this function uses the default date languages
for the session.
TO_NUMBER(char,[fmt],
[nlsparams])
Converts a character string containing digits to a
number in the format specified by the optional
format model fmt.
The nlsparams parameter has the same purpose
in this function as in the TO_CHAR function for
number conversion.
TO_DATE(char,[fmt],[nlsparams]) Converts a character string representing a date
to a
date value according to the fmt specified. If fmt is omitted, the format is DD-M
ON-YY.
The nlsparams parameter has the same purpose in this function as in the TO_CHAR
function for date conversion.
Using the TO_CHAR Function with Dates
The format model:
• Must be enclosed in single quotation marks and is case sensitive
• Can include any valid date format element
• Has an fm element to remove padded blanks or suppress leading zeros
• Is separated from the date value by a comma
Elements of the Date Format Model:
YYYY
YEAR
MM
MONTH
DY
DAY
DD
Full year in numbers
Year spelled out
Two-digit value for month
Three-letter abbreviation of the
day of the week
Full name of the day of the week
Full name of the month
MON
Three-letter abbreviation of the
month
Numeric day of the month
Current Year
1995
1995
2001
2001
Specified Date
27-OCT-95
27-OCT-17
27-OCT-17
27-OCT-95
RR Format
1995
2017
2017
1995
YY Format
1995
1917
2017
2095
General Functions
These functions work with any data type and pertain
to using nulls.
• NVL (expr1, expr2)
• NVL2 (expr1, expr2, expr3)
• NULLIF (expr1, expr2)
• COALESCE (expr1, expr2, ..., exprn)
NVL Converts a null value to an actual value
NVL2 If expr1 is not null, NVL2 returns expr2. If expr1 is null, NVL2
returns expr3. The argument expr1 can have any data type.
NULLIF Compares two expressions and returns null if they are equal, or the first
expression if they are not equal
COALESCE Returns the first non-null expression in the expression list
DECODE:
SELECT last_name, job_id, salary, DECODE(job_id, ’IT_PROG’, 1.10*salary, ’ST_CLERK’, 1.1
5*salary, ’SA_REP’, 1.20*salary, salary)
REVISED_SALARY
FROM employees;
The Analytical Functions in Oracle (Analytical Functions I)
The analytical functions fall into categories:
------------------------------------------------
ranking,
aggregate,
row comparison,
statistical.
The function has this syntax:
function() OVER()
For example, SELECT RANK() OVER(ORDER BY salary) FROM employee
The part may be empty, as it is in the above example: "RANK()."
The contains an ordering, partitioning, or windowing clause.
The ordering clause in the above example is "OVER(ORDER BY salary)."
An analytical function that uses an ordering may also partition the result set b
ased on some attribute value.
The Row-numbering and Ranking Functions
ROW_NUMBER
RANK
DENSE_RANK
PERCENT_RANK
CUME_DIST
NTILE
You use the ranking functions to calculate ranks, percentiles, and n-tiles.
RANK() returns the rank of items in a group.
RANK() leaves a gap in the sequence of rankings in the event of a tie.
DENSE_RANK() returns the rank of items in a group.
DENSE_RANK() doesn t leave a gap in the sequence of rankings in the event of a t
ie.
CUME_DIST() returns the position of a specified value relative to a group of val
ues.
CUME_DIST() is short for cumulative distribution.
PERCENT_RANK() returns the percent rank of a value relative to a group of values
.
NTILE() returns n-tiles: tertiles, quartiles, and so on.
ROW_NUMBER() returns a number with each row in a group.
select rownum, ename, empno
from (select ename, empno from emp)
/
select empno, ename, sal, rownum
from (select empno, ename, sal from emp order by sal desc)
order by ename
/
The format of an analytical function: function() OVER()
where contains ordering, partitioning, windowing, or some combination.
select rownum, empno, ename, sal
from emp order by sal desc
/
ROWNUM EMPNO ENAME SAL
------ ---------- ---------- ----------
1 7369 TOM
13 7902 FORD
10 7844 TURNER
14 7934 MILLER 21000
12 7900 JAMES 19000
11 7876 ADAMS 18000
9 7839 KING 16000
8 7788 SCOTT 15000
7 7782 CLARK 14000
6 7698 BLAKE 13000
5 7654 MARTIN 12000
ROWNUM EMPNO ENAME SAL
------ ---------- ---------- ----------
4 7566 JONES 11000
3 7521 WARD 10000
2 7499 ALLEN 9000
ROW_NUMBER():
select row_number() over (order by sal desc) row_num, empno, ename, sal
from emp
/
ROW_NUM EMPNO ENAME SAL
--------- ---------- ---------- ----------
1 7369 TOM
2 7902 FORD
3 7844 TURNER
4 7934 MILLER 21000
5 7900 JAMES 19000
6 7876 ADAMS 18000
7 7839 KING 16000
8 7788 SCOTT 15000
9 7782 CLARK 14000
10 7698 BLAKE 13000
11 7654 MARTIN 12000
ROW_NUM EMPNO ENAME SAL
--------- ---------- ---------- ----------
12 7566 JONES 11000
13 7521 WARD 10000
14 7499 ALLEN 9000
select row_number() over (order by sal desc nulls last) row_num, empno, ename, s
al
from emp
/
SELECT
month, SUM(amount) AS month_amount,
LAG(SUM(amount), 1) OVER (ORDER BY month) AS previous_month_amount,
LEAD(SUM(amount), 1) OVER (ORDER BY month) AS next_month_amount
FROM all_sales
WHERE year = 2003
SELECT *
FROM pivot_sales_data
UNPIVOT (
amount FOR month IN (JAN, FEB, MAR, APR)
)
ORDER BY prd_type_id;
===============================================================
GO to statement in PL/SQL
create or replace procedure myproc
as
l_checking boolean := FALSE;
begin
null;
if l_checking
then
dbms_output.put_line('Never here....');
else
dbms_output.put_line('Always here...');
goto end_of_function;
end if;
<<end_of_function>>
dbms_output.put_line('Hello Rohit');
end;
/
================================================================
create or replace function emp_exists
(v_empno in emp.empno%type) return boolean as
v_name emp.ename%type;
begin
select ename into v_name from emp
where empno=v_empno;
if v_name in ('Rohit') then
return false;
else
return true;
end if;
end;
/
declare
cnt pls_integer :=0;
begin
for v_rec in (select empno, ename from emp)
loop
if emp_exists(v_rec.empno) then
dbms_output.put_line(v_rec.empno||' has name '||v_rec.ename);
end if;
cnt := cnt+1;
end loop;
dbms_output.put_line(cnt);
end;
DECLARE
2 test_date DATE;
3 day_of_week VARCHAR2(3);
4 years_ahead INTEGER;
5 BEGIN
6 test_date := TO_DATE('1-Jan-1997','dd-mon-yyyy');
7
8 FOR years_ahead IN 1..10 LOOP
9 day_of_week := TO_CHAR(test_date,'Dy');
10
11 IF day_of_week IN ('Sat','Sun') THEN
12 DBMS_OUTPUT.PUT_LINE(TO_CHAR(test_date,'dd-Mon-yyyy')|| ' A long
weekend!');
13 ELSE
14 DBMS_OUTPUT.PUT_LINE(TO_CHAR(test_date,'dd-Mon-yyyy')|| ' Not a long
weekend.');
15 END IF;
16 test_date := ADD_MONTHS(test_date,12);
17 END LOOP;
18 END;
19 /
1 select case
2 when sal is null then 'NULL'
3 else to_char(sal)
4 end salary
5* from emp
SQL> /
SALARY
----------------------------------------
NULL
9000
10000
11000
12000
13000
14000
15000
16000
NULL
18000
SALARY
----------------------------------------
19000
NULL
21000
DECLARE
2 v_FirstName VARCHAR2(20);
3 v_LastName VARCHAR2(20);
4
5 CURSOR c_Students IS
6 SELECT first_name, last_name FROM employee;
7 BEGIN
8 -- Begin cursor processing.
9 OPEN c_Students;
10 LOOP
11 -- Retreive one row.
12 FETCH c_Students INTO v_FirstName, v_LastName;
13 EXIT WHEN c_Students%NOTFOUND;
14 END LOOP;
15 CLOSE c_Students;
16 END;
17 /
SQL> DECLARE
2
3 year_number PLS_INTEGER := 1992;
4
5 BEGIN
6
7 <<year_loop>>
8 WHILE year_number <= 1995
9 LOOP
10
11 dbms_output.put_line('year = '||year_number);
12
13 <<month_loop>>
14 FOR month_number IN 1 .. 12
15 LOOP
16 dbms_output.put_line('...and month = '||month_number);
17
18 END LOOP month_loop;
19
20 year_number := year_number + 2;
21
22 END LOOP year_loop;
23
24 END;
25 /
FOR Loops:
A FOR loop runs a predetermined number of times.
The syntax for a FOR loop is as follows:
FOR loop_variable IN [REVERSE] lower_bound..upper_bound LOOP
statements
END LOOP;
where
loop_variable specifies the loop variable.
REVERSE specifies that the loop variable value is to be decremented each time th
rough
the loop.
lower_bound specifies the loop's lower bound.
upper_bound specifies the loop's upper bound.
If REVERSE is used, the loop variable is initialized to this upper bound.
SQL> BEGIN
2 <<outerloop>>
3 FOR v_outerloopcounter IN 1..2 LOOP
4 <<innerloop>>
5 FOR v_innerloopcounter IN 1..4 LOOP
6 DBMS_OUTPUT.PUT_LINE('Outer Loop counter is ' ||
7 v_outerloopcounter ||
8 ' Inner Loop counter is ' ||
9 v_innerloopcounter);
10 END LOOP innerloop;
11 END LOOP outerloop;
12 END;
13 /
REVERSE FOR LOOP:
----------------
BEGIN
FOR v_loopcounter IN REVERSE 1..5 LOOP
DBMS_OUTPUT.PUT_LINE('Loop counter is ' || v_loopcounter);
END LOOP;
END;
/
Loop counter is 5
Loop counter is 4
Loop counter is 3
Loop counter is 2
Loop counter is 1
SQL> BEGIN
2 FOR v_loopcounter IN 1..6 LOOP
3 IF MOD(v_loopcounter,2) = 0 THEN
4 DBMS_OUTPUT.PUT_LINE('Loop counter is ' || v_loopcounter);
5 END IF; -- End execution of statements for even counter
6 END LOOP;
7 END;
8 /
Loop counter is 2
Loop counter is 4
Loop counter is 6
WHILE loops check the condition and execute a set of commands.
The loop is repeated until the loop is exited.
A WHILE loop is exactly equivalent to a regular loop with an EXIT WHEN as the fi
rst statement.
The condition is checked before the code in the loop is executed.
If the condition is false, the code in the loop will never be executed.
WHILE loop makes your code a little easier to read than with a normal loop.
declare
i pls_integer :=0;
BEGIN
LOOP
exit when i>5;
DBMS_OUTPUT.PUT_LINE('Loop counter is ' || i);
i:=i+1;
END LOOP;
END;
/
No output will be generated:
DECLARE
v_Counter BINARY_INTEGER;
BEGIN
WHILE v_Counter <= 50 LOOP
INSERT INTO employee (id) VALUES (v_Counter);
v_Counter := v_Counter + 1;
END LOOP;
END;
/
SQL> DECLARE
CURSOR cursorValue IS
SELECT h.product_description,o.company_short_name
FROM company o,product h
WHERE o.product_id =h.product_id
ORDER by 2;
v_company_rec cursorValue%ROWTYPE;
BEGIN
OPEN cursorValue;
FETCH cursorValue INTO v_company_rec;
WHILE (cursorValue%FOUND) LOOP
dbms_output.put_line(rpad(v_company_rec.product_description,20,' ')||' '|
|
rpad(v_company_rec.company_short_name,30,' '));
FETCH cursorValue INTO v_company_rec;
END LOOP;
CLOSE cursorValue;
END;
/
SQL> DECLARE
v_Radius NUMBER := 2;
BEGIN
WHILE TRUE LOOP
DBMS_OUTPUT.PUT_LINE('The Area is ' ||mypi * v_Radius * v_Radius
);
EXIT WHEN v_RADIUS = 10;
v_Radius := v_Radius + 2 ;
END LOOP;
END;
/
=======================================================rohit====================
====================================
CREATE OR REPLACE function letterCounter(myString VARCHAR2)
RETURN NUMBER IS
v_current_position INTEGER := 1;
v_counter NUMBER := 0;
BEGIN
WHILE v_current_position <= LENGTH(myString) LOOP
IF SUBSTR(myString,v_current_position,1) != ' ' THEN
v_counter := v_counter + 1;
ELSE
NULL;
END IF;
v_current_position := v_current_position + 1;
END LOOP;
RETURN v_counter ;
END letterCounter;
/
Function created.
SQL>
SQL> DECLARE
v_MYTEXT VARCHAR2(20) := 'THIS IS A TEST';
BEGIN
DBMS_OUTPUT.PUT_LINE('Total count is ' || letterCounter(v_MYTEXT));
END;
/
Total count is 11
================================================================================
=
Understanding bind variable types:
Bind variables can be of type IN, OUT, or IN OUT.
By default, all parameters are of type IN.
Using an OUT Parameter
declare
2 a NUMBER;
3 b NUMBER:=1;
4 c NUMBER:=2;
5 v_plsql_tx VARCHAR2(2000);
6 begin
7 v_plsql_tx := 'begin ' || ':1:=:2 -:3; ' || 'end;';
8 execute immediate v_plsql_tx using out a, b, c;
9 DBMS_OUTPUT.put_line('a='||a);
10 end;
11 /
a=-1
SQL> create or replace function f_getMax(i_empNo1 VARCHAR,i_empNo2 VARCHAR,i_col
umn_tx VARCHAR2)
2 return VARCHAR2
3 is
4 v_out_tx VARCHAR2(2000);
5 v_sql_tx VARCHAR2(2000);
6 begin
7 v_sql_tx := 'select max('||i_column_tx||')'
|| ' from employee ' || 'where id in (:var01, :var02)';
8 execute immediate v_sql_tx into v_out_tx using i_empNo1, i_empNo2;
9 return v_out_tx;
10 exception
11 when others then return null;
12 end f_getMax;
13 /
Function created.
SQL> declare
2 v_null_nr NUMBER:=NULL;
3 v_where_tx VARCHAR2(2000):='salary>2000';
4 v_sql_tx VARCHAR2(2000);
5 begin
6 v_sql_tx:='update employee ' || ' set salary=:1 ' || 'where '||v_where_
tx;
7 execute immediate v_sql_tx using v_null_nr;
8 end;
9 /
Building DDL on the Fly:
There is no way to place DDL statements
(drop function, create table, and so on) into PL/SQL.
But you can place DDL statements to a dynamic SQL.
Dynamic SQL allows you to do whatever you want with database objects.
All DDL statements always fire implicit commits.
In most cases, procedures and functions that generate DDL should be defined
as autonomous transactions.
SQL> create or replace function f_getEmp_tx(i_empNo VARCHAR, i_column_tx VARCHAR
2)
return VARCHAR2
is
v_out_tx VARCHAR2(2000);
v_sql_tx VARCHAR2(2000);
begin
v_sql_tx := 'select '||i_column_tx|| ' from employee '
|| 'where id='||'''||i_empNo||''';
execute immediate v_sql_tx into v_out_tx;
return v_out_tx;
exception
when others then return null;
end;
/
SQL> select f_getEmp_tx('01', 'first_name') from dual;
SQL> create or replace procedure p_removeDept(i_deptNo VARCHAR) is
2 begin
3 savepoint A;
4 update employee set salary = 2000 where id=i_deptNo;
5 delete from employee where id ='02';
6
7 execute immediate 'drop table employee';
8 exception
9 when others then
10 rollback to savepoint A;
11 raise;
12 end;
13 /
SQL> call p_removeDept('01');
Simple EXECUTE IMMEDIATE:
The EXECUTE IMMEDIATE command can be
a VARCHAR2 variable,
a literal quoted string, or
any string expression.
The code to be executed can be passed as a variable or directly as a string in t
he command.
The string cannot exceed 32K.
The code can be a single SQL command or a large block of PL/SQL.
All PL/SQL blocks passed as a string should have a semicolon at the end,
as shown here: execute immediate 'begin p_test; end;';
All SQL statements passed as a string should not have a semicolon at the end,
as shown here: execute immediate 'select 1 from dual' into a;
CREATE OR REPLACE FUNCTION value_in (varname IN VARCHAR)
2 RETURN VARCHAR2
3 IS
4 retval VARCHAR2(2000);
5 BEGIN
6 EXECUTE IMMEDIATE 'BEGIN :val := ' || varname || '; END;' USING OUT retv
al;
7 RETURN retval;
8 END;
9 /
SQL> variable b varchar2(10);
SQL> exec :b:=10;
PL/SQL procedure successfully completed.
SQL> exec :a:=value_in(:b);
PL/SQL procedure successfully completed.
SQL> print :a;
A
----------------------------------------
10
Use procedure to create an index dynamically:
SQL> CREATE OR REPLACE PROCEDURE runddl (ddl_in in VARCHAR2)
2 AUTHID CURRENT_USER
3 IS
4 BEGIN
5 EXECUTE IMMEDIATE ddl_in;
6 END;
7 /
Procedure created.
SQL>
SQL>
SQL>
SQL> CREATE OR REPLACE PROCEDURE creindx(index_in IN VARCHAR2, tab_in IN VARCHAR
2, col_in IN VARCHAR2)
2 IS
3 DDL_statement VARCHAR2(200):= 'CREATE INDEX ' || index_in || ' ON ' || t
ab_in ||' ( ' || col_in || ')';
4 BEGIN
5 runddl (DDL_statement);
6 END;
7 /
Procedure created.
BULK Collect:
declare
type array is table of varchar2(30) index by binary_integer;
l_data array;
begin
select ename BULK COLLECT into l_data from emp;
for j in 1 .. l_data.count
loop
dbms_output.put_line(l_data(j));
end loop;
end;
/
Fortunately, PL/SQL makes it easy for developers to control the
amount of memory used in a BULK COLLECT operation by using the LIMIT clause.
SQL> declare
2 type text_nt is table of VARCHAR2(256);
3 v_ename_nt text_nt;
4 cursor c_emp is select first_name from employee where id='02';
5 procedure p_print_row is
6 begin
7 if v_eName_nt.count=2 then
8 DBMS_OUTPUT.put_line(v_eName_nt(1)||' '||v_eName_nt(2));
9 elsif v_eName_nt.count=1 then
10 DBMS_OUTPUT.put_line(v_eName_nt(1));
11 end if;
12 end;
13 begin
14 open c_emp;
15 loop
16 fetch c_emp bulk collect into v_eName_nt limit 2;
17 p_print_row;
18 exit when c_emp%NOTFOUND;
19 end loop;
20 close c_emp;
21 end;
22 /
DECLARE
Type cust_rec_t is RECORD
(empl_no number, lastname varchar2(25) );
Type cust_tab_t is table of cust_rec_t
index by binary_integer;
cust_tab cust_tab_t;
BEGIN
UPDATE emp SET sal = '917'
WHERE deptno = '10'
RETURNING empno, ename
BULK COLLECT INTO cust_tab;
-- dbms_output.put_line(cust_tab(1).lastname );
dbms_output.put_line(cust_tab.COUNT );
END;
3
declare
2 type number_array is table of number;
3 type varchar2_array is table of varchar2(20);
4 v1 number_array;
5 v2 varchar2_array;
6
7 begin
8
9 select empl_no, lastname
10 bulk collect into v1, v2
11 from employee;
12
13 for i in 1..v1.LAST loop /* could use COUNT or SQL%rowcount */
14 dbms_output.put_line(v2(i) );
15 end loop;
16 end;
17 /
===============================================================================
=
The FORALL command builds a set of SQL statements and executes all of them at on
ce.
SQL> declare
2 type number_nt is table of VARCHAR(20);
3 v_deptNo_nt number_nt:=number_nt('01','02');
4 begin
5 forall i in v_deptNo_nt.first()..v_deptNo_nt.last()
6 update employee set salary = 0 where id =v_deptNo_nt(i);
7 end;
8 /
================================================================================
==
Autonomous Transactions:
Oracle can suspend a transaction and transfer execution control to an independen
t
child transaction.
This child transaction is called an autonomous transaction.
An autonomous transaction is completely independent of the calling transaction.
An autonomous transaction does not share resources, locks, or any commit
dependencies with the main transaction.
Autonomous transactions can include just as much functionality as any other
database transactions.
Autonomous transactions are useful for creating software components that can be
reused in numerous applications.
One advantage of using an autonomous transaction is that DML can be executed
and committed, even if the main transaction is rolled back.
Setting up the syntax for an autonomous transaction
Autonomous Transaction Syntax
SQL> create or replace procedure p_log_audit
2 (what_tx VARCHAR2, descr_tx VARCHAR2,
3 who_tx VARCHAR2, when_dt DATE)
4 is
5 pragma autonomous_transaction;
6 begin
7 insert into Audit_emp
8 values(audit_seq.nextval, what_tx, descr_tx,
9 who_tx, when_dt);
10 commit;
11 end;
12 /
Procedure created.
SQL>
SQL> create or replace trigger bu_emp
2 before update of salary on employee
3 referencing new as new old as old for each row
4 begin
5 p_log_audit ('update','update of emp.salary', user, SYSDATE);
6 end;
7 /
Trigger created.
Dynamic SQL
SQL> CREATE OR REPLACE PROCEDURE runddl (ddl_in in VARCHAR2)
2 IS
3 cur INTEGER:= DBMS_SQL.OPEN_CURSOR;
4 returnValue INTEGER;
5 BEGIN
6 DBMS_SQL.PARSE (cur, ddl_in, DBMS_SQL.NATIVE);
7 returnValue := DBMS_SQL.EXECUTE (cur);
8 DBMS_SQL.CLOSE_CURSOR (cur);
9 EXCEPTION
10 WHEN OTHERS
11 THEN
12 DBMS_OUTPUT.PUT_LINE (
13 'RunDDL Failure on ' || ddl_in);
14 DBMS_OUTPUT.PUT_LINE (SQLERRM);
15 DBMS_SQL.CLOSE_CURSOR (cur);
16 END;
17 /
SQL> -- Build an anonymous block that will trigger an error.
SQL> DECLARE
2
3 -- Define local exceptioon variable.
4 my_error EXCEPTION;
5
6 BEGIN
7
8
9 RAISE my_error;
10
11 EXCEPTION
12
13 WHEN others THEN
14 dbms_output.put_line('RAISE my_error'||CHR(10)
15 ||'SQLCODE ['||SQLCODE||']'||CHR(10)
16 ||'SQLERRM ['||SQLERRM||']');
17
18 END;
19 /
RAISE my_error
SQLCODE [1]
SQLERRM [User-Defined Exception]
SQL> DECLARE
2
3
4 my_error EXCEPTION;
5 PRAGMA EXCEPTION_INIT(my_error,-20001);
6
7 BEGIN
8
9 -- Raise the exception.
10 RAISE my_error;
11
12 EXCEPTION
13
14 WHEN my_error THEN
15 dbms_output.put_line('RAISE my_error'||CHR(10)
16 ||'SQLCODE ['||SQLCODE||']'||CHR(10)
17 ||'SQLERRM ['||SQLERRM
18 ||'User defined error.]');
19
20 END;
21 /
RAISE my_error
SQLCODE [-20001]
SQLERRM [ORA-20001: User defined error.]
Exceptions:
In PL/SQL, the user can catch certain runtime errors.
Exceptions can be internally defined by Oracle or the user.
Exceptions are used to handle errors that occur in your PL/SQL code.
A PL/SQL block contains an EXCEPTION block to handle exception.
There are three types of exceptions:
Predefined Oracle errors
Undefined Oracle errors
User-defined errors
The different parts of the exception.
Declare the exception.
Raise an exception.
Handle the exception.
An exception has four attributes:
Name provides a short description of the problem.
Type identifies the area of the error.
Exception Code gives a numeric representation of the exception.
Error message provides additional information about the exception.
The predefined divide-by-zero exception has the following values for the attribu
tes:
Name = ZERO_DIVIDE
Type = ORA (from the Oracle engine)
Exception Code = C01476
Error message = divisor is equal to zero
The following is the typical syntax of an exception-handling PL/SQL block.
exception
when exception_1 THEN
statements
when exception_2 THEN
statements
...
exception_1 and exception_2 are the names of the predefined exceptions.
statements is the PL/SQL code that will be executed if the exception name is sat
isfied.
Types of more commonly used Exceptions:
no_data_found Singleton SELECT statement returned no data.
too_many_rows Singleton SELECT statement returned more than one row of data.
invalid_cursor Illegal cursor operation occurred.
value_error Arithmetic, conversion, or truncation error occurred.
invalid_number Conversion of a number to a character string failed.
zero_divide Attempted to divide by zero.
dup_val_on_index Attempted to insert a duplicate value into a column that
has a unique index.
cursor_already_open Attempted to open a cursor that was previously opened.
not_logged_on A database call was made without being logged into Oracle.
transaction_backed_out Usually raised when a remote portion of a transaction is
rolled back.
login_denied Login to Oracle failed.
program_error If PL/SQL encounters an internal problem.
storage_error If PL/SQL runs out of memory or if memory is corrupted.
timeout_on_resource Timeout occurred while Oracle was waiting for a resource
.
others For all of the rest.
Exception Handling:
EXCEPTION
WHEN OTHERS THEN
<statements>
statements is one or more statements that will be processed when the exception o
ccurs.
You could always code a NULL statement if no action is to be taken.
Understanding Different Exception Types:
Error Code Prefix Indicates This Exception Type of Error
ORA Core RDBMS errors
PLS PL/SQL errors
FRM Oracle Forms errors
REP Oracle Reports errors
Note:
BEGIN
DBMS_OUTPUT.PUT_LINE(1 / 0);
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('Division by zero');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('An exception occurred');
END;
/
SQL> /
An exception occurred
BEGIN
DBMS_OUTPUT.PUT_LINE(1 / 0);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('An exception occurred');
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('Division by zero');
END;
/
SQL> /
WHEN OTHERS THEN
*
ERROR at line 4:
ORA-06550: line 4, column 8:
PLS-00370: OTHERS handler must be last among the exception handlers of a block
ORA-06550: line 0, column 0:
PL/SQL: Compilation unit analysis terminated
DECLARE
2 Num_a NUMBER := 6;
3 Num_b NUMBER;
4 BEGIN
5 Num_b := 0;
6 Num_a := Num_a / Num_b;
7 Num_b := 7;
8 dbms_output.put_line(' Value of Num_b ' || Num_b);
9 EXCEPTION
10 WHEN ZERO_DIVIDE THEN
11 DECLARE
12 err_num NUMBER := SQLCODE;
13 err_msg VARCHAR2(512) := SQLERRM;
14 BEGIN
15 dbms_output.put_line('ORA Error Number ' || err_num );
16 dbms_output.put_line('ORA Error message ' || err_msg);
17 dbms_output.put_line(' Value of Num_a is ' || Num_a);
18 dbms_output.put_line(' Value of Num_b is ' || Num_b);
19 END;
20 END;
21 /
ORA Error Number -1476
ORA Error message ORA-01476: divisor is equal to zero
Value of Num_a is 6
Value of Num_b is 0
An example showing continuing program execution after handling exception:
DECLARE
v_descr VARCHAR2(20);
BEGIN
BEGIN
SELECT sal
INTO v_descr
FROM emp
WHERE empno =12;
dbms_output.put_line(v_descr);
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('I am in 1 exception block');
END;
BEGIN
SELECT sal
INTO v_descr
FROM emp
WHERE empno =1;
dbms_output.put_line(v_descr);
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('I am in 2 exception block');
END;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('ERR:An error occurred with info :'||
TO_CHAR(SQLCODE)||' '||SQLERRM);
END;
/
SQL> /
I am in 1 exception block
I am in 2 exception block
The OTHERS Exception Handler:
SQL> DECLARE
2 e_TooManyEmployee EXCEPTION; -- Exception to indicate an error condition
3 BEGIN
4 RAISE e_TooManyEmployee;
5 EXCEPTION
6 WHEN e_TooManyEmployee THEN
7 DBMS_OUTPUT.put_line('e_TooManyEmployee');
8 WHEN OTHERS THEN
9 DBMS_OUTPUT.put_line('OTHERS');
10 END;
11 /
e_TooManyEmployee
PL/SQL procedure successfully completed.
create or replace function f_get_speed
(i_distance in number
,i_time in number)
return number
is
v_out number;
begin
v_out:=i_distance/i_time;
return v_out;
exception
when zero_divide then
return 0;
end;
SQL> select f_get_speed(2,2)
2 from dual;
F_GET_SPEED(2,2)
----------------
1
SQL> select f_get_speed(2,0)
2 from dual;
F_GET_SPEED(2,0)
----------------
0
Raising own exception:
Users can explicitly raise an exception with the RAISE command.
Steps for trapping a user-defined error include the following:
Declare the name for the user exception within the declaration section of the bl
ock.
Raise the exception explicitly within the executable portion of the block using
the RAISE command.
Reference the declared exception with an error-handling routine.
1 DECLARE
2 rohit_exception EXCEPTION;
3 PRAGMA EXCEPTION_INIT(rohit_exception, -20001);
4 BEGIN
5 raise rohit_exception;
6 EXCEPTION
7 WHEN rohit_exception then
8 DBMS_OUTPUT.put_line('sqlcode:'||' '||sqlcode);
9 DBMS_OUTPUT.put_line('sqlerrm:'||' '||sqlerrm);
10* END;
11 /
sqlcode: -20001
sqlerrm: ORA-20001:
Using RAISE_APPLICATION_ERROR:
CREATE OR REPLACE PROCEDURE Register is
2 BEGIN
3 RAISE_APPLICATION_ERROR(-20000, 'Can''t add more Employee');
4
5 EXCEPTION
6 WHEN NO_DATA_FOUND THEN
7 RAISE_APPLICATION_ERROR(-20001, ' doesn''t exist!');
8 END Register;
9 /
DECLARE
v_descr VARCHAR2(20);
BEGIN
BEGIN
SELECT sal
INTO v_descr
FROM emp
WHERE empno =12;
dbms_output.put_line(v_descr);
EXCEPTION WHEN NO_DATA_FOUND THEN
Raise_application_error(-20099, 'I am in 1 exception block');
END;
BEGIN
SELECT sal
INTO v_descr
FROM emp
WHERE empno =1;
dbms_output.put_line(v_descr);
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('I am in 2 exception block');
END;
/* EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('ERR:An error occurred with info :'||
TO_CHAR(SQLCODE)||' '||SQLERRM);
*/END;
/
DECLARE
*
ERROR at line 1:
ORA-20099: I am in 1 exception block
ORA-06512: at line 11
DECLARE
v_descr VARCHAR2(20);
BEGIN
BEGIN
SELECT sal
INTO v_descr
FROM emp
WHERE empno =12;
dbms_output.put_line(v_descr);
EXCEPTION WHEN NO_DATA_FOUND THEN
Raise_application_error(-20099, 'I am in 1 exception block');
END;
BEGIN
SELECT sal
INTO v_descr
FROM emp
WHERE empno =1;
dbms_output.put_line(v_descr);
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('I am in 2 exception block');
END;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('ERR:An error occurred with info :'||
TO_CHAR(SQLCODE)||' '||SQLERRM);
END;
/
ERR:An error occurred with info :-20099 ORA-20099: I am in 1 exception block
PL/SQL procedure successfully completed.
declare
e_increaseTooLarge exception;
pragma exception_init(e_increaseTooLarge, -20001);
begin
raise e_increaseTooLarge;
exception
when e_increaseTooLarge then
DBMS_OUTPUT.put_line(sqlcode||' '||sqlerrm);
end;
/
SQL> /
-20001 ORA-20001:
PL/SQL procedure successfully completed.
===============================================================
calling a function or procedure:
SQL> call f_getDateType(null) into :a;
exec :a:=f_getDateType(sysdate);
================================================================
Use SQL%NOTFOUND after insert statement
SQL> DECLARE
2 quant NUMBER := 20;
3 BEGIN
4 INSERT INTO employee (salary)
5 VALUES (quant);
6 IF (SQL%NOTFOUND)
7 THEN
8 dbms_output.put_line('Insert error?!');
9 END IF;
10 END;
11 /
SQL> BEGIN
2 DELETE FROM employee
3 WHERE first_name = 'junk';
4 IF (SQL%NOTFOUND)
5 THEN
6 dbms_output.put_line('No such employee');
7 END IF;
8 END;
9 /
No such employee
================================================================
A record is a collection of individual fields that represents a row in a table.
These fields are unique and each has its own values.
By using records, you can group like data into one structure and then manipulate
this structure as one entity or logical unit.
You declare a record in the declaration portion of a PL/SQL block, subprogram, o
r package
DECLARE
2 TYPE hrc_company_rec IS RECORD
3 (hrc_company_id NUMBER,
4 product_description VARCHAR2(20),
5 company_short_name VARCHAR2(30));
6 v_example_rec1 hrc_company_rec;
7 v_example_rec2 hrc_company_rec;
8 BEGIN
9 v_example_rec1.hrc_company_id :=1001;
10
11 v_example_rec1.product_description :='C';
12
13 v_example_rec1.company_short_name :='O Inc.';
14
15 v_example_rec2 :=v_example_rec1;
16
17 dbms_output.put_line(to_number(v_example_rec2.hrc_company_id)||' '||
18 v_example_rec2.product_description||' '||
19
20 v_example_rec2.company_short_name);
21 END;
22 /
declare
2 type emp_ty is record (emp_tx VARCHAR2(256),deptNo employee.id%TYPE);
3 v_emp_rty emp_ty;
4 begin
5 select id ||' '||first_Name, id into v_emp_rty from employee where id=
'01';
6 DBMS_OUTPUT.put_line('Emp:'||v_emp_rty.emp_tx||'('||v_emp_rty.deptno||'
)');
7 end;
8 /
declare
2 r_emp employee%ROWTYPE;
3 begin
4 select * into r_emp from employee where id = '01';
5 DBMS_OUTPUT.put_line(r_emp.first_Name);
6 end;
===============================================================
create or replace procedure p_log_audit
2 (what_tx VARCHAR2, descr_tx VARCHAR2,
3 who_tx VARCHAR2, when_dt DATE)
4 is
5 pragma autonomous_transaction;
6 begin
7 insert into Audit_emp
8 values(audit_seq.nextval, what_tx, descr_tx,
9 who_tx, when_dt);
10 commit;
11 end;
12 /
===============================================================
You may follow five steps when using a cursor:
Declare variables to store the column values from the SELECT statement.
Declare the cursor, specifying your SELECT statement.
Open the cursor.
Fetch the rows from the cursor.
Close the cursor.
SQL> DECLARE
2 -- step 1: declare the variables
3 v_id employee. id%TYPE;
4 v_name employee.first_name%TYPE;
5 v_salary employee.salary%TYPE;
6
7 -- step 2: declare the cursor
8 CURSOR cv_employee_cursor IS
9 SELECT id, first_name, salary
10 FROM employee
11 ORDER BY id;
12
13 BEGIN
14
15 -- step 3: open the cursor
16 OPEN cv_employee_cursor;
17
18 LOOP
19
20 -- step 4: fetch the rows from the cursor
21 FETCH cv_employee_cursor
22 INTO v_id, v_name, v_salary;
23
24 -- exit the loop when there are no more rows, as indicated by
25 -- the Boolean variable NOTFOUND (= true when
26 -- there are no more rows)
27 EXIT WHEN cv_employee_cursor%NOTFOUND;
28 -- use DBMS_OUTPUT.PUT_LINE() to display the variables
29 DBMS_OUTPUT.PUT_LINE(
30 'v_id = ' || v_id || ', v_name = ' || v_name ||
31 ', v_salary = ' || v_salary
32 );
33
34 END LOOP;
35
36 -- step 5: close the cursor
37 CLOSE cv_employee_cursor;
38
39 END;
40 /
SQL> DECLARE
2 CURSOR product_cur IS
3 SELECT * FROM product
4 FOR UPDATE OF product_price;
5 BEGIN
6 FOR product_rec IN product_cur
7 LOOP
8 UPDATE product
9 SET product_price = (product_rec.product_price * 0.97)
10 WHERE CURRENT OF product_cur;
11 END LOOP;
12 END;
13 /
Note:
select *
from emp
where ename = 'TOM'
for update of sal
locks only one row where ename = 'TOM'
select *
from emp
for update of sal;
locks all the rows
declare
cursor c is select /*+ FIRST_ROWS(1) */ * from emp order by empno;
l_rec emp%rowtype;
begin
open c;
loop
fetch c into l_rec;
exit when c%notfound;
dbms_output.put_line(l_rec.empno);
end loop;
close c;
end;
cursor example:
--------------
declare
type ridArray is table of rowid index by binary_integer;
type vcArray is table of emp.ename%type index by binary_integer;
l_rids ridArray;
l_names vcArray;
cursor c is select rowid, ename from emp;
begin
open c;
loop
fetch c bulk collect into l_rids, l_names LIMIT 100;
forall i in 1 .. l_rids.count
update emp set ename = upper(l_names(i)) where rowid = l_rids(i);
commit;
exit when c%notfound;
end loop;
close c;
end;
/
declare
type ridArray is table of rowid index by binary_integer;
type vcArray is table of emp.ename%type index by binary_integer;
l_rids ridArray;
l_names vcArray;
cursor c is select rowid, ename from emp;
begin
open c;
loop
fetch c bulk collect into l_rids, l_names LIMIT 100;
forall i in 1 .. l_rids.count
update emp set ename = upper(l_names(i)) where rowid = l_rids(i)
commit;
exit when c%notfound;
end loop;
close c;
end;
/
declare
ridarray rowid ;
vcarray emp.ename%type;
cursor c is select rowid, ename from emp;
begin
open c;
loop
fetch c into ridarray, vcarray;
update emp set ename = upper(vcarray) where rowid = ridarray;
commit;
exit when c%notfound;
end loop;
close c;
end;
/
DECLARE
2
3 v_employeeID employee.id%TYPE;
4 v_FirstName employee.first_name%TYPE;
5 v_LastName employee.last_name%TYPE;
6
7
8 v_city employee.city%TYPE := 'Vancouver';
9
10
11 CURSOR c_employee IS
12 SELECT id, first_name, last_name FROM employee WHERE city = v_city;
13 BEGIN
14
15 OPEN c_employee;
17 LOOP
18
19 FETCH c_employee INTO v_employeeID, v_FirstName, v_LastName;
20 DBMS_OUTPUT.put_line(v_employeeID);
21 DBMS_OUTPUT.put_line(v_FirstName);
22 DBMS_OUTPUT.put_line(v_LastName);
23
24 EXIT WHEN c_employee%NOTFOUND;
25 END LOOP;
26
27
28 CLOSE c_employee;
29 END;
30 /
declare
2 cursor c_empInDept is select * from employee for update of salary;
3 begin
4 for r_emp in c_empInDept loop
5 if r_emp.salary < 5000 then
6 update employee
7 set salary = salary * 1.1
8 where current of c_empInDept;
9 end if;
10 end loop;
11 end;
12 /
Note:
where current of <cursor name> targets only current condition or current value
in cursor and also set locks at only current condition.
CURSOR c_Registeredemployee IS
SELECT *
FROM emp
WHERE empno IN ('10')
for update of sal;
................
................
UPDATE emp
SET sal = sal + v_salary
where current of c_Registeredemployee;
This will target on sal for emp 01.
if we are using where current of we have to use for update
PLS-00404: cursor 'C_REGISTEREDEMPLOYEE' must be declared with FOR UPDATE to
use with CURRENT OF
declare
type ridArray is table of rowid index by binary_integer;
type vcArray is table of emp.ename%type index by binary_integer;
l_rids ridArray;
l_names vcArray;
cursor c is select rowid, ename from emp;
begin
open c;
loop
fetch c bulk collect into l_rids, l_names;-- LIMIT 2;
dbms_output.put_line('======'||l_rids.count);
dbms_output.put_line('======'||l_rids.first);
dbms_output.put_line('======'||l_rids.last);
exit when l_rids.count=0;
end loop;
close c;
end;
we can also use exit when l_rids.count=0; as closing condition.
======15
======1
======15
======0
======
======
declare
type ridArray is table of rowid index by binary_integer;
type vcArray is table of emp.ename%type index by binary_integer;
l_rids ridArray;
l_names vcArray;
cursor c is select rowid, ename from emp;
begin
open c;
loop
fetch c bulk collect into l_rids, l_names LIMIT 2;
dbms_output.put_line('======'||l_rids.count);
dbms_output.put_line('======'||l_rids.first);
dbms_output.put_line('======'||l_rids.last);
exit when l_rids.count=0;
end loop;
close c;
end;
======2
======1
======2
======2
======1
======2
======2
======1
======2
======2
======1
======2
======2
======1
======2
======2
======1
======2
======2
======1
======2
======1
======1
======1
======0
======
======
Limit limits the number of rows being fetch.
Raise no data found exception if cursor is empty:
DECLARE
2 d VARCHAR2(1);
3 no_data_found EXCEPTION;
4
5 CURSOR myCursor IS SELECT dummy FROM dual WHERE 1=2;
6 BEGIN
7 OPEN myCursor;
8 FETCH myCursor INTO d;
9
10 IF d IS NULL
11 THEN
12 RAISE no_data_found;
13 END IF;
14 EXCEPTION
15 WHEN no_data_found
16 THEN
17 DBMS_OUTPUT.PUT_LINE ('Trapped the error!?');
18 END;
19 /
Trapped the error!?
Nested cursor open:
-----------------
SQL> DECLARE
2 CURSOR empCursor IS
3 SELECT *
4 FROM employee;
5 CURSOR cur_department
6 (p_department_num department.department_id%TYPE) IS
7 SELECT department_name
8 FROM department
9 where department_id = p_department_num;
10 lv_department_txt department.department_name%TYPE;
11 BEGIN
12 FOR empCursor_rec IN empCursor LOOP
13 OPEN cur_department(empCursor_rec.department_id);
14 FETCH cur_department INTO lv_department_txt;
15 CLOSE cur_department;
16 END LOOP;
17 END;
18 /
SQL> DECLARE
2 CURSOR c_employee IS
3 SELECT id, first_name, last_name
4 FROM employee
5 WHERE id = '01';
6
7 v_employeeData c_employee%ROWTYPE;
8 BEGIN
9 -- Open the cursor and initialize the active set
10 OPEN c_employee;
11
12 -- Retrieve the first row, to set up for the WHILE loop
13 FETCH c_employee INTO v_employeeData;
14
15 -- Continue looping while there are more rows to fetch
16 WHILE c_employee%FOUND LOOP
17 DBMS_OUTPUT.put_line(v_employeeData.ID);
18 FETCH c_employee INTO v_employeeData;
19 END LOOP;
20
21 -- Free resources used by the cursor
22 CLOSE c_employee;
23
24 -- Commit our work
25 COMMIT;
26 END;
27 /
01
SQL> create or replace function f_delete_nr (i_tab_tx VARCHAR2)
2 return NUMBER
3 is
4 begin
5 execute immediate 'delete from '||i_tab_tx;
6 return SQL%ROWCOUNT;
7 end;
8 /
SQL> begin
2 DBMS_OUTPUT.put_line('Deleted:'||f_delete_nr('EMPLOYEE'));
3 end;
4 /
Deleted:8
declare
type strong_rcty is ref cursor return employee%ROWTYPE;
c_strong_rcty strong_rcty;
DECLARE
2
3 cv_emp SYS_REFCURSOR;
4 v_title BOOKS.TITLE%TYPE;
5 v_emp emp%ROWTYPE;
6 v_counter PLS_INTEGER := 0;
7
8 CURSOR book_cur IS SELECT b.title,CURSOR (SELECT * FROM emp a WHERE a.id
= b.emp1 OR a.id = b.emp2 OR a.id = b.emp3)
9 FROM books b
10 WHERE isbn = '1';
11
12 BEGIN
13
14 DBMS_OUTPUT.ENABLE(1000000);
15
16 OPEN book_cur;
17
18 LOOP
19 FETCH book_cur INTO v_title, cv_emp;
20 EXIT WHEN book_cur%NOTFOUND;
21
22 v_counter := 0;
23
24 DBMS_OUTPUT.PUT_LINE('Title from the main cursor: '||v_title);
25
26 LOOP
27 FETCH cv_emp INTO v_emp;
28 EXIT WHEN cv_emp%NOTFOUND;
29
30 v_counter := v_counter + 1;
31
32 DBMS_OUTPUT.PUT_LINE('emp'||v_counter||': '||v_emp.fname||' '||v_e
mp.lname);
33 END LOOP;
34 END LOOP;
35
36 CLOSE book_cur;
37
38 END;
39 /
CREATE OR REPLACE PROCEDURE authors_sel (cv_results IN OUT SYS_REFCURSOR)
2 IS
3 BEGIN
4 OPEN cv_results FOR
5 SELECT id, first_name, last_name
6 FROM employee;
7 END;
8 /
Procedure created.
SQL>
SQL> VARIABLE x REFCURSOR
SQL> EXEC authors_sel(:x)
SQL> declare
2 c sys_refcursor;
3 type array is table of big_table%rowtype index by binary_integer;
4 l_rec array;
5 begin
6 for i in 1 .. 5
7 loop
8 open c for select * from big_table ORDER BY OWNER;
9 fetch c bulk collect into l_rec limit i*1000;
10 close c;
11 end loop;
12 end;
13 /
SQL> DECLARE
2
3 TYPE book_typ IS REF CURSOR RETURN book%ROWTYPE;
4 cv_book book_typ;
5 v_book book%ROWTYPE;
6
7 BEGIN
8
9 DBMS_OUTPUT.ENABLE(1000000);
10
11 OPEN cv_book FOR SELECT * FROM book WHERE isbn = '1';
12
13 FETCH cv_book INTO v_book;
14
15 DBMS_OUTPUT.PUT_LINE(v_book.title||' is '||v_book.price);
16
17 CLOSE cv_book;
18 END;
19 /
Since Oracle 9i you can use SYS_REFCURSOR as the type for the returning REF_CURS
OR.
/** From Oracle 9 */
create or replace procedure test( p_deptno IN number
, p_cursor OUT SYS_REFCURSOR)
is
begin
open p_cursor FOR
select *
from emp
where deptno = p_deptno;
end test;
/* Strong type */
create or replace procedure test( p_deptno IN number
, p_cursor OUT REFCURSOR_PKG.STRONG
REF_CURSOR)
is
begin
open p_cursor FOR
select *
from emp
where deptno = p_deptno;
end test;
===============================================================
Oracle provides the FOR UPDATE clause in SQL syntax to allow the developer to lo
ck a set of Oracle rows for the duration of a transaction.
The FOR UPDATE clause is generally used in cases where an online system needs to
display a set of row data on a screen and
they need to ensure that the data does not change before the end-user has an opp
ortunity to update the data.
However, the FOR UPDATE clause in SQL will cause the SQL to “hang” if one of the req
uested rows is locked by another user.
If you try to access the rows with the NOWAIT clause, you will get an error mess
age, ORA-00054 Resource busy and acquire with NOWAIT specified.
Essentially, the options were either “wait forever” or “don’t wait. Oracle has added ad
ditional flexibility to the syntax by allowing
the SQL to wait for a pre-defined amount of time for locked rows before aborting
.
In this example we select a student row and wait up to 15 seconds for another se
ssion to release their lock:
select
student_last_name
from
student
where student_id = 12345
FOR UPDATE WAIT 15;
===============================================================
There are three types of collections:
Varrays,Nested tables,Associative arrays (formerly known as index-by tables)
A varray is similar to an array in Java.
You can use a varray to store an ordered set of elements having an index associa
ted with it.
The elements in a varray are of the same type.
A varray has one dimension.
A varray has a maximum size that you set when creating it.
Elements in a varray can only be modified as a whole, not individually.
You can change the size of a varray later.
The elements stored in a varray are stored with the table when the size of the v
array is 4KB or less, otherwise the varray is stored outside of the table.
When a varray is stored with the table, accessing its elements is faster than ac
cessing elements in a nested table.
A nested table is a table that is embedded within another table.
You can insert, update, and delete individual elements in a nested table.
Because you can modify individual elements in a nested table, this makes them mo
re flexible than a varray.
A nested table doesn t have a maximum size, and you can store an arbitrary numbe
r of elements in a nested table.
The elements for nested tables are stored in separate tables.
Associative arrays is formerly known as index-by tables.
An associative array is a set of key and value pairs.
You can get the value from the array using the key (which may be a string) or an
integer.
An associative array is similar to a hash table.
You create a collection type using the SQL DDL CREATE TYPE statement.
Then you can use these types to define columns in a table.
An associative array is a PL/SQL construct, not a SQL construct.
An associative array cannot be stored persistently in a table.
You might be asking yourself why you would want to use collections in the first
place.
After all, using two tables with a foreign key already allows you to model relat
ionships between data.
The answer is that the data stored in the collection may be accessed more rapidl
y by the database than if you were to use two tables instead.
Typically, you ll want to use a collection if you have data that is only used by
one table.
A series of collection methods can be used to determine the size, and the rows p
opulated,
in any collection datatype: index-by tables, VARRAYs, and nested tables. The fol
lowing is a list of the collection methods and their purposes:
EXISTS(row) returns TRUE if the row specified exists.
COUNT returns the number of rows.
FIRST returns the row number of the first populated row.
LAST returns the row number of the last populated row.
PRIOR(row) returns the row number of the last row populated before the row speci
fied.
NEXT(row) returns the row number of the next row populated after the row specifi
ed.
DELETE removes all rows.
DELETE(row) removes the specified row.
DELETE(start_row,end_row) removes all rows between and including the start_row a
nd end_row.
TRIM removes the last row.
TRIM(n) removes the last n rows.
EXTEND adds one row.
EXTEND(n) adds n rows.
EXTEND(n,m) adds n copies of row m.
declare
type VarrayType is varray(size) of ElementType;
...
create or replace type VarrayType is varray(size) of ElementType;
declare
2 type month_va is varray(13) of VARCHAR2(20);
3 v_month_va month_va;
4 v_count_nr number;
5 begin
6 v_month_va:=month_va( January , February , March , April , May , June ,
July , August , September , October , November , December );
7 DBMS_OUTPUT.put_line( Length: ||v_month_va.count);
8
9 v_month_va.extend;
10 v_month_va(v_month_va.last):='Null';
11 DBMS_OUTPUT.put_line('Length:'||v_month_va.count);
12
13 for i in v_month_va.first..v_month_va.last
14 loop
15 select count(*) into v_count_nr from employee
16 where nvl(replace(to_char(start_date,'Month'),' '), 'Null')=v_month
_va(i);
17 DBMS_OUTPUT.put_line(v_month_va(i)||': '||v_count_nr);
19 end loop;
20 end;
21 /
SQL> CREATE OR REPLACE TYPE mem_type IS VARRAY(10) of VARCHAR2(15)
2 /
SQL>
SQL>
SQL> CREATE TABLE club (Name VARCHAR2(10),
2 Address VARCHAR2(20),
3 City VARCHAR2(20),
4 Phone VARCHAR2(8),
5 Members mem_type)
6 /
Defining our type to be a VARRAY with 10 elements, where each element is a vary
ing character string of up to 15 characters.
SQL> CREATE OR REPLACE TYPE mem_type IS VARRAY(10) of VARCHAR2(15)
2 /
SQL> DECLARE
2 CURSOR all_emps IS
3 SELECT *
4 FROM employee
5 ORDER BY first_name;
6
7 TYPE emp_array IS VARRAY(100) OF employee%ROWTYPE;
8
9 emps emp_array;
10 inx1 PLS_INTEGER;
11 inx2 PLS_INTEGER;
12 BEGIN
13 inx1 := 0;
14
15 emps := emp_array ();
16
17 FOR emp IN all_emps LOOP
18 inx1 := inx1 + 1;
19 emps.extend();
20 emps(inx1).id := emp.id;
21 emps(inx1).first_name := emp.first_name;
22 emps(inx1).salary := emp.salary;
23 END LOOP;
24
25 FOR inx2 IN 1..emps.count LOOP
26 DBMS_OUTPUT.PUT_LINE (emps(inx2).id ||' ' || emps(inx2).first_name)
;
27 END LOOP;
28 END;
29 /
SQL> CREATE OR REPLACE FUNCTION vs (vlist club.members%type, sub integer)
2 RETURN VARCHAR2
3 IS
4 BEGIN
5 IF sub <= vlist.last THEN
6 RETURN vlist(sub);
7 END IF;
8 RETURN NULL;
9 END vs;
10 /
The CAST function may also be used with the MULTISET function to perform DML ope
rations on VARRAYs.
MULTISET is the "reverse" of CAST in that MULTISET converts a nonobject set of d
ata to an object set. Suppose we create a new table of names:
DECLARE
2 CURSOR all_emps IS
3 SELECT *
4 FROM employee
5 ORDER BY first_name;
6
7 TYPE emp_table IS TABLE OF employee%ROWTYPE;
8
9 emps emp_table;
10 emps_max PLS_INTEGER;
11 inx1 PLS_INTEGER;
12 BEGIN
13 emps_max := 0;
14
15 emps := emp_table ();
16
17 FOR emp IN all_emps LOOP
18 emps_max := emps_max + 1;
19 emps.extend;
20 emps(emps_max).id := emp.id;
21 emps(emps_max).first_name := emp.first_name;
22 emps(emps_max).salary := emp.salary;
23 END LOOP;
24
25 emps.extend(5,1);
26
27 FOR inx1 IN 1..emps_max+5 LOOP
28 DBMS_OUTPUT.PUT_LINE(emps(inx1).id ||' ' || emps(inx1).first_name);
29 END LOOP;
30 emps.trim(5);
31
32 emps.delete(1);
33
34 DBMS_OUTPUT.PUT_LINE(emps.count);
35
36 FOR inx1 IN 1..emps_max+5 LOOP
37 IF emps.exists(inx1) THEN
38 DBMS_OUTPUT.PUT_LINE (emps(inx1).id ||' ' || emps(inx1).first_n
ame);
39 END IF;
40 END LOOP;
41 END;
42 /
SQL> declare
2 type dept_rty is record(id number, extra_tx VARCHAR2(2000));
3 type dept_aa is table of dept_rty index by binary_integer;
4 v_dept_aa dept_aa;
5 begin
6 v_dept_aa(10).id :=10;
7 v_dept_aa(20).id :=20;
8
9 for i in v_dept_aa.first..v_dept_aa.last loop
10 if v_dept_aa.exists(i) then
11 DBMS_OUTPUT.put_line(v_dept_aa(i).id);
12 end if;
13 end loop;
14 end;
15 /
================================================================================
=
Once you have defined your nested table type, you can use it to define a column
in a table.
The NESTED TABLE clause identifies the name of the nested table column (addresse
s).
The STORE AS clause specifies the name of the actual nested table (nested_addres
ses).
You cannot access the nested table independently of the table in which it is emb
edded.
create table courses
2 ( code VARCHAR2(6)
3 , description VARCHAR2(30)
4 , category CHAR(3)
5 , duration NUMBER(2)) ;
create type errorNumberType as object
2 ( code varchar2(4)
3 , ch number(2)
4 , pg number(3)
5 , txt varchar2(40)
6 ) ;
create type errorNumberTableCollection as table of errorNumberType;
SQL> alter table c
2 add (errata errorNumberTableCollection)
3 nested table errata store as errata_tab;
SQL> update c
2 set errata = errorNumberTableCollection();
SQL>
SQL>
SQL> insert into table ( select errata from c where code = 'SQL' )
2 values ( 'SQL', 3, 45, 'line.' );
SQL> select code, cardinality(errata)
2 from c
3 where errata is not empty;
SQL> update table ( select errata from c where code = 'SQL') e
2 set e.ch = 7;
SQL> CREATE TABLE employee (
2 id INTEGER PRIMARY KEY,
3 first_name VARCHAR2(10),
4 last_name VARCHAR2(10),
5 addresses nested_table_AddressType
6 )
7 NESTED TABLE
8 addresses
9 STORE AS
10 nested_addresses;
Table created.
SQL>
SQL>
SQL> INSERT INTO employee VALUES (
2 1, 'Steve', 'Brown',
3 nested_table_AddressType(
4 AddressType('2 Ave', 'City', 'MA', '12345'),
5 AddressType('4 Ave', 'City', 'CA', '54321')
6 )
7 );
1 row created.
===============================================================
SQL> CREATE OR REPLACE PROCEDURE myProc
2 AS
3 CURSOR ccur IS SELECT first_name, city FROM employee;
4 TYPE nametab IS TABLE OF employee.first_name%type
5 INDEX BY employee.city%type;
6 ch nametab;
7 i integer := 0;
8 imax integer;
9 BEGIN
10 FOR j IN ccur LOOP
11 /* i := i + 1; */
12 ch(j.first_name) := j.first_name;
13 END LOOP;
14 /* imax := i;
15 i := 0;
16 dbms_output.put_line('number of values read: '||imax); */
17 dbms_output.put_line('Name ... '||ch('Vancouver'));
18 END myProc;
19 /
SQL> declare
2 type text_nt is table of VARCHAR2(256);
3 v_ename_nt text_nt;
4 cursor c_emp is select first_name from employee where id='02';
5 procedure p_print_row is
6 begin
7 if v_eName_nt.count=2 then
8 DBMS_OUTPUT.put_line(v_eName_nt(1)||' '||v_eName_nt(2));
9 elsif v_eName_nt.count=1 then
10 DBMS_OUTPUT.put_line(v_eName_nt(1));
11 end if;
12 end;
13 begin
14 open c_emp;
15 loop
16 fetch c_emp bulk collect into v_eName_nt limit 2;
17 p_print_row;
18 exit when c_emp%NOTFOUND;
19 end loop;
20 close c_emp;
21 end;
22 /
Alison
===============================================================
SQL> declare
2 type cust_array_type is table of number
3 index by binary_integer;
4 employee_array cust_array_type;
5 v_index number;
6 begin
7 select empl_no bulk collect
8 into employee_array from employee_history;
9
10 FORALL i IN employee_array.FIRST..employee_array.LAST
11 delete from ord where empl_no = employee_array(i);
12
13 v_index := employee_array.FIRST;
14 for i in employee_array.FIRST..employee_array.LAST loop
15 dbms_output.put_line('delete for employee '
16 ||employee_array(v_index)
17 ||' deleted '
18 ||SQL%BULK_ROWCOUNT(v_index)
19 ||' rows.');
20 v_index := employee_Array.NEXT(v_index);
21 end loop;
22 end;
23 /
===============================================================
BEGIN
12 CASE
13 WHEN salary >= 10000 AND salary <=20000 THEN
14 give_bonus(employee_id, 1500);
15 WHEN salary > 20000 AND salary <= 40000 THEN
16 give_bonus(employee_id, 1000);
17 WHEN salary > 40000 THEN
18 give_bonus(employee_id, 500);
19 ELSE
20 give_bonus(employee_id, 0);
21 END CASE;
22 END;
23 /
==============================================================
select job, ename
2 , decode(greatest(msal,2500),2500,'cheap','expensive') as class
3 from employees
4 where bdate <date '1964-01-01'
5 order by decode(job,'DIRECTOR',1,
6 'MANAGER',2,
7 3);
SQL> select attendee, begindate
2 , case evaluation
3 when 1 then 'bad'
4 when 2 then 'mediocre'
5 when 3 then 'ok'
6 when 4 then 'good'
7 when 5 then 'excellent'
8 else 'not filled in'
9 end
10 from registrations
11 where course = 'SQL';
SQL> DECLARE
2 v_Calc NUMBER := 0;
3 BEGIN
4 WHILE v_Calc >= 10 LOOP
5 v_Calc := v_Calc + 1;
6 DBMS_OUTPUT.PUT_LINE('The value of v_Calc is ' || v_Calc);
7 END LOOP;
8 END;
9 /
SQL> declare
2 v_ind NUMBER;
3 v_current NUMBER;
4 begin
5 v_current:=0; -- should not be null!
6 loop
7 v_ind:=0; -- reset each time
8 loop
9 v_ind:=v_ind+1;
10 DBMS_OUTPUT.put_line(v_current);
11 exit when v_ind=4;
12 end loop;
13 v_current:=v_current+5;
14 exit when v_current=25;
15 end loop;
16 end;
17 /
===============================================================
SQL> DECLARE
2 CURSOR cursorValue(p_product_id NUMBER) IS
3 SELECT h.product_description,o.company_short_name
4 FROM company o,product h
5 WHERE o.product_id =h.product_id
6 AND h.product_id =p_product_id
7 ORDER by 2;
8 v_company_rec cursorValue%ROWTYPE;
9 BEGIN
10 OPEN cursorValue(1);
11
12 LOOP
13 FETCH cursorValue INTO v_company_rec;
14 EXIT WHEN cursorValue%NOTFOUND;
15 dbms_output.put_line(rpad(v_company_rec.product_description,20,' ')||'
'||
16 rpad(v_company_rec.company_short_name,30,' '));
17 END LOOP;
18 CLOSE cursorValue;
19 OPEN cursorValue(2);
20
21 LOOP
22 FETCH cursorValue INTO v_company_rec;
23 EXIT WHEN cursorValue%NOTFOUND;
24 dbms_output.put_line(rpad(v_company_rec.product_description,20,' ')||'
'||
25 rpad(v_company_rec.company_short_name,30,' '));
26 END LOOP;
27 CLOSE cursorValue;
28 END;
29 /
SQL> DECLARE
2 CURSOR cursorValue IS
3 SELECT h.product_description,o.company_short_name
4 FROM company o,product h
5 WHERE o.product_id =h.product_id
6 ORDER by 2;
7 v_company_rec cursorValue%ROWTYPE;
8 BEGIN
9 IF (NOT cursorValue%ISOPEN) THEN
10 OPEN cursorValue;
11 END IF;
12
13 FETCH cursorValue INTO v_company_rec;
14
15 WHILE (cursorValue%FOUND)LOOP
16 dbms_output.put_line(rpad(v_company_rec.product_description,20,' ')||'
'||
17 rpad(v_company_rec.company_short_name,30,' '));
18 FETCH cursorValue INTO v_company_rec;
19 END LOOP;
20 IF (cursorValue%ISOPEN)THEN
21 CLOSE cursorValue;
22 END IF;
23 END;
24 /
SQL> DECLARE
2 CURSOR cursorValue IS
3 SELECT h.product_description,o.company_short_name FROM company o,produc
t h
4 WHERE o.product_id =h.product_id
5 ORDER by 2;
6 num_total_rows NUMBER;
7 BEGIN
8
9 FOR idx IN cursorValue LOOP
10 dbms_output.put_line(rpad(idx.product_description,20,' ')||' '||
11 rpad(idx.company_short_name,30,' '));
12
13 num_total_rows :=cursorValue%ROWCOUNT;
14 END LOOP;
15 IF num_total_rows >0 THEN
16 dbms_output.new_line;
17 dbms_output.put_line('Total Organizations = '||to_char(num_total_rows))
;
18 END IF;
19 END;
20 /
===============================================================
SQL> DECLARE
2 CURSOR c_AllEmployees IS
3 SELECT first_name, last_name
4 FROM employee;
5
6 v_FormattedName VARCHAR2(50);
7
8 /* Function which will return the first and last name
9 concatenated together, separated by a space. */
10 FUNCTION FormatName(p_FirstName IN VARCHAR2,
11 p_LastName IN VARCHAR2)
12 RETURN VARCHAR2 IS
13 BEGIN
14 RETURN p_FirstName || ' ' || p_LastName;
15 END FormatName;
16
17 -- Begin main block.
18 BEGIN
19 FOR v_EmployeeRecord IN c_AllEmployees LOOP
20 v_FormattedName :=
21 FormatName(v_EmployeeRecord.first_name,
22 v_EmployeeRecord.last_name);
23 DBMS_OUTPUT.put_line(v_FormattedName);
24 END LOOP;
25
26 COMMIT;
27 END;
28 /
SQL> CREATE OR REPLACE function exitfunc(myString VARCHAR2)
2 RETURN NUMBER IS
3 v_current_position INTEGER := 1;
4 v_counter NUMBER := 0;
5 BEGIN
6 WHILE v_current_position <= LENGTH(myString) LOOP
7 IF SUBSTR(myString,v_current_position,1) != ' ' THEN
8 v_counter := v_counter + 1;
9 ELSE
10 NULL;
11 END IF;
12 v_current_position := v_current_position + 1;
13 EXIT WHEN SUBSTR(myString,v_current_position,1) = ' ';
14 END LOOP;
15 RETURN v_counter ;
16 END exitfunc;
17 /
If there are no parameters to pass, you can simply call the function without the
parentheses.
To pass actual values, you can use commas as placeholders for parameters.
SQL>
SQL> CREATE OR REPLACE FUNCTION squareme(thenum number)
2 RETURN NUMBER IS
3 BEGIN
4 RETURN thenum * thenum;
5 END squareme;
6 /
Function created.
declare
2
3 function myFunction (myValue in number,myFactor in number) return number
is
4
5 myFactor_to_use number;
6 minimum_wage number := 5;
7 begin
8 if (myFactor is null) or (myFactor < minimum_wage) then
9 myFactor_to_use := minimum_wage;
10 else
11 myFactor_to_use := myFactor;
12 end if;
13
14 return myValue * myFactor_to_use;
15 end;
16 begin
17 dbms_output.put_line(myFunction(40,10));
18 dbms_output.put_line(myFunction(40,2));
19 dbms_output.put_line(myFunction(40,null));
20 end;
21 /
SQL>
================================================================================
==
Private Versus Public Package Objects:
For objects that are declared inside the package body, you are restricted to use
within that package.
Therefore, PL/SQL code outside the package cannot reference any of the variables
that were privately declared within the package.
Any items declared inside the package specification are visible outside the pack
age.
These objects declared in the package specification are called public.
If the variable, constant, or cursor was declared in a package specification or
body, their values persist for the duration of the user's session.
The values are lost when the current user's session terminates or the package is
recompiled.
================================================================================
============
Packages:
Packages encapsulate related functionality into one self-contained unit.
Packages are typically made up of two components: a specification and a body.
The package specification contains information about the package.
The package specification lists the available procedures and functions.
These are potentially available to all database users.
The package specification generally doesn't contain the code.
The package body contains the actual code.
SQL> create or replace package pkg_test1
2 as
3 function getArea (i_rad NUMBER) return NUMBER;
4 procedure p_print (i_str1 VARCHAR2 :='hello',
5 i_str2 VARCHAR2 :='world',
6 i_end VARCHAR2 :='!' );
7 end;
8 /
Package created.
SQL>
SQL> create or replace package body pkg_test1
2 as
3 function getArea (i_rad NUMBER)return NUMBER
4 is
5 v_pi NUMBER:=3.14;
6 begin
7 return v_pi * (i_rad ** 2);
8 end;
9
10 procedure p_print(i_str1 VARCHAR2 :='hello',
11 i_str2 VARCHAR2 :='world',
12 i_end VARCHAR2 :='!' )
13 is
14 begin
15 DBMS_OUTPUT.put_line(i_str1||','||i_str2||i_end);
16 end;
17 end;
18 /
Package body created.
Package State:
A package is always either valid or invalid.
A package is valid if none of its source code or objects it references have been
dropped, replaced, or altered since the package specification was last recompil
ed.
a package is invalid if its source code or any object that it references has bee
n dropped, altered, or replaced since the package specification was last recompi
led.
When a package becomes invalid, Oracle will also make invalid any object that re
ferences the package.
Recompiling Packages:
To recompile a package, use the ALTER PACKAGE command with the compile keyword.
Recompiling a package recompiles all objects defined within the package.
The following examples recompile just the body of a package.
ALTER PACKAGE inventory_pkg compile body
The following statement recompiles the entire package including the body and spe
cification:
ALTER PACKAGE inventory_pkg compile package
Creating a Package Specification:
You create a package specification using the CREATE PACKAGE statement.
The simplified syntax for the CREATE PACKAGE statement is as follows:
CREATE [OR REPLACE] PACKAGE package_name
{IS | AS}
package_specification
END package_name;
package_specification specifies the list of procedures and functions (along with
any variables, type definitions, and cursors) that are available to your packag
e's users.
Creating a Package Body
You create a package body using the CREATE PACKAGE BODY statement.
The simplified syntax for the CREATE PACKAGE BODY statement is as follows:
SQL>
SQL> CREATE [OR REPLACE] PACKAGE BODY package_name
2 {IS | AS}
3 package_body
4 END package_name;
where
package_name specifies the name of the package, which must match the package nam
e previously set in the package specification.
package_body specifies the code for the procedures and functions, along with any
variables and cursors.
===========================================================================
Controlling access to packages
You can do this in one of two ways: a simple command or a wrapper package.
To grant a user rights to access a particular package:
grant execute on package_name to user
You can't grant rights to execute a portion of a package.
Rights must be granted to an entire package.
To revoke grants from a user, use the following command:
revoke execute on package_name from user
===========================================================================
SQL> create or replace package pkg_global is
2 function f_countryUSA_cd return VARCHAR2;
3 end;
4 /
Package created.
SQL> create or replace package body pkg_global is
2 gv_countryUSA_cd VARCHAR2(3) := 'USA';
3
4 function f_countryUSA_cd return VARCHAR2 is
5 begin
6 return gv_countryUSA_cd;
7 end;
8
9 end;
10 /
Package body created.
===========================================================================
package RECURSION
SQL> create package RECURSION is
2 procedure A(p number);
3 procedure B(p number);
4 end;
5 /
The hint NOCOPY is applicable only to OUT and IN OUT types of variables
IN ----> The IN mode is the default mode. It means a formal parameter is read-on
ly.
When you set a formal parameter as read-only, you can’t alter it during the
execution of the subroutine. You can assign a default value to a parameter,
making the parameter optional.
OUT ----> The OUT mode means a formal parameter is write-only. When you set a
formal parameter as write-only, there is no initial physical size allocated
to the variable. You allocate the physical size and value inside your
subroutine. You can’t assign a default value, which would make an OUT
mode formal parameter optional.
IN OUT ----> The IN OUT mode means a formal parameter is read-write. When you
set a formal parameter as read-write, the actual parameter provides the
physical size of the actual parameter. While you can change the contents
of the variable inside the subroutine, you can’t change or exceed the actual
parameter’s allocated size. You can’t assign a default value making an IN
OUT mode parameter optional.
NOCOPY ----> You can override the default behavior of passing copies of variable
s when calling functions
and procedures for local transactions. This means you use fewer resources and ac
tually pass a
reference, not a copy of data.
CREATE OR REPLACE PROCEDURE very_confusing (
arg1 IN VARCHAR2
, arg3 IN OUT NOCOPY VARCHAR2
)
IS
BEGIN
arg1 := Hello ;
DBMS_OUTPUT.put_line ( arg3 assigned, arg1 = || arg1);
arg3 := 'Third value';
DBMS_OUTPUT.put_line ('arg3 assigned, arg1 = ' || arg1);
END;
DECLARE
str VARCHAR2 (100) := 'First value';
BEGIN
DBMS_OUTPUT.put_line ('str before = ' || str);
very_confusing (str, str);
DBMS_OUTPUT.put_line ('str after = ' || str);
END;
str before = First value
arg3 assigned, arg1 = Third value
str after = Third value
SQL> DECLARE
2 PROCEDURE b (caller VARCHAR2); -- This is a forward referencing stub.
3 PROCEDURE a (caller VARCHAR2) IS
4 procedure_name VARCHAR2(1) := 'A';
5 BEGIN
6 dbms_output.put_line('Procedure "A" called by ['||caller||']');
7 b(procedure_name);
8 END;
9 PROCEDURE b (caller VARCHAR2) IS
10 procedure_name VARCHAR2(1) := 'B';
11 BEGIN
12 dbms_output.put_line('Procedure "B" called by ['||caller||']');
13 END;
14 BEGIN
15 a('Main');
16 END;
17 /
Procedure "A" called by [Main]
Procedure "B" called by [A]
SQL> create or replace procedure p_print (i_string in VARCHAR2,i_replace in VAR
CHAR2 := 'new') is
2 begin
3 if i_string is null then
4 return;
5 end if;
6 DBMS_OUTPUT.put_line(replace(i_string,'<in>', i_replace));
7 end;
8 /
SQL> CREATE OR REPLACE PACKAGE EmployeePackage AS
2 PROCEDURE AddEmployee(p_EmployeeID IN Employee.id%TYPE,
3 p_first_name IN Employee.first_name%TYPE,
4 p_last_name IN Employee.last_name%TYPE);
5
6 PROCEDURE AddEmployee(p_FirstName IN Employee.first_name%TYPE,
7 p_LastName IN Employee.last_name%TYPE,
8 p_city IN Employee.city%TYPE);
9
10 END EmployeePackage;
11 /
Package created.
SQL>
SQL> CREATE OR REPLACE PACKAGE BODY EmployeePackage AS
2
3 PROCEDURE AddEmployee(p_EmployeeID IN Employee.id%TYPE,
4 p_first_name IN Employee.first_name%TYPE,
5 p_last_name IN Employee.last_name%TYPE) IS
6 BEGIN
7 INSERT INTO Employee (id, first_name, last_name)
8 VALUES (p_EmployeeID, p_first_name, p_last_name);
9 COMMIT;
10 END AddEmployee;
11
12 -- Add a new student by name, rather than ID.
13 PROCEDURE AddEmployee(p_FirstName IN Employee.first_name%TYPE,
14 p_LastName IN Employee.last_name%TYPE,
15 p_city IN Employee.city%TYPE) IS
16 v_EmployeeID Employee.ID%TYPE;
17 BEGIN
18 SELECT ID
19 INTO v_EmployeeID
20 FROM Employee
21 WHERE first_name = p_FirstName
22 AND last_name = p_LastName;
23
24 -- Now we can add the student by ID.
25 INSERT INTO Employee (id, first_name, city)
26 VALUES (v_EmployeeID, p_firstname, p_city);
27 COMMIT;
28 END AddEmployee;
29
30 END EmployeePackage;
31 /
You can do this in one of two ways: a simple command or a wrapper package.
To grant a user rights to access a particular package:
grant execute on package_name to user
You can't grant rights to execute a portion of a package.
Rights must be granted to an entire package.
To revoke grants from a user, use the following command:
revoke execute on package_name from user
SQL> DECLARE
2 v_descr VARCHAR2(20);
3 BEGIN
4 SELECT product_description
5 INTO v_descr
6 FROM product
7 WHERE product_id =10;
8 dbms_output.put_line(v_descr);
9 EXCEPTION
10 WHEN NO_DATA_FOUND THEN
11 dbms_output.put_line('ERR:Invalid Hierarchy Code 10');
12 WHEN OTHERS THEN
13 dbms_output.put_line('ERR:An error occurred with info :'||
14 TO_CHAR(SQLCODE)||' '||SQLERRM);
15 END;
16 /
SQL> DECLARE
2 e_MissingNull EXCEPTION;
3 PRAGMA EXCEPTION_INIT(e_MissingNull, -1400);
4 BEGIN
5 INSERT INTO Employee (id) VALUES (NULL);
6 EXCEPTION
7 WHEN e_MissingNull then
8 DBMS_OUTPUT.put_line('ORA-1400 occurred');
9 END;
10 /
ORA-1400 occurred
==============================================================
There are four types of database triggers:
Table-level triggers can initiate activity before or after an INSERT, UPDATE, or
DELETE event.
View-level triggers defines what can be done to the view.
Database-level triggers can be activated at startup and shutdown of a database.
Session-level triggers can be used to store specific information.
A trigger is a procedure that is run automatically by the database when a specif
ied SQL DML INSERT, UPDATE, or DELETE statement is run against a table.
Triggers are useful for doing things like advanced auditing of changes made to c
olumn values in a table.
When a Trigger Runs
A trigger can fire before or after the SQL statement runs.
A trigger can may be run once for every row affected. Such a trigger is known as
a row-level trigger.
A trigger can may be run for all the rows. Such trigger is known as a statement-
level trigger.
A row-level trigger has access to the old and new column values when the trigger
fires as a result of an UPDATE statement on that column.
The firing of a trigger may also be limited using a trigger condition.
Different events may fire a trigger, but these events are always divided into th
ree groups:
DML triggers,
INSTEAD OF triggers, and
system event triggers.
DML triggers are the triggers on INSERT/UPDATE/DELETE operations in any table.
Statement-level triggers
------------------------
Use statement-level triggers when you need to check business rules that are not
row dependent.
SQL> create or replace trigger emp_bid
2 before insert or delete
3 on employee
4 referencing new as new old as old
5 begin
6 if to_char(sysdate,'Dy') in ('Sat','Sun') then
7 raise_application_error(-20999,'No create/delete employees on weeke
nd!');
8 end if;
9 end;
10 /
Trigger created.
Mutating trigger:
----------------
CREATE OR REPLACE TRIGGER LimitSalary
2 BEFORE INSERT OR UPDATE OF salary ON employee
3 FOR EACH ROW
4 DECLARE
5 v_MaxSalary CONSTANT NUMBER := 2000;
6 v_CurrentSalary NUMBER;
7 BEGIN
8 SELECT salary
9 INTO v_CurrentSalary
10 FROM employee
11 WHERE id = :new.id;
12
13 IF v_CurrentSalary > v_MaxSalary THEN
14 RAISE_APPLICATION_ERROR(-20000, 'Too high in salary ' || :new.id);
15 END IF;
16 END LimitSalary;
17 /
Mutating Table Exceptions
Mutating table exceptions occur when we try to reference the triggering table in
a query from within row-level trigger code.
In this article I'll present and example of how a mutating table exception might
occur and a simple method to get round it.
A mutating trigger occurs in Oracle when the table that originally fired the tri
ggering event is being accessed in the body of the
trigger code, directly or implicitly in a procedure, or in a nested trigger call
ed from the trigger body.
This action is forbidden because the table is in middle of a transaction, and re
ferencing the same table again in the middle of the updating action
causes the trigger to mutate.
System triggers:
---------------
There are a number of events where you can set system triggers such as
ON LOGON, ON LOGOFF, ON STARTUP, ON DROP, ON TRUNCATE, and so on.
You can even track when any DDL command (CREATE, DROP, ALTER, and so on) was exe
cuted in the database.
You may place system triggers at the database level or schema level.
At the database level, triggers fire for each event for all users.
At the schema level, triggers fire for each event for a specific user.
You create a trigger using the CREATE TRIGGER statement.
The simplified syntax for the CREATE TRIGGER statement is as follows:
CREATE [OR REPLACE] TRIGGER trigger_name
{BEFORE | AFTER | INSTEAD OF} trigger_event
ON table_name
[FOR EACH ROW [WHEN trigger_condition]]
BEGIN
trigger_body
END trigger_name;
where
OR REPLACE specifies the trigger is to replace an existing trigger if present.
BEFORE specifies the trigger fires before the triggering event is performed.
AFTER specifies the trigger fires after the triggering event is performed.
INSTEAD OF specifies the trigger fires instead of performing the triggering even
t.
trigger_event specifies the event that causes the trigger to fire.
table_name specifies the table that the trigger references.
FOR EACH ROW specifies the trigger is a row-level trigger.
A row-level trigger is run for each row when the trigger fires.
If you omit FOR EACH ROW, the trigger is a statement-level trigger.
A statement-level trigger is run once when the trigger fires regardless of the n
umber of rows affected.
trigger_condition specifies a Boolean condition that limits when a trigger actua
lly runs its code.
trigger_body contains the SQL and PL/SQL statements that perform the trigger's t
ask.
SQL>
SQL> CREATE DIRECTORY bfile_dir AS 'c:\proj';
SQL>
SQL> DECLARE
2 bfileValue BFILE;
3 BEGIN
4 INSERT INTO myBFile VALUES (1,BFILENAME('BFILE_DIR','test.bmp'));
5 SELECT bfile_data
6 INTO bfileValue
7 FROM myBFile
8 WHERE id = 1;
9
10 END;
11 /
create or replace
2 function trace_file_contents( p_filename in varchar2 )
3 return vcArray
4 pipelined
5 as
6 l_bfile bfile := bfilename('UDUMP_DIR',p_filename);
7 l_last number := 1;
8 l_current number;
9 begin
10 select rownum into l_current
11 from user_avail_trace_files
12 where filename = p_filename;
13
14 dbms_lob.fileopen( l_bfile );
15 loop
16 l_current := dbms_lob.instr( l_bfile, '0A', l_last, 1 );
17 exit when (nvl(l_current,0) = 0);
18 pipe row(utl_raw.cast_to_varchar2(dbms_lob.substr( l_bfile, l_curre
nt-l_last+1,l_last ) ));
19 l_last := l_current+1;
20 end loop;
21 dbms_lob.fileclose(l_bfile);
22 return;
23 end;
SQL>
SQL> DECLARE
2 bfileValue BFILE;
3 BEGIN
4 INSERT INTO myBFile VALUES (1,BFILENAME('BFILE_DIR','test.bmp'));
5 SELECT bfile_data
6 INTO bfileValue
7 FROM myBFile
8 WHERE id = 1;
9
10 END;
11 /
SQL> CREATE TABLE facebook (
2 name VARCHAR2(80),
3 photo BLOB,
4 directions CLOB,
5 description NCLOB,
6 web_page BFILE);
Table created.
SQL>
SQL> CREATE DIRECTORY bfile_data AS 'c:\xxx';
Directory created.
SQL>
SQL>
SQL> DECLARE
2 web_page BFILE;
3 BEGIN
4 DELETE FROM facebook WHERE name='Tannery Falls';
5
6 web_page := BFILENAME('BFILE_DATA','Tannery Falls.htm');
7
8 INSERT INTO facebook (name, web_page) VALUES ('Tannery Falls',web_page);
9 END;
10 /
SQL> CREATE TABLE waterfalls (
2 falls_name VARCHAR2(80),
3 falls_photo BLOB,
4 falls_directions CLOB,
5 falls_description NCLOB,
6 falls_web_page BFILE);
Table created.
SQL>
SQL> CREATE DIRECTORY bfile_data AS 'c:\xxx';
Directory created.
SQL>
SQL> DECLARE
2 web_page BFILE;
3 html RAW(60);
4 amount BINARY_INTEGER := 60;
5 offset INTEGER := 1;
6 BEGIN
7
8 SELECT falls_web_page
9 INTO web_page
10 FROM waterfalls
11 WHERE falls_name='Tannery Falls';
12
13 DBMS_LOB.OPEN(web_page);
14 DBMS_LOB.READ(web_page, amount, offset, html);
15 DBMS_LOB.CLOSE(web_page);
16
17 DBMS_OUTPUT.PUT_LINE(RAWTOHEX(html));
18
19 DBMS_OUTPUT.PUT_LINE(UTL_RAW.CAST_TO_VARCHAR2(html));
20 END;
Use a BFILE to load a LOB column
SQL>
SQL> CREATE TABLE facebook (
2 name VARCHAR2(80),
3 photo BLOB,
4 directions CLOB,
5 description NCLOB,
6 web_page BFILE);
Table created.
SQL>
SQL> CREATE DIRECTORY bfile_data AS 'c:\xxx';
Directory created.
SQL>
SQL>
SQL> DECLARE
2 myBFile BFILE := BFILENAME('BFILE_DATA','TanneryFalls.directions');
3 directions CLOB;
4 destination_offset INTEGER := 1;
5 source_offset INTEGER := 1;
6 language_context INTEGER := DBMS_LOB.default_lang_ctx;
7 warning_message INTEGER;
8 BEGIN
9
10 DELETE FROM facebook WHERE name='Falls';
11
12 INSERT INTO facebook (name,directions)VALUES ('Falls',EMPTY_CLOB());
13
14 SELECT directions INTO directions FROM facebook WHERE name='Falls';
15
16 DBMS_LOB.OPEN(directions, DBMS_LOB.LOB_READWRITE);
17 DBMS_LOB.OPEN(myBFile);
18
19 DBMS_LOB.LOADCLOBFROMFILE(directions, myBFile,
20 DBMS_LOB.LOBMAXSIZE,
21 destination_offset, source_offset,
22 NLS_CHARSET_ID('US7ASCII'),
23 language_context, warning_message);
24
25 IF warning_message = DBMS_LOB.WARN_INCONVERTIBLE_CHAR THEN
26 dbms_output.put_line('Warning! Some characters couldn''t be convert
ed.');
27 END IF;
28
29 DBMS_LOB.CLOSE(directions);
30 DBMS_LOB.CLOSE(myBFile);
31 END;
32 /
SQL> CREATE OR REPLACE PROCEDURE initClob(clob_par IN OUT CLOB,id_par IN INTEGER
) IS
2 BEGIN
3 SELECT clob_column INTO clob_par FROM myTable WHERE id = id_par;
4 END initClob;
5 /
Procedure created.
SQL>
SQL>
SQL> CREATE OR REPLACE PROCEDURE readClob(id_par IN INTEGER) IS
2 clob_var CLOB;
3 char_buffer_var VARCHAR2(50);
4 offset INTEGER := 1;
5 amount_var INTEGER := 50;
6 BEGIN
7 initClob(clob_var, id_par);
8 DBMS_LOB.READ(clob_var, amount_var, offset, char_buffer_var);
9 DBMS_OUTPUT.PUT_LINE('char_buffer_var = ' || char_buffer_var);
10 DBMS_OUTPUT.PUT_LINE('amount_var = ' || amount_var);
11 END readClob;
12 /
SQL> INSERT INTO myTable(id, clob_column) VALUES (1, EMPTY_CLOB());
1 row created.
SQL>
SQL>
SQL> UPDATE myTable
2 SET clob_column = 'AAAAA'
3 WHERE id = 1;
SQL> declare
2 v_file_bf BFILE;
3 v_manual_cl CLOB;
4 lang_ctx NUMBER := DBMS_LOB.default_lang_ctx;
5 charset_id NUMBER := 0;
6 src_offset NUMBER := 1;
7 dst_offset NUMBER := 1;
8 warning NUMBER;
9 begin
10 update catalog set manual_cl = EMPTY_CLOB() where id = 1;
11
12 select mastertxt_bf, manual_cl into v_file_bf, v_manual_cl from catalog
where id = 1;
13
14 DBMS_LOB.fileopen(v_file_bf, DBMS_LOB.file_readonly);
15 DBMS_LOB.loadclobfromfile (v_manual_cl, v_file_bf,DBMS_LOB.getlength (v
_file_bf),
16 src_offset, dst_offset,charset_id, lang_ctx,war
ning);
17 DBMS_LOB.fileclose (v_file_bf);
18 end;
19 /
SQL> declare
2 v_manual_cl CLOB;
3 v_nr NUMBER;
4 v_tx VARCHAR2 (2000);
5 v_add_tx VARCHAR2 (2000):='Loaded: '||TO_CHAR(SYSDATE,'mm/dd/yyyy
hh24:mi');
6 begin
7 select manual_cl into v_manual_cl from catalog where id = 1
8 for update;
9
10 DBMS_LOB.writeappend (v_manual_cl,LENGTH (v_add_tx), v_add_tx);
11
12 v_nr := INSTR (v_manual_cl, 'Loaded:', -1);
13 v_tx := SUBSTR (v_manual_cl, v_nr);
14 DBMS_OUTPUT.put_line (v_tx);
15 end;
SQL> DECLARE
2 web_page BFILE;
3 html RAW(60);
4 amount BINARY_INTEGER := 60;
5 offset INTEGER := 1;
6 BEGIN
7
8 SELECT falls_web_page
9 INTO web_page
10 FROM waterfalls
11 WHERE falls_name='Tannery Falls';
12
13 DBMS_LOB.OPEN(web_page);
14 DBMS_LOB.READ(web_page, amount, offset, html);
15 DBMS_LOB.CLOSE(web_page);
16
17 DBMS_OUTPUT.PUT_LINE(RAWTOHEX(html));
18
19 DBMS_OUTPUT.PUT_LINE(UTL_RAW.CAST_TO_VARCHAR2(html));
20 END;
21 /
SQL> CREATE OR REPLACE PROCEDURE initClob(clob_par IN OUT CLOB,id_par IN INTEGE
R) IS
2 BEGIN
3 SELECT clobData INTO clob_par FROM myTable WHERE id = id_par;
4 END initClob;
5 /
SQL> declare
2 clob_pointer CLOB;
3 v_buf VARCHAR2(1000);
4 Amount BINARY_INTEGER :=1000;
5 Position INTEGER :=1;
6 BEGIN
7 v_buf :=rpad('A',1000,'A');
8
9 insert into myClob values (1 ,EMPTY_CLOB());
10
11 commit;
12
13 SELECT clob_data INTO clob_pointer FROM myClob WHERE id = 1 FOR UPDATE;
14 DBMS_LOB.OPEN (clob_pointer,DBMS_LOB.LOB_READWRITE);
15
16 FOR i IN 1..500 LOOP
17
18 DBMS_LOB.WRITE (clob_pointer,Amount,Position,v_buf);
19
20 Position :=Position +Amount;
21
22 END LOOP;
23
24 DBMS_LOB.CLOSE (clob_pointer);
25
26 END;
27 /
DECLARE
2 bfile_pointer BFILE;
3 blob_pointer BLOB;
4 bfile_offset NUMBER :=1;
5 blob_offset NUMBER :=1;
6 tot_len INTEGER;
7 BEGIN
8
9 INSERT INTO myBlob VALUES (1,EMPTY_BLOB());
10
11 SELECT blob_data INTO blob_pointer FROM myBlob WHERE id = 1 FOR UPDATE;
12
13 bfile_pointer :=bfilename('BFILE_DIR','test.bmp');
14
15 dbms_lob.fileopen(bfile_pointer,dbms_lob.file_readonly);
16
17 dbms_lob.OPEN(blob_pointer,dbms_lob.lob_readwrite);
18
19 dbms_lob.LOADBLOBFROMFILE(blob_pointer,bfile_pointer,dbms_lob.lobmaxsize,
bfile_offset,blob_offset);
20
21 tot_len :=DBMS_LOB.GETLENGTH(blob_pointer);
22
23 dbms_lob.close(blob_pointer);
24
25 dbms_lob.fileclose(bfile_pointer);
26
27 DBMS_OUTPUT.PUT_LINE(TO_CHAR(tot_len));
28 END;
29 /
SQL> CREATE OR REPLACE PROCEDURE initClob(clob_par IN OUT CLOB,id_par IN INTEGER
) IS
2 BEGIN
3 SELECT clobData INTO clob_par FROM myTable WHERE id = id_par;
4 END initClob;
5 /
Procedure created.
SQL>
SQL>
SQL> CREATE OR REPLACE PROCEDURE readClob(id_par IN INTEGER) IS
2 clobVariable CLOB;
3 charVariable VARCHAR2(50);
4 offsetPos INTEGER := 1;
5 amount_var INTEGER := 50;
6 BEGIN
7 initClob(clobVariable, id_par);
8 DBMS_LOB.READ(clobVariable, amount_var, offsetPos, charVariable);
9 DBMS_OUTPUT.PUT_LINE('charVariable = ' || charVariable);
10 DBMS_OUTPUT.PUT_LINE('amount_var = ' || amount_var);
11 END readClob;
12 /
Procedure created.
SQL>
SQL> CREATE OR REPLACE PROCEDURE copy_example IS
2 clobSrc CLOB;
3 clobDest CLOB;
4 src_offsetPos INTEGER := 1;
5 dest_offsetPos INTEGER := 7;
6 amount_var INTEGER := 5;
7 BEGIN
8 SELECT clobData INTO clobSrc FROM myTable WHERE id = 2;
9 SELECT clobData INTO clobDest FROM myTable WHERE id = 1 FOR UPDATE;
10
11 readClob(1);
12 DBMS_LOB.COPY(clobDest, clobSrc, amount_var,dest_offsetPos, src_offsetPos
);
13 readClob(1);
14
15 ROLLBACK;
16 END copy_example;
17 /
SQL> declare
2
3 c1 clob := to_clob('abc');
4 c2 clob;
5
6 begin
7 case c1
8 when to_clob('abc') then dbms_output.put_line('abc');
9 when to_clob('def') then dbms_output.put_line('def');
10 end case;
11
12 c2 := case c1
13 when to_clob('abc') then 'abc'
14 when to_clob('def') then 'def'
15 end;
16
17 dbms_output.put_line(c2);
18
19 end;
20 /
abc
abc
QL> declare
2 clob_pointer CLOB;
3 v_buf VARCHAR2(1000);
4 Amount BINARY_INTEGER :=1000;
5 Position INTEGER :=1;
6 BEGIN
7 v_buf :=rpad('A',1000,'A');
8
9 insert into myClob values (1 ,EMPTY_CLOB());
10
11 commit;
12
13 SELECT clob_data INTO clob_pointer FROM myClob WHERE id = 1 FOR UPDATE;
14 DBMS_LOB.OPEN (clob_pointer,DBMS_LOB.LOB_READWRITE);
15
16 FOR i IN 1..500 LOOP
17
18 DBMS_LOB.WRITE (clob_pointer,Amount,Position,v_buf);
19
20 Position :=Position +Amount;
21
22 END LOOP;
23
24 DBMS_LOB.CLOSE (clob_pointer);
25
26 END;
27 /
SQL> DECLARE
2 bfile_pointer BFILE;
3 blob_pointer BLOB;
4 bfile_offset NUMBER :=1;
5 blob_offset NUMBER :=1;
6 tot_len INTEGER;
7 BEGIN
8
9 INSERT INTO myBlob VALUES (1,EMPTY_BLOB());
10
11 SELECT blob_data INTO blob_pointer FROM myBlob WHERE id = 1 FOR UPDATE;
12
13 bfile_pointer :=bfilename('BFILE_DIR','test.bmp');
14
15 dbms_lob.fileopen(bfile_pointer,dbms_lob.file_readonly);
16
17 dbms_lob.OPEN(blob_pointer,dbms_lob.lob_readwrite);
18
19 dbms_lob.LOADBLOBFROMFILE(blob_pointer,bfile_pointer,dbms_lob.lobmaxsize,
bfile_offset,blob_offset);
20
21 tot_len :=DBMS_LOB.GETLENGTH(blob_pointer);
22
23 dbms_lob.close(blob_pointer);
24
25 dbms_lob.fileclose(bfile_pointer);
26
27 DBMS_OUTPUT.PUT_LINE(TO_CHAR(tot_len));
28 END;
29 /
Creating a Directory Object
Before you can store a pointer to a file in a BFILE column, you must first creat
e a directory object.
The directory object represents the directory in the file system.
You create a directory object using the CREATE DIRECTORY statement.
To perform such a statement, you must have the CREATE ANY DIRECTORY database pri
vilege.
CREATE OR REPLACE DIRECTORY SAMPLE_FILES_DIR AS 'C:\';
When you create a directory object you must ensure that
The actual directory exists in the file system.
The user account has read permission on the directories and files.
SQL> declare
2 x long;
3 y clob;
4 begin
5 select long_data
6 into x
7 from myLongTable
8 where id =100;
9 y :=to_clob(x);
10 insert into myClobTable values (200,y);
11 end;
===============================================================
An execution plan is a list of steps that Oracle will
follow in order to execute a SQL statement. Each
step is one of a finite number of basic operations
known to the database server. Even the most
complex SQL statement can be broken down into a
series of basic operations.
EXPLAIN PLAN is a statement that allows you to
have Oracle generate the execution plan for any
SQL statement without actually executing it. You
will be able to examine the execution plan by
querying the plan table.
A plan table holds execution plans generated by
the EXPLAIN PLAN statement.
The typical name for a plan table
is plan_table, but you may use any
name you wish.
Create the plan table by running
utlxplan.sql, located in
$ORACLE_HOME/rdbms/admin.
Important Columns in the Plan Table
statement_id Unique identifier for each execution plan
timestamp When the execution plan was generated
operation The operation performed in one step of the execution
plan, such as “table access”
options Additional information about the operation, such as “by
index ROWID”
object_name Name of table, index, view, etc. accessed
optimizer Optimizer goal used when creating execution plan
id Step number in execution plan
parent_id Step number of parent step
EXPLAIN PLAN
SET STATEMENT_ID = 'statement id'
INTO PLAN_TABLE FOR
statement
utlxplp.sql
utlxpls.sql
select * from table(dbms_xplan.display());
SELECT id, parent_id, LPAD (' ', LEVEL - 1) ||
operation || ' ' || options operation,
object_name
FROM plan_table
WHERE statement_id = '&stmt_id'
START WITH id = 0
AND statement_id = '&stmt_id'
CONNECT BY PRIOR id = parent_id
AND statement_id = '&stmt_id';
set autotrace on: Shows the execution plan as well as statistics of the st
atement.
set autotrace on explain: Displays the execution plan only.
set autotrace on statistics: Displays the statistics only.
set autotrace traceonly: Displays the execution plan and the statistics (
as set autotrace on does), but doesn't print a query's result.
set autotrace off: Disables all autotrace
===============================================================
EXECUTE IMMEDIATE v_sql_tx INTO v_out_tx;
EXECUTE IMMEDIATE sql_stmt USING emp_id RETURNING INTO salary;
CREATE OR REPLACE PROCEDURE list_employees(loc VARCHAR2, job VARCHAR2)
IS
TYPE cur_typ IS REF CURSOR;
C cur_typ;
query_str VARCHAR2(1000);
emp_name VARCHAR2(20);
emp_num NUMBER;
BEGIN
query_str := 'SELECT ename, empno FROM emp_' || loc || ' WHERE job = :job_title'
;
-- find employees who perform the specified job
OPEN C FOR query_str USING job;
LOOP
FETCH C INTO emp_name, emp_num;
EXIT WHEN C%NOTFOUND;
-- process row here
END LOOP;
CLOSE C;
END;
DECLARE
fHandler UTL_FILE.FILE_TYPE;
buf varchar2(4000);
BEGIN
fHandler := UTL_FILE.FOPEN('MYDIR', 'myfile', 'r');
UTL_FILE.GET_LINE(fHandler, buf);
dbms_output.put_line('DATA FROM FILE: '||buf);
UTL_FILE.FCLOSE(fHandler);
EXCEPTION
WHEN utl_file.invalid_path THEN
raise_application_error(-20000, 'Invalid path. Create directory or set UTL_
FILE_DIR.');
END;
/
SELECT *
FROM tableX
WHERE rowid in (
SELECT rowid FROM tableX
WHERE rownum <= 7
MINUS
SELECT rowid FROM tableX
WHERE rownum < 5);
Can one retrieve only the Nth row from a table?
SELECT * FROM t1 a
WHERE n = (SELECT COUNT(rowid)
FROM t1 b
WHERE a.rowid >= b.rowid);
3rd largest
SELECT * FROM t1 a
WHERE 3 = (SELECT COUNT(rowid)
FROM t1 b
WHERE a.rowid <= b.rowid);
SELECT * FROM (
SELECT ENAME,ROWNUM RN FROM EMP WHERE ROWNUM < 101 )
WHERE RN = 100;
SELECT *
FROM my_table a
WHERE 10 >= (SELECT COUNT(DISTINCT maxcol)
FROM my_table b
WHERE b.maxcol <= a.maxcol)
ORDER BY maxcol;