Professional Documents
Culture Documents
CET341 OracleORTutorialv4.6 PDF
CET341 OracleORTutorialv4.6 PDF
CET341 OracleORTutorialv4.6 PDF
Contents
Many relational systems, e.g. Oracle and Informix have been extended with object-
oriented functionality, therefore converting them to be classified as ‘object-
relational’ systems. Oracle first had object-relational features added in version 8
of its DBMS, and these facilities were extended in later versions. For example,
inheritance between object types was added in Oracle 9i.
In this tutorial you will develop a small database application in Oracle. An UML
class diagram is included in appendix B.
You should read through this tutorial and try all of the examples in either Oracle
SQL Developer, SQL Plus or Oracle APEX. Preferably you should use at least
Oracle 10g (we will be using Oracle 12g in this module). You will be given
instructions on how to access this in the tutorial.
Before you can run this tutorial, you need to log in to Oracle through either SQL
Developer, SQL P or Oracle APEX. Your user-name and password are the same
as the ones you previously used during this module for connecting to Oracle. If
you are using University of Sunderland computers then the database is
There are two forms of extended data type in Oracle: Object-types (equivalent to
abstract data types, or user defined types, UDTs, in SQL 3) and collection types
(i.e. arrays and nested tables). We will cover object types in this section. These
are similar to UDTs in SQL3, or can be thought of as being similar to a class in
object-oriented programming languages such as C#. We will look at collection
types in a later section.
In Oracle (using SQL*Plus, APEX or SQL Developer), type, and run, the following:
Important Points:
1. Note that you need the ‘/’ symbol at the end or the CREATE statement
because type definitions can include the ‘;’ symbol within their code, so you
need to highlight the end of your code entry using the ‘/’ symbol.
2. If your code contains syntax errors, you will get the message ‘Warning: Type
created with compilation errors.’, however the errors will most likely not be
displayed. To subsequently display the errors, you should type and run the
command:
show errors
If you are using Oracle APEX then the show errors command does not work
and you should instead use the Object Browser as you have previously.
Next you need to create the Patient table. Type and run the following code.
Use the describe command and you should see the following structure:
You can also use the describe command to display information about object types.
For example:
desc addresstype;
Name Null? Type
----------------------------- -------- -------------
STREET VARCHAR2(30)
TOWN VARCHAR2(20)
Finally insert the following row of data into your new table:
Notice the extra parentheses around the column which contains the complex type
(which in itself contains four attributes), and the type name preceding it. This is
the required syntax when inserting complex attributes.
Now insert some more rows of data into your patient table (i.e. make up your own
data), and then list the contents of the table.
You could have created the patient table as an ‘object table’, by creating a patient
type. We could then, for example, create member functions (i.e. methods) or
nested tables as part of the type definition. We can even define sub-types (for
example we may want to be able to differentiate between in-patients and out-
patients), as we will see in a later section.
Firstly you will need to delete the existing patient table using the drop command,
i.e. type
Then, input the following commands to create a patient type and an object table.
Don’t worry about the ‘NOT FINAL’ bit for now – this simply signifies to Oracle
that we want the ability to be able to create sub-types (i.e. in-patients and out-
patients) which we will do later on. If you did not specify the type as ‘NOT
FINAL’, or alternatively used ‘FINAL’ then you would not be able to create new
sub-types.
You also do not need to worry just yet about the MEMBER FUNCTION, we will
look at these in the next section on methods.
This would then give you the option of sharing this type across a number of tables
– as discussed later.
Use the describe command to inspect the structure of your Patient table.
Exercise: Insert the same data into this relation that you had inserted previously.
Then, insert two more rows of data into your tables. Run the following queries and
make sure that you understand the results:
This does not mean that your query is correct – it just means that the version of
the softwar we are using cannot support displaying the result. You will see
however that you can view parts of a complex object by the query below, which
does work in Oracle APEX and SQL Developer Web.
3.1 Introduction
Types can contain methods as well as attributes. In Oracle they are known as
MEMBER FUNCTIONS.
These can then be used as if they were attributes within your SQL queries.
Methods can be written in any language, including PL/SQL (an extension of SQL),
Java and C++. Our examples in this section will be written in PL/SQL – a basic
tutorial on PL/SQL is included in appendix A, however you should understand this
language as you previously used it for developing stored procedures in a previous
tutorial.
The first example adds a new member function to the staff type.
This however simply informs Oracle that a function is going to be written, but the
code to perform this function has not yet been provided. You therefore have to
subsequently write the function body as follows:
Now, create a table called staff2 based on the staff type and insert the following
data:
________________________________________________________________
________________________________________________________________
________________________________________________________________
Note that you need to use qualifiers (i.e. giving the table staff2 an alias s in the
query) throughout. Many parts of the extended versions of Oracle will not work
without using table aliases and qualifiers, so you should get used to using them
always from now on.
Exercise: In your definition of the Patient type you had defined a member function
get_age. However, you have not yet written the code for this function. Write the
code for the get_age function (HINT, use the months_between function that was
covered in the previous booklet on Oracle SQL) and test that it works by running
the following query:
3.2 Updates
If we wanted to update the address of a member of staff, then we could issue the
following update command:
UPDATE staff2 s
SET s.staff_address =
(
SELECT p.address
FROM patient p
WHERE p.patient_no = 1
)
WHERE s.staff_id = 1;
This would assign the address of the member of staff with staff_id 1 (i.e. Dr
Smith) to have the same address as the patient Smith. Note that we had to use
a nested select query to retrieve the patient address.
Now we have decided to update the address of the patient, if for example Fred
Smith moves to Lanchester, then we could issue the following update command:
UPDATE patient p
SET p.address.town = 'Lanchester'
WHERE p.patient_no = 1;
So you can see that we can use the ‘nested dot’ notation to update elements of a
composite attribute.
Now run the following two queries and note the result:
________________________________________________________________
________________________________________________________________
________________________________________________________________
Each row in a table has a unique identifier that is automatically generated by the
system, which is the object identity of that row. It is also commonly called the ‘row
identifier’.
to output the object identifier. You’ll see that what is output is a long, meaningless
identifier. However, the use of object identity is very powerful.
Note that Oracle SQL Developer Web and Oracle APEX do not support the use
REF ref in some queries, so running the above query will unfortunately give you
an error in APEX. In SQL Developer web the query will run but display no data for
the REF column. However, the use of REF in associating tables will work, so all
the code below works fine in Oracle APEX and SQL Developer Web.
Using row identifiers means we no longer need to use foreign keys to join tables.
Insert the following data into your new Consultations table, using the INSERT
command given in example 7, and then run the query given.
Unfortunately, as with REF, DEREF also does not work in Oracle APEX.
However the below query works and shows how you can follow references
between tables.
IN SQL Developer Web, the query will run but just displays [object Object], rather
than displaying the contents of the object.
________________________________________________________________
________________________________________________________________
________________________________________________________________
This example also contains an example of a nested array. We will discuss this in
a later section.
If Oracle did not support inheritance then there would be a number of alternative
methods with which we could model this.
ALTERNATIVE 1
We could create one table and store all of the values within that table:
TABLE Patient
Patient_ Surna Forena Addre Admit_ Discharge_ Visit_date
no me me ss date date
1 Smith Fred …. NULL NULL NULL
________________________________________________________________
________________________________________________________________
________________________________________________________________
We could create three tables, one to store patients, and then one each to store in-
patients and out-patients:
TABLE PATIENT
Patient_no Surname Forename Address
1 Smith Fred ….
2 Jones Alan …..
3 King Helen ….
TABLE IN_PATIENT
Patient_no Admit_date Discharge_date
2 13/04/2005 16/04/2005
TABLE OUT_PATIENT
Patient_no Visit_date
3 18/05/2005
________________________________________________________________
________________________________________________________________
________________________________________________________________
ALTERNATIVE 3
We could create two tables, one to store in-patients and one to store out-patients:
TABLE IN_PATIENT
________________________________________________________________
________________________________________________________________
________________________________________________________________
Normally, solution one or two would be chosen (very much dependent on the
queries which you are most expecting to run), and then queries would have to be
written to retrieve the required information, e.g. list all in-patients, list all information
about all patients. However this requires a lot of work on behalf of the user.
PATIENT
OUT_PATIENT IN_PATIENT
Database systems such as Oracle 11g allow type inheritance, by use of the
UNDER command. For example, to create both the in-patient and out-patient
types we would use the following commands:
If we had not used the ‘NOT FINAL’ keyword earlier then we would be given an
error at this point because otherwise the system assumes the previously defined
Patient type is FINAL, which means that it is not allowed to have subtypes.
To insert some data into the patient table would then require the following syntax
(ensure that the patient number, i.e. 4, is the next available patient number in your
table – use a different number if patient 4 already exists):
The important point to note here is that we have not created an InPatient table, all
in-patients (and out-patients) can be stored within the patient table.
Exercise: Run the following query and see what data is returned. Is this what you
expected to see?
Exercise: Create the OutPatient type and insert some data into the patient table.
Then, again run the query above to check that your data has been correctly
inserted into the Patient table.
________________________________________________________________
________________________________________________________________
________________________________________________________________
Unfortunately this query and query 2 below will not work in Oracle APEX, giving
an unsupported data type result. The remaining queries work fine.
In SQL Developer Web, rather than displaying the object contents, it just displays
‘[object Object]’.
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
Exercise: Now, write the method, called days_in_hospital, for the InPatient type
which calculates how many days that the patient is in hospital for (for simplicity you
may assume that there are 31 days in any month). Test that your function works
for by running a query which calls this function for patient number 4.
6. Arrays and Nested Tables
6.1 Arrays
This in fact contains an array (of type diagnosis type) with a maximum of three
elements. It is called a VARRARY because the array can store a varying number
of elements, with a maximum upper bound (i.e. maximum number of elements)
which is specified when you declare the array type.
Unfortunately this query gives an unsupported data type error in Oracle APEX.
However you can run the following query instead to show all elements of the array:
To find out how many elements are stored in a particular array we need to write a
PL/SQL stored procedure (this is similar to a method but simpler in that it can exist
independently of a table definition and does not require to be embedded in an SQL
statement to be executed). The simple example below creates an array and
displays the number of elements in the array. If you type this directly into SQL*Plus
it will run the code, and display the array length. You can test this works correctly
by changing the number of elements in the array and checking that the result is
always correct.
Then, to run your procedure, type (in Oracle SQL*Plus or Oracle SQL Developer:
EXECUTE check_array_size();
If you are using Oracle APEX then the equivalent command is:
BEGIN
check_array_size();
END;
However, what we ideally want is a procedure which will count the number of
diagnoses for each consultation. Enter and run the following procedure and
ensure that you understand what it is doing. You will need to look in the
appendices at the section on Cursors to aid your understanding.
SELECT c.consult_id,
diagnoses_array_size_fn(c.diagnosis)
FROM consultations c;
Finally, to access a specific element of the array we could use the following query:
SELECT d.*
FROM consultations c, TABLE(c.diagnosis) d
WHERE d.COLUMN_VALUE = 'Scurvy';
A nested table on the other hand does not have a fixed upper limit, i.e. it can
contain as many rows as you want.
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
4. Arrays and Nested Tables break one of the levels of normalisation. Which
________________________________________________________________
________________________________________________________________
5. Just as you did with arrays, it is also possible to display the length of nested
tables. Adapt the procedure you developed in the last section to display the
number of addresses for any employee.
You have already seen triggers in the PL/SQL tutorial. You should run through
this section to ensure you understand triggers fully.
Triggers allow us to add complex data validation or data integrity checks to our
database. Although we can normally add data validation/integrity checks as
constraints on tables, the constraint system normally only allows very simple
checks to be performed. We use triggers when we want to manage more complex
constraints on our data.
The examples given in this section are very simple triggers, and you will be
expected to be writing much more complex triggers for your assignment, which will
include, for example SQL statements and other code examples given in the
appendix.
For the next example, we need to create a new table, because we want to record
clocking on and clocking off times for staff who work in the hospital. We could
have created a type, but for simplicity we will simply create a table as follows:
Now we wish to add a constraint which says that staff are not allowed to leave
work before they have started. We could use the following trigger:
Note the use of the to_date function. The DATE type also allows us to store time,
but we need to ‘cast’ the string so that the database can understand what we are
storing, based on the template (HH24:MI, i.e. 24 hour clock with 2 digits
representing the hour and two digits for the minutes, separated by a colon).
________________________________________________________________
________________________________________________________________
________________________________________________________________
with
Make this change and write below the difference between the two statements:
________________________________________________________________
________________________________________________________________
________________________________________________________________
Triggers can be written to fire either at row level or statement level. Row level
triggers fire once for every row affected by the statement, whereas statement level
triggers fire once for the whole statement.
For example, create the following table, and then the two very simple triggers
which will fire on an update to the table
UPDATE trigger_example_table
SET a = a * 2;
Note below the effect of the two triggers, i.e. how many times does each trigger
________________________________________________________________
________________________________________________________________
________________________________________________________________
You have a choice of firing the trigger either before the event happens, or after.
For example, if you are inserting a new row of data into a table, then you can either
fire the trigger before the new row of data is inserted, or after.
To decide which type of trigger you are using depends on the context of the trigger.
If, for example, you may need to change the data being inserted into the table then
you must use a BEFORE trigger. However, if you are not changing the data but
are for example performing data validation, then it is normally better to use an
AFTER trigger.
Some minor issues exist with Oracle’s support for object-relational features within
triggers. For example, it is not possible to use the REF feature within a trigger.
There are other issues, which you will discover when writing more complex triggers
for your assignment.
This short document has introduced you to some of the more advanced features
of the Oracle DBMS, which you can use in your assignment. Further coverage of
these features can be found in Connolly and Begg, which discusses the object-
relational features in Oracle. The following appendix is a short introduction to the
PL/SQL programming language, concentrating on syntax rather than being a
tutorial on PL/SQL. Further detailed information and examples on PL/SQL can be
found in Shah’s book ‘Database Systems Using Oracle’, as well as in Connolly and
Begg.
DECLARE
<variable and constant declarations go here, this
section is optional>
BEGIN
<trigger or method code goes here>
EXCEPTION
<exception handling code goes here, this section
is optional>
END;
/
Variables and constants can be used within your procedure. They can use any
type in Oracle, e.g. NUMBER, VARCHAR2, DATE, BOOLEAN – the only
difference is that NUMBER and VARCHAR2 do not have to have a size specified.
DECLARE
my_age NUMBER;
my_name VARCHAR := 'Fred' /* This is a variable
with a default value */
my_sex CONSTANT CHAR := 'M'; /* This is a
constant */
DECLARE
pat_surname patient.patient_surname%TYPE;
A.2. SQL
You can insert any SQL command, i.e. SELECT, INSERT, UPDATE, DELETE into
a procedure. Normally, you would use a SELECT query, and retrieve the results
from the query into a variable.
For example:
EXECUTE my_first_procedure();
BEGIN
My_first_procecedure();
END;
SET SERVEROUTPUT ON
And then when you execute the procedure you will see the output from your
procedure.
EXECUTE pr_name(3);
EXECUTE pr_name(10);
When you run the procedure the second time, you get an error message to say
‘no_data_found’. This is because the SQL SELECT command has not returned
any data, i.e. there is no patient with patient number 10. To get round this, we
could add an exception block to the procedure, to deal with a NO_DATA_FOUND
exception. This is a type of pre-defined exception, the other one you will commonly
see is TOO_MANY_ROWS, which will fire if your query returns more than one row
when you are only expecting one. We can therefore add some more code to the
above procedure:
In the trigger which you created earlier, there was an example of the other type of
exception, which is a user-defined exception. You can write user-defined
exceptions to deal with special cases in your code.
Then you could use this function in a query such as the one below:
The statements that you most commonly use in PL/SQL will be:
- selection
- loops
Selection
IF and CASE
IF condition THEN
<action statements 1>
ELSIF condition2 THEN
<action statements 2>
ELSIF condition3 THEN
<action statements 3>
ELSE
<action statement 4>
END IF;
CASE has two options, either you can test the value of a variable, or you can check
specific conditions. The syntax for both options is given below:
CASE Syntax 1:
CASE <variable>
WHEN <value1> THEN <statement set 1>
WHEN <value2> THEN <statement set 2>
…
ELSE <statement set n>
END CASE;
CASE
WHEN <condition1> THEN <statement set 1>
WHEN <condition2> THEN <statement set 2>
…
ELSE <statement set n>
END CASE;
Loops
1. Basic loop
2. WHILE loop
3. FOR loop
Basic loop
Basic loops are indefinite, and therefore need a condition to exit. The syntax for a
basic loop is:
LOOP
<statement 1>
<statement 2>
…
<statement n>
EXIT WHEN <condition>;
END LOOP;
The WHILE loop is an alternative to the basic loop and performs as long as the
condition is TRUE. The syntax is:
A.4. Cursors
Cursors allow us to execute a statement and then process the results of the
statement on a line by line basis. A cursor is declared within the DECLARE section
in the PL/SQL block.
DECLARE
pat_no patient.patient_no%TYPE;
pat_surname patient.patient_surname%TYPE;
pat_forename patient.patient_forename%TYPE;
CURSOR pat_cursor IS
SELECT patient_no, patient_surname,
patient_forename
FROM patient;
BEGIN
IF NOT pat_cursor%ISOPEN THEN
OPEN pat_cursor;
END IF;
LOOP
FETCH pat_cursor INTO pat_no, pat_surname,
pat_forename;
EXIT WHEN pat_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Patient number: '
||pat_no);
DBMS_OUTPUT.PUT_LINE('Patient name: '
||pat_forename||' '||pat_surname);
DECLARE
BEGIN
FOR pat_cursor IN
(SELECT patient_no, patient_surname,
patient_forename
FROM patient)
LOOP
DBMS_OUTPUT.PUT_LINE(pat_cursor.patient_fore
name||' '||pat_cursor.patient_surname);
END LOOP;
END;
/
It would depend on how you intend to use the cursor as to which is the best
method. For simple operations then the second example would suffice, but if you
need more complex manipulation of the cursor then you would need to explicitly
create the cursor as in the first example.