Oracle Academy Introduction to PL/SQL Instructor...

36
Oracle Academy 1 Database Programming with PL/SQL Copyright © 2007, Oracle. All rights reserved. Oracle Academy Introduction to PL/SQL Instructor Resource Guide INSTRUCTOR NOTES FOR SLIDES SECTION 11 LESSON 1 Slide 1: Using Large Object Data Types No instructor notes for this slide Slide 2: What Will I Learn? No instructor notes for this slide Slide 3: Why Learn It? Remind students that the RAW datatype is the binary equivalent of VARCHAR2. Slide 4: Tell Me / Show Me – New Columns for EMPLOYEES No instructor notes for this slide Slide 5: Tell Me / Show Me – We need Large Object (LOB) Column Data Types Be aware that for BFILEs (which are stored outside the database as Operating System files) the Operating System may impose a restriction on the maximum size. For example, some operating systems cannot store files larger than 32GB. But this is still very large! Slide 6: Tell Me / Show Me – Two ways to store large objects: the old and the new way “Deprecated” means that you can still use them, but Oracle does not recommend their use because newer and better methods exist. Liıke many other deprecated features in Oracle, LONG and LONG RAW still exist because some older applications still use them. Slide 7: Tell Me / Show Me – Example Uses of LOB Columns The word “LOB” (“Large OBject”) is generally used when talking about CLOBs, BLOBs, and BFILEs in general. Slide 8: Tell Me / Show Me – The Old Way The New Way The rest of this lesson explains only CLOBs and BLOBs. Students will learn more about BFILEs in the next lesson. Slide 9: Tell Me / Show Me – Converting LONG to CLOB Although the SQL syntax is easy, converting LONG to CLOB (or LONG RAW to BLOB) can take a long time, because the physical formats of the two datatypes are different, so every byte of data has to be copied. Imagine a table of 1 million rows in which the average size of the LONG column is 5MB. That’s 5 Terabytes (5000 Gigabytes) of data to be copied.

Transcript of Oracle Academy Introduction to PL/SQL Instructor...

Page 1: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 1 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

Oracle Academy

Introduction to PL/SQL

Instructor Resource Guide

INSTRUCTOR NOTES FOR SLIDES

SECTION 11 LESSON 1

Slide 1: Using Large Object Data Types

No instructor notes for this slide

Slide 2: What Will I Learn?

No instructor notes for this slide

Slide 3: Why Learn It?

Remind students that the RAW datatype is the binary equivalent of VARCHAR2.

Slide 4: Tell Me / Show Me – New Columns for EMPLOYEES

No instructor notes for this slide

Slide 5: Tell Me / Show Me – We need Large Object (LOB) Column Data Types

Be aware that for BFILEs (which are stored outside the database as Operating System files) the

Operating System may impose a restriction on the maximum size. For example, some operating

systems cannot store files larger than 32GB. But this is still very large!

Slide 6: Tell Me / Show Me – Two ways to store large objects: the old and the new way

“Deprecated” means that you can still use them, but Oracle does not recommend their use

because newer and better methods exist. Liıke many other deprecated features in Oracle, LONG

and LONG RAW still exist because some older applications still use them.

Slide 7: Tell Me / Show Me – Example Uses of LOB Columns

The word “LOB” (“Large OBject”) is generally used when talking about CLOBs, BLOBs, and

BFILEs in general.

Slide 8: Tell Me / Show Me – The Old Way The New Way

The rest of this lesson explains only CLOBs and BLOBs. Students will learn more about

BFILEs in the next lesson.

Slide 9: Tell Me / Show Me – Converting LONG to CLOB

Although the SQL syntax is easy, converting LONG to CLOB (or LONG RAW to BLOB) can

take a long time, because the physical formats of the two datatypes are different, so every byte of

data has to be copied. Imagine a table of 1 million rows in which the average size of the LONG

column is 5MB. That’s 5 Terabytes (5000 Gigabytes) of data to be copied.

Page 2: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 2 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

Slide 10: Tell Me / Show Me – CLOB column

No instructor notes for this slide

Slide 11: Tell Me / Show Me – How and where is LOB data stored?

Components of a LOB

There are two distinct parts to a LOB:

• LOB value: The data that constitutes the real object being stored

• LOB locator: A pointer to the location of the LOB value stored in the table row

Regardless of where the value of a LOB is stored, a locator is stored in the row. You can think of

a LOB locator as a pointer to the actual location of the LOB value.

A LOB column does not contain the data; it contains the locator of the LOB value.

When a user creates an internal LOB (CLOB or BLOB), the value is stored elsewhere in the

database and a locator to the out-of-line LOB value is placed in the LOB column of the

corresponding row in the table. External LOBs (BFILES) store the data outside the database, so

only a locator to the LOB value is stored in the database.

Slide 12: Tell Me / Show Me – Adding a LOB column to a table

No instructor notes for this slide

Slide 13: Tell Me / Show Me – Initializing a LOB Column

Despite the word EMPTY_ in the function names, these two functions do not set the LOB

column to null (it is automatically null when the column is first added). These functions place a

real non-null value in the column. This value is a locator (pointer) to the space elsewhere in the

database where the large LOB value will be stored.

Slide 14: Tell Me / Show Me – Populating a CLOB Column with Data

Note that this and the next few slides shows only how to populate and manipulate CLOB data.

BLOBs are usually populated either by converting existing LONG RAW data (as shown earlier

in this lesson) or by loading the BLOB data from an external BFILE.

Slide 15: Tell Me / Show Me – Reading CLOB data from the table

We can use normal SQL character functions – UPPER, LOWER, INITCAP, SUBSTR, INSTR

and so on – when SELECTing CLOB columns.

Slide 16: Tell Me / Show Me – Updating CLOB data

We have to use PL/SQL because calls to DBMS_LOB require passing the locator as a parameter.

Therefore we need to declare a PL/SQL variable to store the locator value.

Page 3: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 3 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

Slide 17: Tell Me / Show Me – Updating CLOB data using DBMS_LOB

This PL/SQL block finds the length of the existing LOB value, then appends ‘NEW TEXT’ to

the end of it, preceded by a space.

A CLOB or BLOB variable in PL/SQL holds the locator, not the data value.

• First, the SELECT loads the locator into the CLOB variable. This locator is used as the

first parameter in all calls to DBMS_LOB.

• Then, the GETLENGTH function returns the length in bytes of the existing LOB data

value.

• Then, the WRITE function modifies the LOB value in the database.

There are many other procedures and functions in the DBMS_LOB package. Students will see

some more of these in the next lesson.

Slide 18: Tell Me / Show Me – Populating a CLOB column with a large value using

DBMS_LOB:

No instructor notes for this slide

Slide 19: Tell Me / Show Me – Populating a CLOB column using DBMS_LOB:

This PL/SQL block loops round, each time appending a new piece of text to the existing CLOB

value, until the next piece is found to be null (LENGTH = 0).

The actual values of the pieces of text in V_TEXT would be inserted programmatically, not

coded as a literal as shown here. In fact, if you execute the anonymous block shown in the slide,

it will loop forever – or until 128TB of data has been loaded.

Slide 20: Tell Me / Show Me – Reading BLOB column data using DBMS_LOB

This block retrieves and displays the country_id, country_name and length in bytes of the flag (a

BLOB column) for countries whose names begin with ‘A’. The output is shown on the next

