PL/SQL Procedural Language / Structured Query

PL/SQL
Procedural Language / Structured Query Language
(Source CaoSys)
Overview
• PL/SQL Records and Tables
• Host File Access with UTL_FILE
Records
To create a record manually that contains employee information you
might use the following:TYPE t_emp_record IS RECORD
( enumber NUMBER
, firstname VARCHAR2(20)
, lastname VARCHAR2(20));
At this point, no PL/SQL object exists, just the new datatype, so, we
need to declare a variable of this type:l_employee t_emp_record;
Records (Continued)
This is no different to declaring a variable of any of the standard
types. You can now use the new l_employee record with the dot
notation to get at its elements, for example:-
l_employee.enumber := 101;
l_employee.firstname := ‘John';
l_employee.lastname := ‘Smith';
You can assign one record to another so long as all the elements in
both records are exactly the same, for example:-
Records (Continued)
You can assign one record to another so long as all the elements in
both records are exactly the same, for example:DECLARE
TYPE t_record IS RECORD
( col1 NUMBER
, col2 VARCHAR2(10) );
l_record1 t_record;
l_record2 t_record;
BEGIN
l_record1.col1 := 10;
l_record1.col2 := 'Test';
l_record2 := l_record1;
END;
Records (Continued)
Records can also be used as arguments in procedures and
functions, for example:CREATE OR REPLACE PROCEDURE print_dept
(p_dept_rec dept%ROWTYPE) IS
BEGIN
DBMS_OUTPUT.put_line(p_dept_rec.deptno);
DBMS_OUTPUT.put_line(p_dept_rec.deptno);
DBMS_OUTPUT.put_line(p_dept_rec.deptno);
END;
Records (Continued)
The procedure could be invoked with:DECLARE
l_rec dept%ROWTYPE;
BEGIN
SELECT * INTO l_rec
FROM dept
WHERE deptno = 10;
print_dept(l_rec);
END;
Tables
A PL/SQL table is very similar to an array found in most third
generation languages. Before a table can be used, you need to
declare a new datatype that is of type table, you do this with the
TYPE statement in a similar way as you create records. The syntax
of TYPE in this case is:TYPE table-name IS TABLE OF type
INDEX BY BINARY_INTEGER;
Tables (Continued)
Where table-name can be any valid identifier and type is any valid
datatype, including any new datatypes you have created, such as a
record. So, to declare a table to hold the names of employees
you might:TYPE t_names IS TABLE OF emp.ename%TYPE
INDEX BY BINARY_INTEGER;
Tables (Continued)
Once your new table type is declared, you must declare a variable
of the new type, for example:names_tab t_names;
The above code will create a new variable called names_tab of type
t_names. You can now use the table names_tab. You access
individual rows on a PL/SQL table by using a table index
(reference/subscript), similar to an array subscript. The index should
appear immediately after the table name, for example, to set the
elements of record one in the names_tab table:names_tab(1).empno := 10;
names_tab(1).ename := ‘John';
Tables (Continued)
Here is another example of a less complex table, this time it is a
table of a scalar datatype:DECLARE
TYPE t_names IS TABLE OF VARCHAR2(10)
INDEX BY BINARY_INTEGER;
names_tab t_names;
BEGIN
names_tab(-10) := ‘John';
names_tab(0) := ‘Mary';
names_tab(250) := ‘Andrew';
END;
Tables (Continued)
Memory has only been allocated for 3
rows, this is very much unlike arrays. To
set the value of the 250’th row in an array,
all elements preceding it must exist. Nor
can an array have a negative subscript.
PL/SQL tables grow dynamically in size as
you create rows, very much like a
database table.
Tables (Continued)
Memory has only been allocated for 3 rows, this is very much unlike
arrays. To set the value of the 250’th row in an array, all elements
preceding it must exist. Nor can an array have a negative subscript.
PL/SQL tables grow dynamically in size as you create rows, very
much like a database table.
Points of interest:
Can not reference undeclared row, as it will result in a exception.
A table object has a number of attributes which we can use to
interact with it e.g. Count, last, exists etc.
Tables (Continued)
COUNT: This will return the number of rows in the table.
DECLARE
TYPE t_table IS TABLE OF NUMBER
INDEX BY BINARY_INTEGER;
number_tab t_table;
l_rows NUMBER;
BEGIN
number_tab(1) := 10;
number_tab(10) := 20;
number_tab(87) := 5;
number_tab(2500) := 100;
l_rows := number_tab.COUNT;
END;
In the above code, l_rows will be set to 4.
Tables (Continued)
DELETE: This is used to remove rows from a table.
DECLARE
TYPE t_table IS TABLE OF NUMBER
INDEX BY BINARY_INTEGER;
number_tab t_table;
BEGIN
number_tab(1) := 10;
number_tab(10) := 20;
number_tab(87) := 5;
number_tab(2500) := 100;
number_tab.DELETE(87);
END;
DELETE can be used in three ways:table.DELETE; will remove all rows
table.DELETE(x); remove row i.
table.DELETE(x,y); remove rows i through y.
Tables (Continued)
EXISTS: This is used to check whether a specified row exists or not.
DECLARE
TYPE t_table IS TABLE OF NUMBER
INDEX BY BINARY_INTEGER;
number_tab t_table;
BEGIN
number_tab(1) := 10;
number_tab(10) := 20;
number_tab(87) := 5;
number_tab(2500) := 100;
IF number_tab.EXISTS(10) THEN
DBMS_OUTPUT.put_line('Row 10 Found');
END IF;
IF NOT number_tab.EXISTS(100) THEN
DBMS_OUTPUT.put_line('Row 100 not found');
END IF;
END;
Tables (Continued)
FIRST and LAST: These are used to find the index of the first and last rows in a
table.
DECLARE
TYPE t_table IS TABLE OF NUMBER
INDEX BY BINARY_INTEGER;
number_tab t_table;
l_first BINARY_INTEGER;
l_last BINARY_INTEGER;
BEGIN
number_tab(1) := 10;
number_tab(10) := 20;
number_tab(87) := 5;
number_tab(2500) := 100;
l_first := number_tab.FIRST;
l_last := number_tab.LAST;
END;
In the above code, l_first wil be set to 1 and l_last will be set to 2500.
Records and Tables Review
PL/SQL are generally very much under-used. They are very
powerful constructs and greatly enhance the functionality of
PL/SQL. Many programs need to have some kind of temporary
storage area, normally used to hold intermediate data which needs
to be processed in some way, a great deal of developers create this
temporary storage using database tables, while this offers some
advantages, such as the ability to restart a failed process from
where it last was, PL/SQL tables offer advantages too; such a vastly
increased performance, PL/SQL tables are much faster to work
with than database tables, as everything is done in memory.
You also have the advantage of not having to create and maintain a
temporary table. The only real disadvantage is slightly more
complex code.
Host File Access
UTL_FILE
Host File Access (Cont)
Reading from and writing to host files is a common task for PL/SQL.
PL/SQL itself does not directly support this kind of functionality, but
it can be done using an Oracle supplied package, UTL_FILE.
UTL_FILE is a server side package for writing to host files on the
server, there is another package, TEXT_IO.
Host File Access (Cont)
UTL_FILE has the following functions and procedures:
FOPEN Used to open a file
FCLOSE Used to closed a file
FCLOSE_ALL Close all open files
IS_OPEN Check if a file is open
FFLUSH Output from UTL_FILE buffered, this procedure is used
to ensure the buffer has been written to the file.
PUT Write to file
PUT_LINE Write to file
NEW_LINE Write to file
PUTF Write to file
GET_LINE Read from a file
Host File Access (Cont)
FOPEN: Use this function to open a file.
FUNCTION fopen( path IN VARCHAR2
, filename IN VARCHAR2
, mode IN VARCHAR2)
RETURN FILE_TYPE;
PATH the directory containing the file FILENAME the actual
filename MODE the Open Mode, this can be one of:
'r' : Read from file
'w' : Write to file
'a' : Append to file
Host File Access (Cont)
The return type is the File Handle which is used to reference the
open file is other functions. It’s type is UTL_FILE.file_type.
DECLARE
l_handle UTL_FILE.file_type;
BEGIN
l_handle := UTL_FILE.fopen( '/u01/app'
, 'my_file.dat'
, 'r');
...
The above code will open the file
/u01/app/my_file.dat for reading.
Host File Access (Cont)
PUT & PUT_LINE: This procedure is used to write data to a file.
UTL_FILE.put(handle,buffer);
Where handle is the handle of an already open file and buffer the string to be written.
PUT does not append a newline character to the buffer; you should use PUT_LINE
or NEW_LINE for this. PUT_LINE is almost the same as PUT except that it
automatically add’s a newline character to the end of buffer.
DECLARE
l_handle UTL_FILE.file_type;
BEGIN
l_handle := UTL_FILE.fopen( '/u01/app'
, 'my_file.dat'
, 'w');
UTL_FILE.put(l_handle,'This line is written');
UTL_FILE.fclose(l_handle);
END;
Host File Access (Cont)
PUTF: This procedure is used to write data to a file.
UTL_FILE.putf(handle,format,arg1,arg2,arg3,arg4,arg5);
Where handle is the handle of an already open file. The parameters arg1 to arg5 are
optional and are string that are written to the file in a format specified by format.
format is any string of text which can contain special codes used to print the strings
in arg1 to arg5. The %s code is replaced in the string with is corresponding arg
parameter. You can also use the code \n to add a newline character. It is similar to
C’s printf function.
DECLARE
l_handle UTL_FILE.file_type;
BEGIN
l_handle := UTL_FILE.fopen( '/u01/app'
, 'my_file.dat'
, 'w');
UTL_FILE.putf(l_handle,'arg1=%s\n',’John');
UTL_FILE.fclose(l_handle);
END;
Host File Access (Example)
Below is a procedure that can be used to dump out the contents of the dept table to
a file.
CREATE OR REPLACE PROCEDURE dump_dept
IS
CURSOR dept_cur
IS
SELECT deptno
, dname
, loc
FROM dept;
l_handle UTL_FILE.file_type;
l_path VARCHAR2(50) := '/u01/app';
l_filename VARCHAR2(20) := 'dept.dat';
l_record VARCHAR2(200);
Host File Access (Example)
BEGIN
-- Open file
l_handle := UTL_FILE.fopen( l_path
, l_filename
, 'w');
-- Get all dept rows
FOR r_dept IN dept_cur
LOOP
l_record := TO_CHAR(r_dept.deptno)||'|'||
r_dept.dname||'|'||
TO_CHAR(r_dept.loc);
-- Write row to file
UTL_FILE.put_line(l_handle,l_record);
END LOOP;
UTL_FILE.fflush(l_handle);
UTL_FILE.fclose(l_handle);
EXCEPTION
WHEN UTL_FILE.invalid_path THEN
DBMS_OUTPUT.put_line('Invalid Path');
WHEN UTL_FILE.write_error THEN
DBMS_OUTPUT.put_line('Write Error');
END;