Sunteți pe pagina 1din 33

Database Programming with

PL/SQL
5-1
User-Defined Records

Copyright © 2018, Oracle and/or its affiliates. All rights reserved.


Objectives
This lesson covers the following objectives:
• Create and manipulate user-defined PL/SQL records
• Define a record structure using the %ROWTYPE
attribute

PLSQL S5L1 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 3
User-Defined Records
Self-reading

Purpose
• Up to this point, our SELECT statements (inside our
PL/SQL blocks) have been fairly simple, generally storing
one or two columns from a table INTO one or two
variables.
• What if you want to create and use a variable structure
(called a record) that corresponds to an entire row in a
table, or a view, or a join of several tables, rather than
using just one or two columns?
• Or maybe you need a record structure which does not
correspond to any object(s) in the database.

PLSQL S5L1 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 4
User-Defined Records
Self-reading

PL/SQL Records
• A 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.
• You can refer to the whole record by its name and/or to
individual fields by their names.
• Typical syntax for defining a record is shown below. This
code defines a record based on the structure of a row
within the named table.
record_name table_name%ROWTYPE;

PLSQL S5L1 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 5
User-Defined Records
Self-reading

Structure of a PL/SQL Record

Field1 (data type) Field2 (data type) Field3 (data type) …

• You reference each field by dot-prefixing its field-name


with the record-name : record_name.field_name
• For example, you reference the job_id field in the
v_emp_record record as follows:
v_emp_record.job_id

PLSQL S5L1 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 6
User-Defined Records
Self-reading

The Problem
• The EMPLOYEES table contains eleven columns:
EMPLOYEE_ID, FIRST_NAME,...., MANAGER_ID,
DEPARTMENT_ID.
• You need to code a SELECT * INTO variable names
FROM EMPLOYEES WHERE… in your PL/SQL
subprogram.
• How many scalar variables must you DECLARE to hold
the column values?

PLSQL S5L1 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 7
User-Defined Records
Self-reading

The Problem
• That is a lot of coding, and some tables will have even more
columns.
• Plus, what do you do if a new column is added to the table?
• Or an existing column is dropped?
DECLARE
v_employee_id employees.employee_id%TYPE;
v_first_name employees.first_name%TYPE;
v_last_name employees.last_name%TYPE;
v_email employees.email%TYPE;
... FIVE MORE SCALAR VARIABLES REQUIRED TO MATCH THE TABLE
v_manager_id employees.manager_id%TYPE;
v_department_id employees.department_id%TYPE;
BEGIN
SELECT employee_id, first_name, ... EIGHT MORE HERE, department_id
INTO v_employee_id, v_first_name, ... AND HERE, v_department_id
FROM employees
WHERE employee_id = 100;
END;

PLSQL S5L1 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 8
User-Defined Records
Self-reading

The Problem
• Look at the code again. Wouldn’t it be easier to declare
one variable instead of eleven?
• %ROWTYPE allows us to declare a variable as a record
based on a particular table's structure.
• Each field or component within the record will have its
own name and data type based on the table's structure.
• You can refer to the whole record by its name
and to individual fields by their names.

PLSQL S5L1 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 9
User-Defined Records
Self-reading

The Solution - Use a PL/SQL Record


• Use %ROWTYPE to declare a variable as a record based
on the structure of the EMPLOYEES table.
• Less code to write and nothing to change if columns are
added or dropped.
DECLARE
v_emp_record employees%ROWTYPE;
BEGIN
SELECT * INTO v_emp_record
FROM employees
WHERE employee_id = 100;
DBMS_OUTPUT.PUT_LINE('Email for ' || v_emp_record.first_name ||
' ' || v_emp_record.last_name || ' is ' || v_emp_record.email ||
'@oracle.com.');
END;

PLSQL S5L1 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 10
User-Defined Records
Self-reading

A Record Based on Another Record


You can use %ROWTYPE to declare a record based on
another record:
DECLARE
v_emp_record employees%ROWTYPE;
v_emp_copy_record v_emp_record%ROWTYPE;
BEGIN
SELECT * INTO v_emp_record
FROM employees
WHERE employee_id = 100;
v_emp_copy_record := v_emp_record;

v_emp_copy_record.salary := v_emp_record.salary * 1.2;

DBMS_OUTPUT.PUT_LINE(v_emp_record.first_name ||
' ' || v_emp_record.last_name || ': Old Salary - ' ||
v_emp_record.salary || ', Proposed New Salary - ' ||
v_emp_copy_record.salary || '.');
END;

PLSQL S5L1 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 11
User-Defined Records
Defining Your Own Records
• What if you need data from a join of multiple tables?
• You can declare your own record structures containing
any fields you like.
• PL/SQL records:
– Must contain one or more components/fields of any scalar or
composite type
– Are not the same as rows in a database table
– Can be assigned initial values and can be defined as NOT NULL
– Can be components of other records (nested records).