slide.

Slide 21: Tell Me / Show Me – Reading BLOB column data using DBMS_LOB (continued)

No instructor notes for this slide

Slide 22: Tell Me / Show Me – Terminology

CLOB – Character Large Objects, such as resumes, text articles, source code files.

BLOB – Binary Large Objects, such as sound (MP3), photos (JPEG, BMP), proprietary formats

(PDF, DOC, XLS), and executables (EXE, DDL).

BFILE – Binary Files, just like BLOB but stored outside the database, often on separate media

(CD, DVD, HD-DVD).

Slide 23: Summary

No instructor notes for this slide

Slide 24: Try It / Solve It

No instructor notes for this slide

Page 4: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 4 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

SECTION 11 LESSON 2

Slide 1: Managing BFiles

No instructor notes for this slide

Slide 2: What Will I Learn?

No instructor notes for this slide

Slide 3: Why Learn It?

No instructor notes for this slide

Slide 4: Tell Me / Show Me – What is a BFILE?

A single BFILE cannot span more than one device, for example a movie must be all on a single

DVD.

Slide 5: Tell Me / Show Me – How is a BFILE different fromCLOBs and BLOBs?

“Created outside Oracle”: for example, we could take a set of DVDs each containing a movie,

and copy them to our computer’s hard disk using normal operating system commands, creating a

set of files in one or more operating system directories.

For example on Windows:

C:\mymovies\titanic.avi

C:\mymovies\eight_mile.avi and so on.

Slide 6: Tell Me / Show Me – When to use a BFILE?

No instructor notes for this slide

Slide 7: Tell Me / Show Me – A New Database Object: DIRECTORY

Directories can be used in other cases where Oracle needs a pointer to files outside the database,

for example when using the UTL_FILE_DIR package. This was mentioned in Section 9 Lesson

6.

Slide 8: Tell Me / Show Me – Creating and Managing Directories

ALTER DIRECTORY only updates the pointer. The files themselves must be moved by

Operating System commands, for example cut/paste in Windows Explorer.

Slide 9: Tell Me / Show Me – Viewing Directories in the Data Dictionary

No instructor notes for this slide

Slide 10: Tell Me / Show Me – Adding and Populating a BFILE column for a Table

A BFILE locator column has two components: the directory alias for the Operating System

directory where the file is stored, and the name of the file ıtself.

Page 5: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 5 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

Slide 11: Tell Me / Show Me – Adding and Populating a BFILE column: Example

Go through this carefully:

Step 1: declares a PL/SQL variable of type BFILE to hold a locator value

Step 2: populates the BFILE variable with the location and name of a specific BFILE (ie a

specific movie) using the BFILENAME function.

Step 3: the DBMS_LOB.FILEEXISTS function checks the Operating System to see if the file is

really there. It returns 1 if the file exists, and 0 if it does not. If the file exists, we must

open it before use using DBMS_LOB.FILEOPEN.

Step 4: updates the table column with the locator value and then closes the file.

NOTE: If the Oracle directory object MOVIE_DIR does not exist it will give an ORA-22285 (a

non-predefined Oracle server error)

Slide 12: Tell Me / Show Me – Reading BFILE Locator and Data Values

A locator value in a BFILE column has two components: the DIRECTORY alias and the

filename. However, it is stored in a binary format which cannot be SELECTed directly. We use

the FILEGETNAME procedure to extract its two components into VARCHAR2 variables.

Slide 13: Tell Me / Show Me – Terminology

BFILE – Is like a CLOB or BLOB, except that its value is stored outside the database in a

separate file. The database holds a pointer to the external file.

DIRECTORY – Is a pointer from the database to an operating system directory (Windows

folder) where BFILEs are stored.

Slide 14: Summary

No instructor notes for this slide

Slide 15: Try It / Solve It

No instructor notes for this slide

Page 6: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 6 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

SECTION 11 LESSON 3

Slide 1: User-Defined Records

No instructor notes for this slide

Slide 2: What Will I Learn?

No instructor notes for this slide

Slide 3: Why Learn It?

No instructor notes for this slide

Slide 4: Tell Me / Show Me – A Problem Scenario

This and the next few slides show that a record declared with %ROWTYPE can be based on a

table rather than on a cursor. Students will probably find this easy to understand, but it is a

necessary prerequisite for the more complex record structures later in the lesson.

Slide 5: Tell Me / Show Me – A Problem Scenario: PL/SQL Code

Point out that this is long-winded and cumbersome code. And what happens if a twelfth column

is added to the EMPLOYEES table? Or an existing column is dropped? Imagine a table with

forty or fifty columns (these are not uncommon in production databases).

Slide 6: Tell Me / Show Me – And how can we return the results to the calling environment

No instructor notes for this slide

Slide 7: Tell Me / Show Me – Using a PL/SQL Record

PL/SQL allows any named variable – scalar or composite - to be passed as a parameter.

Slide 8: Tell Me / Show Me – PL/SQL Records

No instructor notes for this slide

Slide 9: Tell Me / Show Me – Defining Our Own Records

Obviously we cannot declare a record as: p_record_name

bits_of_table1_plus_bits_of_table2_plus….%ROWTYPE ! We could create a database view to

implement the join, and then use %ROWTYPE on the view. But as we have seen, views have

limitations, for example complex views may not be updateable.

Stress that records are not the same as rows in a table, even if (using %ROWTYPE) their

structure exactly matches a table row. Table rows are stored on disk and are permanent; the data

in a record is held in a memory structure (like any other program variable) and persists only for

the duration of the session.

Page 7: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 7 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

Slide 10: Tell Me / Show Me – Creating a User-Defined PL/SQL Record

The Oracle-predefined scalar data types such as VARCHAR2, DATE, NUMBER and so on, are

actually TYPEs declared automatically (with global scope) in every Oracle database. If you

have DBA privileges, try the following:

SELECT type_name

FROM dba_types

WHERE predefined = ‘YES’;

Slide 11: Tell Me / Show Me – User-Defined PL/SQL Records Example

The slide example declares two record types and a record based on each of the types.

Slide 12: Tell Me / Show Me – User-Defined PL/SQL Records: Example (continued)

No instructor notes for this slide

Slide 13: Tell Me / Show Me – Where Can Types and Records be Declared and Used?

Remind students that declarations in a package specification are visible to the calling

environment, not just within the package itself.

Slide 14: Tell Me / Show Me

The package specification declares a record type person_type. Two records are declared based

on this type: p_pers_rec is an OUT formal parameter from the procedure, and v_pers_rec is a

local variable within the procedure.

Note: The EXCEPTION section has been omitted from the package body to save space on the

slide.

Slide 15: Tell Me / Show Me – Visibility and Scope of records Example (continued)

Step 1: declares a record based on the record type declared globally in the package specification

Step 2: passes the record-name as an actual parameter to the package procedure.

Slide 16: Tell Me / Show Me – Terminology

PL/SQL record – is a composite data type consisting of a group of related data items stored as

fields, each with its own name and data type.

Slide 17: Summary

No instructor notes for this slide

Slide 18: Try It / Solve It

No instructor notes for this slide

Page 8: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 8 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

SECTION 11 LESSON 4

Slide 1: Indexing Tables of Records

No instructor notes for this slide

Slide 2: What Will I Learn?

No instructor notes for this slide

