PL/SQL User Defined types - Record and Table
RECORD and TABLE are user-defined composite types that can be defined by the programmer.
The record type allows the defination of a type that consists of specified fields. Once
the type has been defined, you can then define a specific name for the type as it is being
used, a variable name that references the type. The following PL/SQL coding examples show
some uses of TYPE IS RECORD. In the examples, a_rec_donor is the variable dame for the
type rec_donor which is defined as having three fields: rec_id, rec_name and rec_yrgoal.
Notice in the first I name the fields in the INTO of the SELECT and in the second example,
I use the variable name for the record type in the INTO.
SQL CODE:
SQL> edit atsd1
PL/SQL CODE:
SET VERIFY OFF
ACCEPT in_idno PROMPT 'Please enter the donor idno: '
DECLARE
TYPE rec_donor IS RECORD
(rec_id VARCHAR2(5), rec_name VARCHAR2(15), rec_yrgoal NUMBER(7,2));
a_rec_donor rec_donor; -- Note: this declares a variable of the defined type
BEGIN
SELECT idno, name, yrgoal INTO a_rec_donor.rec_id, a_rec_donor.rec_name, a_rec_donor.rec_yrgoal
FROM new_donor WHERE idno = &in_idno;
INSERT INTO some_donor
VALUES(a_rec_donor.rec_id, a_rec_donor.rec_name, a_rec_donor.rec_yrgoal);
END;
/
SET VERIFY ON
SQL CODE:
SQL> SELECT * FROM new_donor;
IDNO NAME STADR CITY ST ZIP DATEFST YRGOAL CONTACT
----- --------------- --------------- ---------- -- ----- --------- --------- ------------
11111 Stephen Daniels 123 Elm St Seekonk MA 02345 03-JUL-98 500 John Smith
12121 Jennifer Ames 24 Benefit St Providence RI 02045 24-MAY-97 400 Susan Jones
22222 Carl Hersey 24 Benefit St Providence RI 02045 03-JAN-98 Susan Jones
23456 Susan Ash 21 Main St Fall River MA 02720 04-MAR-92 100 Amy Costa
33333 Nancy Taylor 26 Oak St Fall River MA 02720 04-MAR-92 50 John Adams
34567 Robert Brooks 36 Pine St Fall River MA 02720 04-APR-98 50 Amy Costa
6 rows selected.
SQL> SELECT * FROM some_donor;
no rows selected
SQL> @ atsd1
Please enter the donor idno: 23456
PL/SQL procedure successfully completed.
Input truncated to 13 characters
SQL> SELECT * FROM some_donor;
IDNO NAME YRGOAL
----- --------------- ---------
23456 Susan Ash 100
SQL> edit atsd2
PL/SQL CODE:
SET VERIFY OFF
ACCEPT in_idno PROMPT 'Please enter the donor idno: '
DECLARE
TYPE rec_donor IS RECORD
(rec_id VARCHAR2(5), rec_name VARCHAR2(15), rec_yrgoal NUMBER(7,2));
a_rec_donor rec_donor; -- Note: this declares a variable of the defined type
BEGIN
SELECT idno, name, yrgoal INTO a_rec_donor
FROM new_donor WHERE idno = &in_idno;
INSERT INTO some_donor
VALUES(a_rec_donor.rec_id, a_rec_donor.rec_name, a_rec_donor.rec_yrgoal);
END;
/
SET VERIFY ON
SQL> @ atsd2
Please enter the donor idno: 11111
PL/SQL procedure successfully completed.
Input truncated to 13 characters
SQL> SELECT * FROM some_donor;
IDNO NAME YRGOAL
----- --------------- ---------
23456 Susan Ash 100
11111 Stephen Daniels 500
The following code uses %ROWTYPE in a similar way that we have used %TYPE. In this case
we are simply declaring the variable v_donor as having the same layout and types as the
row in the some_donor table. ROWTYPE is useful because it allows the underlying database
to be modified without having to go in and change the PL/SQL code.
Note that in the select I am taking certain columns from new_donor and inserting them in
the variable v_donor which is of the row type some_donor. The fields that I have selected
are fields that correspond correctly with the layout of some_donor. When I do the insert,
I need to name the columns and since v_donor has the same layout as some_donor, the columns
have the same name and I can refer to the as v_donor.column name. Note that in the values,
I cannot use v_donor successfully.
SQL CODE:
SQL> edit atsd4
PL/SQL CODE:
SET VERIFY OFF
ACCEPT in_idno PROMPT 'Please enter the donor idno: '
DECLARE
v_donor some_donor%ROWTYPE;
BEGIN
SELECT idno, name, yrgoal INTO v_donor
FROM new_donor WHERE idno = &in_idno;
INSERT INTO some_donor
VALUES(v_donor.idno, v_donor.name, v_donor.yrgoal);
END;
/
SET VERIFY ON
SQL CODE:
SQL> @ atsd4
Please enter the donor idno: 22222
PL/SQL procedure successfully completed.
SQL> SELECT * FROM some_donor;
IDNO NAME YRGOAL
----- --------------- ---------
23456 Susan Ash 100
11111 Stephen Daniels 500
22222 Carl Hersey
SQL> @ atsd4
Please enter the donor idno: 12121
PL/SQL procedure successfully completed.
Input truncated to 13 characters
SQL> SELECT * FROM some_donor;
IDNO NAME YRGOAL
----- --------------- ---------
12121 Jennifer Ames 400
23456 Susan Ash 100
11111 Stephen Daniels 500
22222 Carl Hersey
In the example above, please note that 12121 was the last record entered and it shows up as
the first once again verifying that if you leave ordering up to Oracle you get an
interesting order.
Tables
PL/SQL tables defined as TYPE IS TABLE are similar to arrays but significantly different in
their implementation. Think of a PL/SQL table as similar to a database table that contains
two columns. The first column would be the KEY or primary key column and would be of type
binary_integer and the second column would be the VALUE column that contains the data we
are putting into the table. Tables in Oracle have no limit on the number of rows, the
elements in the table are not ordered in any specified way, specifically, they do not have
to be sequential.
A table is defined as a TYPE field in the Declare section using the syntax:
TYPE table type name IS TABLE OF data type INDEX BY BINARY_INTEGER
Note that the data type has to be a scalar or record datatype and the index has to be
binary_integer, at least in the current version.
Examples of declaring table:
TYPE t_first_table IS TABLE of whatever.first%TYPE
INDEX BY BINARY_INTEGER;
TYPE t_second_table is TABLE of whatever.datesnd%TYPE
INDEX BY BINARY_INTEGER;
v_first t_first_table;
v_second t_second_table;
In the examples above, the first table and the second table are defined as having similar
types to the field first and the field datesnd on the table whatever. Then, variables are
set up from each of the defined table types. The index to both of them is the required
binary integer. You could also define a table using a record layout. In this case, you
can also use the column names when you are putting data into the tables if you want to put
data into a specific column in the table.
TYPE t_third_table IS TABLE OF whatever%ROWTYPE
INDEX BY BINARY_INTEGER;
v_third t_third_table
To put elements into the tables, the following assignment statements could be used.
v_first(1) := 'Snow shovel';
v_first(8) := 'Trowel';
v_first(4) := 'Sprinkler';
v_second(2) := SYSDATE;
v_third(12).city := 'Rhode Island'
The first table would now contain the following:
KEY VALUE
1 Snow Shovel
8 Trowel
4 Sprinkler
The second table would contain the following:
KEY VALUE
2 21-JUL-99
The third table would contain the following:
KEY VALUE
12 RHODE ISLAND
When dealing with tables there are several attributes available that will help in the
effective handling of the table data:
- COUNT will return the number of rows in the table
- DELETE will delete rows from a table
- EXISTS will return a boolean true if the entry requested exists in the table and
a boolean false if it does not exist
- FIRST will return the binary_integer index of the first row
- LAST will return the binary_integer index of the last row
- NEXT will return the binary_integer index of the next row in the table - the one
after the specified row
- PRIOR will return the binary_integer index of the previous row in the table -
the one before the specified row
In the example below, I have created a table called t_table_1 and then added 4 variables to
that table. Notice that the numbers are not in order or in sequence. For my own sense of
order and control, I would prefer to plan for more orderly numbering when possible. The
next thing I did was use COUNT to tell me how many rows are in the table. COUNT returned
the count of 4.
SQL CODE:
SQL> edit table1
PL/SQL CODE:
SET SERVEROUTPUT ON
DECLARE
TYPE t_table1 IS TABLE OF VARCHAR2(12)
INDEX BY BINARY_INTEGER;
v_table_var t_table1;
v_count_result NUMBER;
BEGIN
v_table_var(5) := 'Fifth week';
v_table_var(2) := 'Second week';
v_table_var(12) := 'Twelfth week';
v_table_var(6) := 'Sixth week';
v_count_result:= v_table_var.COUNT;
dbms_output.put_line('The count is: '||v_count_result);
END;
/
SET SERVEROUTPUT OFF
SQL CODE:
SQL> @ table1
The count is: 4
PL/SQL procedure successfully completed.
In the code below, I have added an IF statement to test to see if a particular record
exists. The user will be prompted to enter a number (the index number) which will come in
as in_num and will be stored as v_num. The IF statement asks if there is an element in the
table with the index number that was keyed in. This is asked in the code IF
v_table_var.EXISTS(v_num) where v_table_var is the variable name and v_num is the keyed in
index. If it is, the message the row exists will display otherwise the row does not exist
will display.
SQL CODE:
SQL> edit table2
PL/SQL CODE:
SET SERVEROUTPUT ON
DECLARE
TYPE t_table1 IS TABLE OF VARCHAR2(12)
INDEX BY BINARY_INTEGER;
v_table_var t_table1;
v_count_result NUMBER;
v_num NUMBER :=&in_num;
BEGIN
v_table_var(5) := 'Fifth week';
v_table_var(2) := 'Second week';
v_table_var(12) := 'Twelfth week';
v_table_var(6) := 'Sixth week';
v_count_result:= v_table_var.COUNT;
dbms_output.put_line('The count is: '||v_count_result);
IF v_table_var.EXISTS(v_num) THEN
dbms_output.put_line('The row exists');
ELSE
dbms_output.put_line('The row does not exist');
END IF;
END;
/
SET SERVEROUTPUT OFF
SQL CODE:
SQL> @ table2
Enter value for in_num: 6
old 6: v_num NUMBER :=&in_num;
new 6: v_num NUMBER :=6;
The count is: 4
The row exists
PL/SQL procedure successfully completed.
Input truncated to 20 characters
SQL> @ table2
Enter value for in_num: 3
old 6: v_num NUMBER :=&in_num;
new 6: v_num NUMBER :=3;
The count is: 4
The row does not exist
PL/SQL procedure successfully completed.
In this example, I am using the first, next, prior and last. Notice first the syntax.
FIRST and LAST stand alone while NEXT and PRIOR need to have an index defined in
parenthesis.
SQL CODE:
SQL> edit table3
PL/SQL CODE:
SET SERVEROUTPUT ON
DECLARE
TYPE t_table1 IS TABLE OF VARCHAR2(12)
INDEX BY BINARY_INTEGER;
v_table_var t_table1;
v_count_result NUMBER;
v_index_first BINARY_INTEGER;
v_index_next BINARY_INTEGER;
v_index_prior BINARY_INTEGER;
v_index_last BINARY_INTEGER;
BEGIN
v_table_var(5) := 'Fifth week';
v_table_var(2) := 'Second week';
v_table_var(12) := 'Twelfth week';
v_table_var(6) := 'Sixth week';
v_count_result:= v_table_var.COUNT;
dbms_output.put_line('The count is: '||v_count_result);
v_index_first := v_table_var.FIRST;
dbms_output.put_line('The first record is: '||v_index_first);
v_index_next := v_table_var.NEXT(v_index_first);
dbms_output.put_line('The next record is: '||v_index_next);
v_index_prior := v_table_var.PRIOR(v_index_next);
dbms_output.put_line('The prior record is: '||v_index_prior);
v_index_last := v_table_var.last;
dbms_output.put_line('The last record is: '||v_index_last);
v_index_prior := v_table_var.PRIOR(v_index_last);
dbms_output.put_line('The prior record is: '||v_index_prior);
v_index_first := v_table_var.FIRST;
dbms_output.put_line('The first record is: '||v_index_first);
END;
/
SET SERVEROUTPUT OFF
The execution is shown below. Compare the order of the table as I created and the results.
The first and last return the first and last rows, however, you need to note that the first
row is defined by Oracle as the row that has the lowest index while the last row is defined
by Oracle as the row that has the highest index. When I worked with next and prior, I had
to provide an index. After I displayed first, I then used the index on first to come up
with next. Next is the next row in order by index. I then used the next index (which is
what I was currently looking at) to display prior - it went back to the first. I then
showed the last and used the last index to show prior. This showed the row with the index
that put it sequentially above last. Finally I showed the first again.
SQL CODE:
SQL> @ table3
The count is: 4
The first record is: 2
The next record is: 5
The prior record is: 2
The last record is: 12
The prior record is: 6
The first record is: 2
PL/SQL procedure successfully completed.
The following example shows the use of the delete and then demonstrates the results using
first and next. The user inputs the index of the row in the table to be deleted. That
number is assigned to an index field of binary integer and then it is used to delete the
row from the table. In my example, I deleted the row with index 5. Note that when I
display first I get 2, then next gets me 6, next again gets me 12 and the request for next
again returns nothing since 5 was deleted and there are now only 3 rows in the table. This
is confirmed by the display of the record count both before and after the delete. Before
the delete it reads 4, after the delete it reads 3.
SQL CODE:
SQL> edit table4
PL/SQL CODE:
SET SERVEROUTPUT ON
DECLARE
TYPE t_table1 IS TABLE OF VARCHAR2(12)
INDEX BY BINARY_INTEGER;
v_table_var t_table1;
v_count_result NUMBER;
v_num NUMBER :=&in_num;
v_index_first BINARY_INTEGER;
v_index_next BINARY_INTEGER;
v_index_in BINARY_INTEGER;
BEGIN
v_table_var(5) := 'Fifth week';
v_table_var(2) := 'Second week';
v_table_var(12) := 'Twelfth week';
v_table_var(6) := 'Sixth week';
v_count_result:= v_table_var.COUNT;
dbms_output.put_line('The count is: '||v_count_result);
v_index_in := v_num;
v_table_var.DELETE(v_index_in);
v_index_first := v_table_var.FIRST;
dbms_output.put_line('The first record is: '||v_index_first);
v_index_next := v_table_var.NEXT(v_index_first);
dbms_output.put_line('The next record is: '||v_index_next);
v_index_next := v_table_var.NEXT(v_index_next);
dbms_output.put_line('The next record is: '||v_index_next);
v_index_next := v_table_var.NEXT(v_index_next);
dbms_output.put_line('The next record is: '||v_index_next);
v_count_result:= v_table_var.COUNT;
dbms_output.put_line('The count is: '||v_count_result);
END;
/
SET SERVEROUTPUT OFF
SQL CODE:
SQL> @ table4
Enter value for in_num: 5
old 6: v_num NUMBER :=&in_num;
new 6: v_num NUMBER :=5;
The count is: 4
The first record is: 2
The next record is: 6
The next record is: 12
The next record is:
The count is: 3
PL/SQL procedure successfully completed.
When using tables in Oracle, it is a good idea to use count as I did simply because the
size of the table is unconstrained. You could also choose to keep your own row count that
you increment everytime you add a row to the table. If it is possible using indexes of 1
and then 2, 3, 4 etc. will allow you a little more control in processing. Sometimes it
isn't possible and as we have seen in the examples above, Oracle accommodates the indexes
you choose. If you want to delete a whole table for some reason, you can assign null to it.
This would be done with the statement: v_table_var := NULL;.