PLSQL S5L1 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 12
User-Defined Records
Syntax for User-Defined Records
• Start with the TYPE keyword to define your record
structure.
• It must include at least one field and the fields may be
defined using scalar data types such as DATE,
VARCHAR2, or NUMBER, or using attributes such as
%TYPE and %ROWTYPE.
• After declaring the type, use the type_name to declare
a variable of that type.
TYPE type_name IS RECORD
(field_declaration[,field_declaration]...);
identifier type_name;

PLSQL S5L1 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 13
User-Defined Records
User-Defined Records: Example 1
• First, declare/define the type and a variable of that
type.
• Then use the variable and its components.
DECLARE
TYPE person_dept IS RECORD
(first_name employees.first_name%TYPE,
last_name employees.last_name%TYPE,
department_name departments.department_name%TYPE);
v_person_dept_rec person_dept;
BEGIN
SELECT e.first_name, e.last_name, d.department_name
INTO v_person_dept_rec
FROM employees e JOIN departments d
ON e.department_id = d.department_id
WHERE employee_id = 200;
DBMS_OUTPUT.PUT_LINE(v_person_dept_rec.first_name ||
' ' || v_person_dept_rec.last_name || ' is in the ' ||
v_person_dept_rec.department_name || ' department.');
END;

PLSQL S5L1 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 14
User-Defined Records
User-Defined Records: Example 2
• Here we have two custom data types, one nested within
the other.
• How many fields can be addressed in
v_emp_dept_rec?
DECLARE
TYPE dept_info_type IS RECORD
(department_id departments.department_id%TYPE,
department_name departments.department_name%TYPE);
TYPE emp_dept_type IS RECORD
(first_name employees.first_name%TYPE,
last_name employees.last_name%TYPE,
dept_info dept_info_type);

v_emp_dept_rec emp_dept_type;
BEGIN
...
END;

PLSQL S5L1 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 15
User-Defined Records
Declaring and Using Types and Records
• Types and records are composite structures that can be
declared anywhere that scalar variables can be declared
in anonymous blocks, procedures, functions, package
specifications (global), package bodies (local), triggers,
and so on.
• Their scope and visibility follow the same rules as for
scalar variables.
• For example, you can declare a type (and a record based
on the type) in an outer block and reference them
within an inner block.

PLSQL S5L1 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 16
User-Defined Records
Visibility and Scope of Types and Records
• The type and the record declared in the outer block are
visible within the outer block and the inner block.
• What will be displayed by each of the PUT_LINEs?
DECLARE -- outer block
TYPE employee_type IS RECORD
(first_name employees.first_name%TYPE := 'Amy');
v_emp_rec_outer employee_type;
BEGIN
DBMS_OUTPUT.PUT_LINE(v_emp_rec_outer.first_name);
DECLARE -- inner block
v_emp_rec_inner employee_type;
BEGIN
v_emp_rec_outer.first_name := 'Clara';
DBMS_OUTPUT.PUT_LINE(v_emp_rec_outer.first_name ||
' and ' || v_emp_rec_inner.first_name);
END;
DBMS_OUTPUT.PUT_LINE(v_emp_rec_outer.first_name);
END;

PLSQL S5L1 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 17
User-Defined Records
Database Programming with
PL/SQL
5-2
Indexing Tables of Records

Copyright © 2018, Oracle and/or its affiliates. All rights reserved.


Purpose
• You have learned that you can temporarily store one
record in a single variable, either by using %ROWTYPE
or a user-defined record.
• However, there are times when you need to temporarily
store multiple rows of data.
• You might do this when calculating averages or
accumulating items in an online shopping cart prior to
checking out.
• These are called collections.

PLSQL S5L2 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 4
Indexing Tables of Records
What is a Collection?
• A PL/SQL collection is a named set of many occurrences
of the same kind of data stored as a variable.
• A collection is a type of composite variable, similar to
user-defined records.
• This lesson discusses INDEX BY tables and INDEX BY
tables of records.
• There are other types of collection variables, for
instance, Nested Tables and Varrays, but they are
outside the scope of this course.

PLSQL S5L2 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 5
Indexing Tables of Records
What is a Collection?
• You will see two kinds of collections in this lesson:
– An INDEX BY table, which is based on a single field or
column; for example, the last_name column of the
EMPLOYEES table.
– An INDEX BY table of records, which is based on a composite
record type; for example, the row structure in the
DEPARTMENTS table.
• Because collections are PL/SQL variables, they are
stored in memory like other PL/SQL variables.
• They are not stored on the disk like data in a database
table.

PLSQL S5L2 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 6
Indexing Tables of Records
An INDEX BY Table Has a Primary Key
• We need to be able to reference each row in an INDEX
BY table.
• Therefore, every INDEX BY table must have a primary
key which serves as an index to the data.
• The primary key is typically a BINARY_INTEGER, but
it may be a VARCHAR2.

PLSQL S5L2 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 7
Indexing Tables of Records
INDEX BY Table Structure
The primary key could be meaningful business data such
as an employee id.

Primary Key Value


… …

100 Jones

157 Smith

135 Maduro

… …
BINARY_INTEGER Scalar