Slide 3: Why Learn It?

This lesson discusses INDEX BY tables and tables of records. There are other types of

collection variable in PL/SQL: Nested Tables and Varrays. They are outside the scope of this

course.

In Oracle 10g, INDEX BY tables are called “Associative Arrays”.

Slide 4: Tell Me / Show Me – What is a Collection?

Stress that an INDEX BY Table is NOT a database table. Their data is stored in memory, not on

disk in the database. Therefore transaction control statements such as COMMIT and

ROLLBACK have no meaning for INDEX BY tables.

Slide 5: Tell Me / Show Me – An INDEX BY Table Has a Primary Key

BINARY_INTEGER is faster than PLS_INTEGER and is therefore recommended. The

magnitude range of a BINARY_INTEGER is -2147483647 to +2147483647, so we can store

many millions of entries in an INDEX BY table … as long as we have enough memory to hold

them!

In Oracle 10g, INDEX BY tables can be string-indexed by VARCHAR2.

Slide 6: Tell Me / Show Me – INDEX BY Table Structure

Point out that the primary key is not like a sequence. Values are not generated automatically, but

must be specifically inserted. Therefore some values can be missing, as the slide shows.

Slide 7: Tell Me / Show Me – Declaring an INDEX BY Table

The slide example uses an anonymous block, but INDEX BY tables can be declared in any kind

of PL/SQL subprogram.

Page 9: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 9 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

Slide 8: Tell Me / Show Me

If we wanted to assign the primary keys incrementally (like a sequence) instead of using the

employee_id, we would code:

DECLARE

TYPE t_names IS TABLE OF VARCHAR2(50)

INDEX BY BINARY_INTEGER;

last_names_tab t_names;

v_count BINARY_INTEGER := 0;

BEGIN

FOR emp_rec IN (SELECT employee_id, last_name

FROM employees) LOOP

v_count := v_count + 1;

last_names_tab(v_count) := emp_rec.last_name;

END LOOP;

END;

Slide 9: Tell Me / Show Me – Using INDEX BY Table Methods

No instructor notes for this slide

Slide 10: Tell Me / Show Me – Using INDEX BY Table Methods (continued)

The COUNT method returns the number of elements in the table. FIRST and LAST return the

lowest and highest primary key values in the table. EXISTS(n) returns TRUE if an element with

primary key = n exists.

Ask students: what output would be produced when this block is executed? Answer: all the

employee last names, in ascending employee_id sequence.

Note: A full discussion of methods is beyond the scope of this course.

Page 10: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 10 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

EXAMPLE WORKING CODE:

DECLARE

TYPE t_names IS TABLE OF VARCHAR2(50)

INDEX BY BINARY_INTEGER;last_names_tab t_names;

last_names_tab t_names;

v_count BINARY_INTEGER := 0;

BEGIN

FOR emp_rec IN (SELECT employee_id, last_name

FROM employees) LOOP

v_count := v_count + 1;

last_names_tab(v_count) := emp_rec.last_name;

END LOOP;

v_count := last_names_tab.COUNT --1

FOR i IN last_names_tab.FIRST .. last_names_tab.LAST --2

LOOP

IF last_names_tab.EXISTS(i) THEN --3

DBMS_OUTPUT.PUT_LINE(last_names_tab(i));

END IF;

END LOOP;

END;

Slide 11: Tell Me / Show Me – INDEX BY TABLE OF RECORDS

An INDEX BY table of records is just like any other INDEX BY table, except that the non-

primary-key field is a composite (a record) rather than a scalar. We populate it and use methods

such as COUNT, EXISTS and FIRST on it exactly as before.

Slide 12: Tell Me / Show Me –Using an INDEX BY Table of Records

--1 populates the table with whole EMPLOYEE rows.

--2 displays the first_name field from each table element in turn.

Note the syntax: table_name(primary-key).field-name, NOT table_name.field-name(primary-

key).

Slide 13: Tell Me / Show Me – Terminology

Collection – A set of occurrences of the same kind of data.

INDEX BY TABLE – A collection which is based on a single field or column, for example on

the last_name column of EMPLOYEES

INDEX BY TABLE OF RECORDS – A collection which is based on a composite record type,

for example on the whole DEPARTMENTS row.

Slide 14: Summary

No instructor notes for this slide

Slide 15: Try It / Solve It

No instructor notes for this slide

Page 11: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 11 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

PRACTICE SOLUTIONS

SECTION 11 LESSON 1 - Using Large Object Data Types

Terminology

1. ___BLOB_________________ Binary Large Objects, such as sound (MP3), photos

(JPEG, BMP), proprietary formats (PDF, DOC, XLS), and executables (EXE, DDL).

2. ___CLOB_________________ Character Large Objects, such as resumes, text articles,

source code files.

3. ___BFILE_________________ Binary Files, just like BLOB but stored outside the

database, often on separate media (CD, DVD, HD-DVD).

Try It / Solve It

1. State the datatypes of the three kinds of LOB. Which are internal and which are external?

What kinds of data can be stored in each one?

CLOB: internal, stores text data such as resumes, text articles, source code files

BLOB: internal, stores binary data such as sound (MP3) and photos (JPEG, BMP)

BFILE: external, stores binary data like a BLOB but outside the database.

2. You will use a partial copy of the employees table. Create this copy by executing:

CREATE TABLE lob_emps

AS SELECT employee_id, last_name

FROM employees

WHERE employee_id IN (103,104);

A. You need to add a column to the table to store employees’ annual performance

evaluations, which are stored in text format. Some evaluations may be very large. Why

would you not use a VARCHAR2 datatype for this?

A VARCHAR2 column can store a maximum of 4096 bytes.

B. Add a LONG column named annual_eval to the copy table.

ALTER TABLE lob_emps ADD (annual_eval LONG);

C. Populate the LONG column for the two employees using the following values:

employee_id 103, value “Programs Java and HTML well.” Employee_id 104, value

“Useless at both HTML and Java.”

Page 12: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 12 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

UPDATE lob_emps

SET annual_eval = 'Programs Java and HTML well.'

WHERE employee_id = 103;

UPDATE lob_emps

SET annual_eval = 'Useless at both HTML and Java.'

WHERE employee_id = 104;

D. Why would it be better if this column were CLOB, not LONG? Convert the column to

CLOB using an ALTER TABLE statement.

A CLOB column can store at least 4GB in each row, while a LONG column can store a

maximum of 2GB. Also a table can contain as many LOB columns as needed, but only

one LONG column.

ALTER TABLE lob_emps MODIFY (annual_eval CLOB);

E. Describe the table to check that the column is now CLOB. Then SELECT the CLOB

data for the two employees.

DESCRIBE lob_emps

SELECT employee_id, annual_eval

FROM lob_emps;

3. Use the partial copy of the employee table from question 2 above for these next questions.

A. What is a LOB locator and what is its purpose?

Because LOB data values are stored out-of-line (in a different area of the database from

the rest of the table) there must be a pointer to it from the table row. This pointer is

called a locator.

B. Write and execute an anonymous block which displays the employee_ids and the length

in bytes of the CLOB column values. Declare a cursor to fetch the rows. Use

DBMS_LOB.GETLENGTH to retrieve the length of the CLOB values. Save your code.

DECLARE

CURSOR emp_curs IS

SELECT employee_id, annual_eval

FROM lob_emps;

v_emprec emp_curs%ROWTYPE;

v_length NUMBER;

BEGIN

OPEN emp_curs;

LOOP

Page 13: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 13 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

FETCH emp_curs INTO v_emprec;

EXIT WHEN emp_curs%NOTFOUND;

v_length := DBMS_LOB.GETLENGTH(v_emprec.annual_eval);

DBMS_OUTPUT.PUT_LINE('Employee: ' || v_emprec.employee_id ||

' Length: ' || v_length );

END LOOP;

CLOSE emp_curs;

END;

C. Modify your anonymous block to add extra text to the end of the CLOB values. Use the

DBMS_LOB.GETLENGTH function to find how long the existing data is; then use

DBMS_LOB.WRITE to add the text “Next evaluation is due after one year.” to the end

of the text. Your cursor will need to be FOR UPDATE because you are modifying the

table. Save your code.

DECLARE

CURSOR emp_curs IS

SELECT employee_id, annual_eval

FROM lob_emps

FOR UPDATE;

v_emprec emp_curs%ROWTYPE;

v_length NUMBER;

v_new_text VARCHAR2(32767)

:= 'Next evaluation is due after one year.';

v_amount_to_add INTEGER;

BEGIN

OPEN emp_curs;

LOOP

FETCH emp_curs INTO v_emprec;

EXIT WHEN emp_curs%NOTFOUND;

v_length := DBMS_LOB.GETLENGTH(v_emprec.annual_eval);

v_amount_to_add := LENGTH(v_new_text);

DBMS_LOB.WRITE(v_emprec.annual_eval,v_amount_to_add,

v_length + 2,v_new_text);

END LOOP;

CLOSE emp_curs;

END;

D. SELECT the CLOB data again (as in question 1e) to check that the values have been

updated correctly.

SELECT employee_id, annual_eval

FROM lob_emps;

Page 14: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 14 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

4. The wf_countries table contains a column named flag of datatype BLOB.

A. Write and execute a SQL statement which attempts to display the country_name and flag

for countries whose names begin with “M”.

SELECT country_name, flag

FROM wf_countries

WHERE country_name LIKE ‘M%’;

B. BLOB data cannot be displayed directly in Application Express, but we can display the

length of the BLOB data. Write and execute an anonymous block which uses a cursor to

fetch and display the country name and the length of the BLOB column value, again for

all countries whose names begin with “M”. Save your code.

DECLARE

CURSOR country_curs IS

SELECT country_name, flag

FROM wf_countries

WHERE country_name LIKE 'M%';

v_country_rec country_curs%ROWTYPE;

v_length NUMBER;

BEGIN

OPEN country_curs;

LOOP

FETCH country_curs INTO v_country_rec;

EXIT WHEN country_curs%NOTFOUND;

v_length := DBMS_LOB.GETLENGTH(v_country_rec.flag);

DBMS_OUTPUT.PUT_LINE(v_country_rec.country_name

||' '||v_length);

END LOOP;

CLOSE country_curs;

END;

Extension Exercises

1. From your lob_emps table from question 2:

A. Add a third row to by executing the following:

INSERT INTO lob_emps (employee_id, last_name)

VALUES (105, ‘Smith’);

B. Now re-execute your anonymous block from question 2c to try to update all three rows.

What happens and why?

Page 15: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 15 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

An ORA-22275 error (invalid LOB locator specified) is returned because the CLOB

locator in the new row has not been initialized. This was not a problem for employees

103 and 104 because the ALTER TABLE lob_emps MODIFY (annual_eval CLOB); in

question 1d initialized the locators automatically.

C. What must be done to correct the error? Correct it, then re-execute your anonymous

block to check that it works.

UPDATE lob_emps

SET annual_eval = EMPTY_CLOB()

WHERE employee_id = 105;

2. We can display the value of BLOB data by copying it into a PL/SQL variable of datatype

RAW and then displaying the RAW column value. Modify your block from question 3b to

declare a variable of datatype RAW(100) and a NUMBER variable initialized to a value of

50. Use DBMS_LOB.READ to copy the first 50 bytes of each BLOB value into the RAW

variable. Then display the country name and the value of the RAW variable.

Here are the formal parameters of the DBMS_LOB.READ procedure:

DBMS_LOB.READ (

lob_loc IN BLOB,

amount IN OUT NUMBER,

offset IN INTEGER,

buffer OUT RAW);

DECLARE

CURSOR country_curs IS

SELECT country_name, flag

FROM wf_countries

WHERE country_name LIKE 'M%';

v_country_rec country_curs%ROWTYPE;

v_length NUMBER;

v_blob_value RAW(50);

v_amount_to_read NUMBER := 50;

BEGIN

OPEN country_curs;

LOOP

FETCH country_curs INTO v_country_rec;

EXIT WHEN country_curs%NOTFOUND;

DBMS_LOB.READ(v_country_rec.flag, v_amount_to_read, 1, v_blob_value);

DBMS_OUTPUT.PUT_LINE(v_country_rec.country_name || ' ' || v_blob_value);

END LOOP;

CLOSE country_curs;

END;

Page 16: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 16 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

SECTION 11 LESSON 2 - Managing Bfiles

Terminology

Directions: Identify the vocabulary word for each definition below.

1. ___BFILE__________________ Is like a CLOB or BLOB, except that its value is stored

outside the database in a separate file. The database holds a pointer to the external file.

2. ___DIRECTORY_____________ Is a pointer from the database to an operating system

directory (Windows folder) where BFILEs are stored.

Try It / Solve It

1. How is BFILE data stored differently from other types of LOB data (CLOB and BLOB)?

BFILE data is stored outside the database in separate operating system files. The database

contains a pointer to the external file.

2. List three restrictions of using BFILEs.

1) BFILEs can be read by Oracle, but not modified. Therefore they must be created outside

Oracle.

2) Normal database object privileges (for example SELECT) cannot be granted on

BFILEs.

3) Normal SQL statements cannot be used with BFILEs; DBMS_LOB must be used.

3. BFILES:

A. What is a DIRECTORY database object? State two reasons why a DIRECTORY is

needed when using BFILEs.

A DIRECTORY is a pointer to an operating system directory (or Windows folder).

It is needed when using BFILEs because:

(a) the database needs to know where the BFILEs are

(b) it controls privileges: which Oracle users are allowed to read the BFILEs.

B. What two SQL statements would you use to create a directory called MYDIR pointing to

an operating system directory called ‘/u01/mybfiles’, and to allow Oracle user TOM to

read BFILEs stored in that directory?

CREATE DIRECTORY mydir AS '/u01/mybfiles';

GRANT READ ON DIRECTORY mydir TO tom;

C. You do not have the privilege to create your own directories. Query the dictionary to see

what directory has already been created for you.

SELECT * FROM all_directories;

Page 17: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 17 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

4. Every country in the world has a two-character FIPS (Federal Information Processing

Standard) code which is stored in the fips_id column of wf_countries. The directory

WF_FLAGS points to an operating system directory which contains a BFILE for each

country. Each BFILE contains the national flag of that country. The operating system file

name of the BFILE is: lower_case_fips_id-lgflag.gif. For example, the fips id of Canada is

“CA”, so its BFILE file name is: ca-lgflag.gif

A. Execute a SELECT statement to display the country id, country name and fips id of all

countries whose name begins with “C”.