PLSQL S5L2 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 8
Indexing Tables of Records
Declaring an INDEX BY Table
• Like user-defined records, you must first declare a type
and then declare a variable of that type.
• The syntax is:
TYPE type_name IS TABLE OF DATA_TYPE
INDEX BY PRIMARY_KEY_DATA_TYPE;
identifier type_name;

• The following example sets up an INDEX BY table to


hold all of the hire_dates from the EMPLOYEES table.
TYPE t_hire_date IS TABLE OF DATE
INDEX BY BINARY_INTEGER;
v_hire_date_tab t_hire_date;

PLSQL S5L2 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 9
Indexing Tables of Records
Populating an INDEX BY Table
• The syntax to populate the INDEX BY table is:
DECLARE
TYPE type_name IS TABLE OF DATA_TYPE
INDEX BY PRIMARY_KEY_DATA_TYPE;
identifier type_name;
BEGIN
FOR record IN (SELECT column FROM table)
LOOP
identifier(primary_key) := record.column;
END LOOP;
END;

• The primary key can be initialized using a unique


column from the selected table or an incrementing
integer.

PLSQL S5L2 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 10
Indexing Tables of Records
Populating an INDEX BY Table
This example populates an INDEX BY table with the hire
date of employees using employee_id as the primary
key.
DECLARE
TYPE t_hire_date IS TABLE OF employees.hire_date%TYPE
INDEX BY BINARY_INTEGER;
v_hire_date_tab t_hire_date;
BEGIN
FOR emp_rec IN
(SELECT employee_id, hire_date FROM employees)
LOOP
v_hire_date_tab(emp_rec.employee_id)
:= emp_rec.hire_date;
END LOOP;
END;

PLSQL S5L2 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 11
Indexing Tables of Records
Populating an INDEX BY Table
This example populates an INDEX BY table with
employees’ date of hire and sets the primary key using a
sequence derived from incrementing v_count.
DECLARE
TYPE t_hire_date IS TABLE OF employees.hire_date%TYPE
INDEX BY BINARY_INTEGER;
v_hire_date_tab t_hire_date;
v_count BINARY_INTEGER := 0;
BEGIN
FOR emp_rec IN
(SELECT hire_date FROM employees)
LOOP
v_count := v_count + 1;
v_hire_date_tab(v_count) := emp_rec.hire_date;
END LOOP;
END;

PLSQL S5L2 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 12
Indexing Tables of Records
Using INDEX BY Table Methods
• You can use built-in procedures and functions (called
methods) to reference single elements of the INDEX
BY table, or to read successive elements.
• The available methods are:
EXISTS PRIOR
COUNT NEXT
FIRST DELETE
LAST TRIM

• You use these methods by dot-prefixing the method-


name with the table-name.

PLSQL S5L2 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 13
Indexing Tables of Records
Using INDEX BY Table Methods
This example demonstrates the method COUNT.
DECLARE
TYPE t_hire_date IS TABLE OF employees.hire_date%TYPE
INDEX BY BINARY_INTEGER;
v_hire_date_tab t_hire_date;
v_hire_date_count NUMBER(4);
BEGIN
FOR emp_rec IN
(SELECT employee_id, hire_date FROM employees)
LOOP
v_hire_date_tab(emp_rec.employee_id)
:= emp_rec.hire_date;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_hire_date_tab.COUNT);
END;

PLSQL S5L2 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 14
Indexing Tables of Records
INDEX BY Table of Records
• Even though an INDEX BY table can have only one data
field, that field can be a composite data type such as a
RECORD.
• This is an INDEX BY table of records.
• The record can be %ROWTYPE or a user-defined record.
• This example declares an INDEX BY table to store
complete rows from the EMPLOYEES table:
DECLARE
TYPE t_emp_rec IS TABLE OF employees%ROWTYPE
INDEX BY BINARY_INTEGER;
v_employees_tab t_emprec;

PLSQL S5L2 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 15
Indexing Tables of Records
INDEX BY Table of Records
• Individual fields within a table of records can be
referenced by adding an index value in parentheses after
the table of records name.
• Syntax: table(index).field
• Example: v_employees_tab(index).hire_date
The index value in the example could be an actual value
(ex. 1, 5, 12, etc.) or a reference to a value
(v_emp_rec_tab.LAST).

PLSQL S5L2 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 16
Indexing Tables of Records
Using an INDEX BY Table of Records
This example is similar to the earlier INDEX BY table
example, but stores the entire EMPLOYEES row and
displays the salary from each row.
DECLARE
TYPE t_emp_rec IS TABLE OF employees%ROWTYPE
INDEX BY BINARY_INTEGER;
v_emp_rec_tab t_emp_rec;
BEGIN
FOR emp_rec IN (SELECT * FROM employees) LOOP
v_emp_rec_tab(emp_rec.employee_id) := emp_rec;
DBMS_OUTPUT.PUT_LINE(
v_emp_rec_tab(emp_rec.employee_id).salary);
END LOOP;
END;

PLSQL S5L2 Copyright © 2018, Oracle and/or its affiliates. All rights reserved. 17
Indexing Tables of Records

S-ar putea să vă placă și