SELECT country_id, country_name, fips_id

FROM wf_countries

WHERE country_name LIKE ‘C%’;

B. Write down the complete path name (operating system directory and file name) of the

Czech Republic’s national flag.

/u02/webapps/oa1bprd_dir/ez-lgflags.gif

5. In the rest of this Practice, you will be working on a partial copy of wf_countries called

copy_countries. This table is created by you, you add two LOB columns to it and then work

with those LOB columns..

A. Create a partial copy of wf_countries by executing the following statement:

CREATE TABLE copy_countries

AS SELECT country_id, country_name, fips_id

FROM wf_countries

WHERE country_name LIKE 'C%';

B. Change the copy_countries table. Add a new BFILE column called external_flag by

running the following statement:

Alter table copy_countries ADD external_flag BFILE;

C. The following code shows part of an anonymous block which checks whether the

external BFILEs actually exist. Copy and complete the block. For each row, use the

BFILENAME function to populate the BFILE variable with the directory and correct file

name of its external BFILE, then use DBMS_LOB.FILEEXISTS to check the existence

of the file, and display a suitable message to show whether the file exists or not. Execute

the block and save your code.

DECLARE

CURSOR flag_curs IS

SELECT country_name, fips_id

FROM copy_countries;

Page 18: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 18 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

v_flag_rec flag_curs%ROWTYPE;

v_dir_alias VARCHAR2(30):= 'WF_FLAGS';

v_file_name VARCHAR2(50);

v_locator BFILE;

BEGIN

OPEN flag_curs;

LOOP

FETCH flag_curs INTO v_flag_rec;

EXIT WHEN flag_curs%NOTFOUND;

...

...

...

END LOOP;

CLOSE flag_curs;

END;

DECLARE

CURSOR flag_curs IS

SELECT country_name, fips_id

FROM copy_countries;

v_flag_rec flag_curs%ROWTYPE;

v_dir_alias VARCHAR2(30):= 'WF_FLAGS';

v_file_name VARCHAR2(50);

v_locator BFILE;

BEGIN

OPEN flag_curs;

LOOP

FETCH flag_curs INTO v_flag_rec;

EXIT WHEN flag_curs%NOTFOUND;

v_file_name := LOWER(v_flag_rec.fips_id) || '-lgflag.gif';

v_locator := BFILENAME(v_dir_alias,v_file_name);

IF DBMS_LOB.FILEEXISTS(v_locator)= 1 THEN

DBMS_OUTPUT.PUT_LINE(v_flag_rec.country_name ||

' flag exists and is called: ' ||

v_file_name);

ELSE

DBMS_OUTPUT.PUT_LINE(v_flag_rec.country_name ||

' flag does not exist');

END IF;

END LOOP;

CLOSE flag_curs;

END;

6. Modify your anonymous block from question 5 to populate the external_flag column of

copy_countries with the locator of its BFILE. You will need to change your cursor to FOR

UPDATE because you are updating the table. Execute the block. Save your work.

Page 19: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 19 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

DECLARE

CURSOR flag_curs IS

SELECT country_name, fips_id

FROM copy_countries FOR UPDATE;

v_length NUMBER;

v_dir_alias VARCHAR2(50):= 'WF_FLAGS';

v_file_name VARCHAR2(50);

v_locator BFILE;

BEGIN

FOR flag_rec IN flag_curs LOOP

v_file_name := LOWER(flag_rec.fips_id) || '-lgflag.gif';

v_locator := BFILENAME(v_dir_alias,v_file_name);

IF dbms_lob.FILEEXISTS(v_locator)= 1 THEN

UPDATE copy_countries SET external_flag = v_locator

WHERE CURRENT OF flag_curs;

DBMS_LOB.FILEOPEN(v_locator);

v_length := DBMS_LOB.GETLENGTH(v_locator);

DBMS_LOB.FILECLOSE(v_locator);

ELSE

v_length := 0;

END IF;

DBMS_OUTPUT.PUT_LINE(flag_rec.country_name || ' ' || v_dir_alias || ' '

|| v_file_name || ' ' || v_length);

END LOOP;

END;

7. Modify the block from question 6 again to display the country names and the size of its

BFILE in bytes. Save your code. Your output should look similar to this:

DECLARE

Page 20: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 20 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

CURSOR flag_curs IS

SELECT country_name, fips_id

FROM copy_countries;

v_flag_rec flag_curs%ROWTYPE;

v_dir_alias VARCHAR2(30):= 'WF_FLAGS';

v_file_name VARCHAR2(50);

v_locator BFILE;

v_bfile_length NUMBER;

BEGIN

OPEN flag_curs;

LOOP

FETCH flag_curs INTO v_flag_rec;

EXIT WHEN flag_curs%NOTFOUND;

v_file_name := LOWER(v_flag_rec.fips_id) || '-lgflag.gif';

v_locator := BFILENAME(v_dir_alias,v_file_name);

IF DBMS_LOB.FILEEXISTS(v_locator)= 1 THEN

DBMS_LOB.FILEOPEN(v_locator);

v_bfile_length := DBMS_LOB.GETLENGTH(v_locator);

DBMS_LOB.FILECLOSE(v_locator);

ELSE

v_bfile_length := 0;

END IF;

DBMS_OUTPUT.PUT_LINE(v_flag_rec.country_name || ' ' ||

v_dir_alias || ' ' ||

v_file_name || ' ' ||

v_bfile_length);

END LOOP;

CLOSE flag_curs;

END;

Extension Exercise

1. In this question you will add a BLOB column to your copy_countries table and populate it

with a copy of the corresponding BFILE data.

A. Add a BLOB column called internal_flag to the copy_countries table.

ALTER TABLE copy_countries

ADD (internal_flag BLOB);

B. Initialize the BLOB column locators by using EMPTY_BLOB().

UPDATE copy_countries

SET internal_flag = EMPTY_BLOB();

C. Copy the external_flag BFILE value to the BLOB column. To do this, modify your code

from question 6 to read the BFILE value into a RAW variable (using

Page 21: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 21 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

DBMS_LOB.READ), then writing this value into the BLOB column (using

DBMS_LOB.WRITE).

You will also need to exclude country_id 1670 from your cursor because the size of its flag is

34196 bytes, and the maximum size of a RAW variable is 32767 bytes.

DECLARE

CURSOR flag_curs IS

SELECT *

FROM copy_countries

WHERE country_id <> 1670

FOR UPDATE;

v_flag_rec flag_curs%ROWTYPE;

v_dir_alias VARCHAR2(30):= 'WF_FLAGS';

v_file_name VARCHAR2(50);

v_locator BFILE;

v_bfile_length NUMBER;

v_bfile_value RAW(32767);

BEGIN

OPEN flag_curs;

LOOP

FETCH flag_curs INTO v_flag_rec;

EXIT WHEN flag_curs%NOTFOUND;

IF DBMS_LOB.FILEEXISTS(v_flag_rec.external_flag)= 1 THEN

DBMS_LOB.FILEOPEN(v_flag_rec.external_flag);

v_bfile_length := DBMS_LOB.GETLENGTH(v_flag_rec.external_flag);

DBMS_LOB.READ(v_flag_rec.external_flag,

v_bfile_length,

1,

v_bfile_value);

DBMS_LOB.FILECLOSE(v_flag_rec.external_flag);

DBMS_LOB.WRITE(v_flag_rec.internal_flag,

v_bfile_length,

1,

v_bfile_value);

END IF;

END LOOP;

CLOSE flag_curs;

END;

D. For each row, display the sizes of the BLOB value and the BFILE value. Examine the

output and check that the two sizes are equal.

DECLARE

CURSOR flag_curs IS

SELECT *

Page 22: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 22 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

FROM copy_countries

WHERE country_id <> 1670;

v_flag_rec flag_curs%ROWTYPE;

v_dir_alias VARCHAR2(30):= 'WF_FLAGS';

v_file_name VARCHAR2(50);

v_locator BFILE;

v_bfile_length NUMBER;

v_blob_length NUMBER;

BEGIN

OPEN flag_curs;

LOOP

FETCH flag_curs INTO v_flag_rec;

EXIT WHEN flag_curs%NOTFOUND;

IF DBMS_LOB.FILEEXISTS(v_flag_rec.external_flag)= 1 THEN

DBMS_LOB.FILEOPEN(v_flag_rec.external_flag);

v_bfile_length := DBMS_LOB.GETLENGTH(v_flag_rec.external_flag);

DBMS_LOB.FILECLOSE(v_flag_rec.external_flag);

v_blob_length := DBMS_LOB.GETLENGTH(v_flag_rec.internal_flag);

DBMS_OUTPUT.PUT_LINE(v_flag_rec.country_name ||

': BFILE size: ' ||

v_bfile_length ||

' BLOB size: ' ||

v_blob_length);

END IF;

END LOOP;

CLOSE flag_curs;

END;

Page 23: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 23 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

SECTION 11 LESSON 3 - User-Defined Records

Terminology

1. ___ROWTYPE_____________ is a composite data type consisting of a group of related

data items stored as fields, each with its own name and data type.

Try It / Solve It

1. Copy and execute the following anonymous block. Then modify it to declare and use a

single record instead of a scalar variable for each column. Make sure that your code will still

work if an extra column is added to the departments table later. Execute your modified block

and save your code.

DECLARE

v_dept_id departments.department_id%TYPE;

v_dept_name departments.department_name%TYPE;

v_mgr_id departments.manager_id%TYPE;

v_loc_id departments.location_id%TYPE;

BEGIN

SELECT department_id, department_name,

manager_id, location_id

INTO v_dept_id, v_dept_name,

v_mgr_id, v_loc_id

FROM departments

WHERE department_id = 80;

DBMS_OUTPUT.PUT_LINE(v_dept_id || ' ' || v_dept_name

|| ' ' || v_mgr_id

|| ' ' || v_loc_id);

EXCEPTION

WHEN no_data_found THEN

DBMS_OUTPUT.PUT_LINE('This department does not exist');

END;

DECLARE

v_dept_rec departments%ROWTYPE;

BEGIN

SELECT * INTO v_dept_rec

FROM departments

WHERE department_id = 80;

DBMS_OUTPUT.PUT_LINE(v_dept_rec.department_id

|| ' ' || v_dept_rec.department_name

|| ' ' || v_dept_rec.manager_id

|| ' ' || v_dept_rec.location_id);

EXCEPTION

WHEN no_data_found THEN

DBMS_OUTPUT.PUT_LINE('This department does not exist');

END;

Page 24: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 24 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

2. Procedure get_dept:

A. Convert your anonymous block from question 1 to a procedure called get_dept which

accepts a department_id as an IN parameter and (instead of displaying the row) returns a

complete department row in a single composite OUT parameter. If the department does

not exist, the procedure should not display message but should instead return a null value

in the department_id field of the OUT parameter. Execute the code to create the

procedure. Save your code.

CREATE OR REPLACE PROCEDURE get_dept

(p_dept_id IN departments.department_id%TYPE,

p_dept_rec OUT departments%ROWTYPE) IS

BEGIN

SELECT * INTO p_dept_rec

FROM departments

WHERE department_id = p_dept_id;

EXCEPTION

WHEN no_data_found THEN

p_dept_rec.department_id := NULL;

END;

B. Write and execute an anonymous block which calls the get_dept procedure and displays

the department’s details. If a null value is returned, display an error message. Execute

the block twice using department_ids 80 and 72. Save your code.

DECLARE

v_dept_rec departments%ROWTYPE;

BEGIN

get_dept(80, v_dept_rec); -- or (72, v_dept_rec)

IF v_dept_rec.department_id IS NOT NULL THEN

DBMS_OUTPUT.PUT_LINE(v_dept_rec.department_id

|| ' ' || v_dept_rec.department_name

|| ' ' || v_dept_rec.manager_id

|| ' ' || v_dept_rec.location_id);

ELSE

DBMS_OUTPUT.PUT_LINE('This department does not exist');

END IF;

END;

Page 25: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 25 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

3. Answer the following questions:

A. Modify your anonymous block from question 1 so that it SELECTs from an equijoin of

departments and locations into a single record, and displays the department id,

department name and the city where the department is located. You will need to declare

a type consisting of three fields, and a record structure of that type. Execute the block

using department_id 80.

DECLARE

TYPE dept_loc_type IS RECORD

(dept_id departments.department_id%TYPE,

dept_name departments.department_name%TYPE,

city locations.city%TYPE);

v_dept_loc_rec dept_loc_type;

BEGIN

SELECT d.department_id, d.department_name, l.city

INTO v_dept_loc_rec

FROM departments d, locations l

WHERE d.location_id = l.location_id

AND d.department_id = 80;

DBMS_OUTPUT.PUT_LINE(v_dept_loc_rec.dept_id

|| ' ' || v_dept_loc_rec.dept_name

|| ' ' || v_dept_loc_rec.city);

EXCEPTION

WHEN no_data_found THEN

DBMS_OUTPUT.PUT_LINE('This department does not exist');

END;

B. Now modify your get_dept procedure from question 2 so that it tries to return the same

joined data as step 3a in an OUT parameter record structure based on a type. Try to

recreate the procedure. What happens and why?

Page 26: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 26 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

CREATE OR REPLACE PROCEDURE get_dept

(p_dept_id IN departments.department_id%TYPE,

p_dept_loc_rec OUT dept_loc_type) IS

TYPE dept_loc_type IS RECORD

(dept_id departments.department_id%TYPE,

dept_name departments.department_name%TYPE,

city locations.city%TYPE);

BEGIN

SELECT d.department_id, d.department_name, l.city

INTO p_dept_loc_rec

FROM departments d, locations l

WHERE d.location_id = l.location_id

AND d.department_id = p_dept_id;

EXCEPTION

WHEN no_data_found THEN

p_dept_loc_rec.dept_id := NULL;

END;

An error is returned because the procedure code is trying to reference the type in the

OUT parameter before declaring the type within the procedure. We cannot use

forward declarations in procedures or functions, only in packages.

C. Modify your get_dept procedure so that it is the only procedure in a package called

get_dept_pkg. Declare the type as a global variable in the package specification. Create

the package specification and body, and save your code.

CREATE OR REPLACE PACKAGE get_dept_pkg IS

TYPE dept_loc_type IS RECORD

(dept_id departments.department_id%TYPE,

dept_name departments.department_name%TYPE,

city locations.city%TYPE);

PROCEDURE get_dept

(p_dept_id IN departments.department_id%TYPE,

p_dept_loc_rec OUT dept_loc_type);

END get_dept_pkg;

Page 27: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 27 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

CREATE OR REPLACE PACKAGE BODY get_dept_pkg IS

PROCEDURE get_dept

(p_dept_id IN departments.department_id%TYPE,

p_dept_loc_rec OUT dept_loc_type) IS

BEGIN

SELECT d.department_id, d.department_name, l.city

INTO p_dept_loc_rec

FROM departments d, locations l

WHERE d.location_id = l.location_id

AND d.department_id = p_dept_id;

EXCEPTION

WHEN no_data_found THEN

p_dept_loc_rec.dept_id := NULL;

END get_dept;

END get_dept_pkg;

D. Describe get_dept_pkg and observe the datatype of its OUT parameter. Then, modify

your anonymous block from question 2b to call the packaged procedure. The block

should not declare the type, but should use the global type declaration from the package

specification. Execute the block twice using department_ids 80 and 72.

DESCRIBE get_dept_pkg

DECLARE

v_dept_rec get_dept_pkg.dept_loc_type;

BEGIN

get_dept_pkg.get_dept(80, v_dept_rec); -- or (72, v_dept_rec)

IF v_dept_rec.dept_id IS NOT NULL THEN

DBMS_OUTPUT.PUT_LINE(v_dept_rec.dept_id

|| ' ' || v_dept_rec.dept_name

|| ' ' || v_dept_rec.city);

ELSE

DBMS_OUTPUT.PUT_LINE('This department does not exist');

END IF;

END;

Page 28: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 28 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

SECTION 11 LESSON 4 - Index By Tables of Records

Terminology

1. _Collection _____________________ A set of occurrences of the same kind of data.

2. _INDEX BY TABLE______________ A collection which is based on a single field or

column, for example on the last_name column of EMPLOYEES

3. _INDEX BY TABLE OF RECORDS_ A collection which is based on a composite record

type, for example on the whole DEPARTMENTS row.

Try It / Solve It

1. Pl/SQL collections:

A. In your own words, describe what a PL/SQL collection is.

A PL/SQL collection is a set of two or more (usually) many occurrences of the same

kind of data. It is a named variable in PL/SQL. The collection’s data is stored in a

private memory area, like any other PL/SQL variable.

B. Which of the following are collections and which are not?

1. A list of all employees’ last names

2. The character value “Chang”

3. The populations of all countries in Europe

4. All the data stored in the employees table about a specific employee.

1 and 3 are collections. 2 is a scalar. 4 is a composite record structure but not a

collection, since each data item occurs only once.

C. What is the difference between an INDEX BY table and a database table such as

employees or wf_countries?

Database tables are stored in the database, ie on disk, and are therefore permanent

(until DROPped). Their data can be seen and used by any database user with the

correct privileges. INDEX BY tables are PL/SQL variables stored in a memory area,

and are not permanent. Their contents are private to the creating session and cannot

be seen by any other session or user.

D. Describe the difference between an INDEX BY table and an INDEX BY table of records.

In an INDEX BY table, each element or “member” of the table is a single scalar value

such as a last name. In an INDEX BY table of records, each element is a record

structure such as a whole employee row. Both kinds of INDEX BY table also have a

numeric primary key which serves as an index into the table.

Page 29: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 29 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

E. Look at the following code. Describe the difference between t_pops and v_pops_tab. Is

v_pops_tab an INDEX BY table or an INDEX BY table of records? How do you know?

DECLARE

TYPE t_pops IS TABLE OF wf_countries.population%TYPE

INDEX BY BINARY_INTEGER;

v_pops_tab t_pops;

t_pops declares a type and v_pops_tab declares a variable of that type. v_pops_tab is

an INDEX BY table (not of records) because each element is a single scalar variable (a

population value).

2. Tables of countries in South America:

A. Write and execute an anonymous block which declares and populates an INDEX BY

table of countries in South America (region_id = 5). The table should use country_id as a

primary key, and should store the country names as the element values. The data should

be stored in the table in ascending sequence of country_id. The block should not display

any output. Save your code.

DECLARE

TYPE t_country_names IS TABLE OF

wf_countries.country_name%TYPE

INDEX BY BINARY_INTEGER;

v_country_names t_country_names;

CURSOR country_curs IS

SELECT country_id, country_name

FROM wf_countries

WHERE region_id = 5

ORDER BY country_id;

v_country_rec country_curs%ROWTYPE;

BEGIN

OPEN country_curs;

LOOP

FETCH country_curs INTO v_country_rec;

EXIT WHEN country_curs%NOTFOUND;

v_country_names(v_country_rec.country_id) :=

v_country_rec.country_name;

END LOOP;

CLOSE country_curs;

END;

Page 30: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 30 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

B. Modify the block so that after populating the INDEX BY table, it uses a FOR loop to

display the contents of the INDEX BY table. You will need to use the FIRST, LAST and

EXISTS table methods. Execute the block and check the displayed results. Save your

code.

DECLARE

TYPE t_country_names IS TABLE OF

wf_countries.country_name%TYPE

INDEX BY BINARY_INTEGER;

v_country_names t_country_names;

CURSOR country_curs IS

SELECT country_id, country_name

FROM wf_countries

WHERE region_id = 5

ORDER BY country_id;

v_country_rec country_curs%ROWTYPE;

BEGIN

OPEN country_curs;

LOOP

FETCH country_curs INTO v_country_rec;

EXIT WHEN country_curs%NOTFOUND;

v_country_names(v_country_rec.country_id) :=

v_country_rec.country_name;

END LOOP;

CLOSE country_curs;

FOR i IN v_country_names.FIRST .. v_country_names.LAST

LOOP

IF v_country_names.EXISTS(i) THEN

DBMS_OUTPUT.PUT_LINE(i || ' ' || v_country_names(i));

END IF;

END LOOP;

END;

Page 31: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 31 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

C. Modify the block again so that instead of displaying all the contents of the table, it

displays only the first and last elements and the number of elements in the table. Execute

the block and check the displayed results.

DECLARE

TYPE t_country_names IS TABLE OF

wf_countries.country_name%TYPE

INDEX BY BINARY_INTEGER;

v_country_names t_country_names;

CURSOR country_curs IS

SELECT country_id, country_name

FROM wf_countries

WHERE region_id = 5

ORDER BY country_id;

v_country_rec country_curs%ROWTYPE;

BEGIN

OPEN country_curs;

LOOP

FETCH country_curs INTO v_country_rec;

EXIT WHEN country_curs%NOTFOUND;

v_country_names(v_country_rec.country_id) :=

v_country_rec.country_name;

END LOOP;

CLOSE country_curs;

DBMS_OUTPUT.PUT_LINE(v_country_names.FIRST || ' ' ||

v_country_names(v_country_names.FIRST));

DBMS_OUTPUT.PUT_LINE(v_country_names.LAST || ' ' ||

v_country_names(v_country_names.LAST));

DBMS_OUTPUT.PUT_LINE('Number of countries is: ' ||

v_country_names.COUNT);

END;

Page 32: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 32 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

3. Table of Records:

A. Write and execute an anonymous block which declares and populates an INDEX by table

of records containing employee data. The table of records should use the employee id as

a primary key, and each element should contain an employee’s last name, job id and

salary. The data should be stored in the table of records in ascending sequence of

employee id. The block should not display any output. Hint: declare a cursor to fetch the

employee data, then declare the INDEX BY table as cursor-name%ROWTYPE Save

your code.

DECLARE

CURSOR emp_curs IS

SELECT employee_id, last_name, job_id, salary

FROM employees

ORDER BY employee_id;

v_emp_rec emp_curs%ROWTYPE;

TYPE t_emp_data IS TABLE OF

emp_curs%ROWTYPE

INDEX BY BINARY_INTEGER;

v_emp_data t_emp_data;

BEGIN

OPEN emp_curs;

LOOP

FETCH emp_curs INTO v_emp_rec;

EXIT WHEN emp_curs%NOTFOUND;

v_emp_data(v_emp_rec.employee_id) :=

v_emp_rec;

END LOOP;

CLOSE emp_curs;

END;

Page 33: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 33 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

B. Modify the block so that after populating the table of records, it uses a FOR loop to

display to display the contents. You will need to use the FIRST, LAST and EXISTS

table methods. Execute the block and check the displayed results. Save your code.

DECLARE

CURSOR emp_curs IS

SELECT employee_id, last_name, job_id, salary

FROM employees

ORDER BY employee_id;

v_emp_rec emp_curs%ROWTYPE;

TYPE t_emp_data IS TABLE OF

emp_curs%ROWTYPE

INDEX BY BINARY_INTEGER;

v_emp_data t_emp_data;

BEGIN

OPEN emp_curs;

LOOP

FETCH emp_curs INTO v_emp_rec;

EXIT WHEN emp_curs%NOTFOUND;

v_emp_data(v_emp_rec.employee_id) :=

v_emp_rec;

END LOOP;

CLOSE emp_curs;

FOR i IN v_emp_data.FIRST .. v_emp_data.LAST

LOOP

IF v_emp_data.EXISTS(i) THEN

DBMS_OUTPUT.PUT_LINE(v_emp_data(i).employee_id || ' '

|| v_emp_data(i).last_name || ' '

|| v_emp_data(i).job_id || ' '

|| v_emp_data(i).salary);

END IF;

END LOOP;

END;

C. Convert the anonymous block from step 3a to a package named tab_rec_pkg which

declares both the cursor and the type as global variables in the package specification.

The block code should be converted to a public packaged procedure named

pop_emp_tab, which does not display anything, but declares the INDEX BY table of

records as an OUT parameter. Execute your code to create the package specification and

body.

Page 34: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 34 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

CREATE OR REPLACE PACKAGE tab_rec_pkg IS

CURSOR g_emp_curs IS

SELECT employee_id, last_name, job_id, salary

FROM employees

ORDER BY employee_id;

TYPE t_emp_data IS TABLE OF

g_emp_curs%ROWTYPE

INDEX BY BINARY_INTEGER;

PROCEDURE pop_emp_tab

(p_emp_data OUT t_emp_data);

END tab_rec_pkg;

CREATE OR REPLACE PACKAGE BODY tab_rec_pkg IS

PROCEDURE pop_emp_tab

(p_emp_data OUT t_emp_data) IS

v_emp_rec g_emp_curs%ROWTYPE;

BEGIN

OPEN g_emp_curs;

LOOP

FETCH g_emp_curs INTO v_emp_rec;

EXIT WHEN g_emp_curs%NOTFOUND;

p_emp_data(v_emp_rec.employee_id) :=

v_emp_rec;

END LOOP;

CLOSE g_emp_curs;

END;

END tab_rec_pkg;

D. Write and execute an anonymous block which calls the pop_emp_tab packaged

procedure and displays the returned table of records of employee data using a FOR loop.

Save your code.

DECLARE

v_emp_data tab_rec_pkg.t_emp_data;

BEGIN

tab_rec_pkg.pop_emp_tab(v_emp_data);

FOR i IN v_emp_data.FIRST .. v_emp_data.LAST

LOOP

IF v_emp_data.EXISTS(i) THEN

DBMS_OUTPUT.PUT_LINE(v_emp_data(i).employee_id || ' '

|| v_emp_data(i).last_name || ' '

|| v_emp_data(i).job_id || ' '

|| v_emp_data(i).salary);

END IF;

END LOOP;

END;

Page 35: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 35 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

Extension Exercise

1. Create an empty partial copy of the employees table by executing the following statement:

CREATE TABLE part_emps AS

SELECT employee_id, last_name, job_id, salary

FROM employees

WHERE 0 = 1;

2. Add a second public procedure named ins_new_emp to the tab_rec_pkg from step 3b. The

procedure should accept an IN parameter which is a record structure (not a collection)

consisting of the employee id, last name, job id and salary for a single employee. (Hint:

declare the IN parameter as global-cursor-name%ROWTYPE). The procedure should insert

this data as a row into the part_emps table. Execute your code to recreate the package

specification and body. Save your code.

CREATE OR REPLACE PACKAGE tab_rec_pkg IS

CURSOR g_emp_curs IS

SELECT employee_id, last_name, job_id, salary

FROM employees

ORDER BY employee_id;

TYPE t_emp_data IS TABLE OF

g_emp_curs%ROWTYPE

INDEX BY BINARY_INTEGER;

PROCEDURE pop_emp_tab

(p_emp_data OUT t_emp_data);

PROCEDURE ins_new_emp

(p_emp_rec IN g_emp_curs%ROWTYPE);

END tab_rec_pkg;

Page 36: Oracle Academy Introduction to PL/SQL Instructor …web.cerritos.edu/hohly/SitePages/Oracle/PL_SQL/PLSQL_course... · Oracle Academy Introduction to PL/SQL ... SECTION 11 LESSON 1

Oracle Academy 36 Database Programming with PL/SQL

Copyright © 2007, Oracle. All rights reserved.

CREATE OR REPLACE PACKAGE BODY tab_rec_pkg IS

PROCEDURE pop_emp_tab

(p_emp_data OUT t_emp_data) IS

v_emp_rec g_emp_curs%ROWTYPE;

BEGIN

OPEN g_emp_curs;

LOOP

FETCH g_emp_curs INTO v_emp_rec;

EXIT WHEN g_emp_curs%NOTFOUND;

p_emp_data(v_emp_rec.employee_id) := v_emp_rec;

END LOOP;

CLOSE g_emp_curs;

END;

PROCEDURE ins_new_emp

(p_emp_rec IN g_emp_curs%ROWTYPE) IS

BEGIN

INSERT INTO part_emps (employee_id, last_name,

job_id, salary)

VALUES (p_emp_rec.employee_id,

p_emp_rec.last_name,

p_emp_rec.job_id,

p_emp_rec.salary);

END;

END tab_rec_pkg;

3. Modify your anonymous block from step 3d so that instead of displaying each element of the

INDEX BY table of records, it calls the ins_new_emp procedure to insert the element data

into the part_emps table. Execute your modified block.

DECLARE

v_emp_data tab_rec_pkg.t_emp_data;

BEGIN

tab_rec_pkg.pop_emp_tab(v_emp_data);

FOR i IN v_emp_data.FIRST .. v_emp_data.LAST

LOOP

IF v_emp_data.EXISTS(i) THEN

tab_rec_pkg.ins_new_emp(v_emp_data(i));

END IF;

END LOOP;

END;

4. Query the part_emps table to check that it has been populated correctly. It should contain 20

rows.

SELECT *

FROM part_emps;