%TYPE or %ROWTYPE are derived types from other variables or tables. These
types are very convenient to declare when you want to declare the same types of
variables as the columns in a table or variables you already declared since it
reduces the possible type mismatch.
%TYPE (anchored type declaration)
Example:
first_name
customer.fname%TYPE;
--
anchoring at compile
time
last_name
varchar2(20);
another_name
last_name%TYPE;
User-defined subtypes
Syntax:
SUBTYPE
new_type IS original_type;
SUBTYPE num IS NUMBER; -- NUMBER(4) not allowed (constraint)
mynum num; -- num is a user-defined type name (number)
SUBTYPE nametype IS customer.fname%TYPE;
Records structure
TYPE student_rec_type IS RECORD (
--
student_rec_type is a record type
name
studentid
NUMBER(5),
first_name
VARCHAR2(20),
last_name
VARCHAR2(20)
);
student_a student_rec_type;
student_b
student_rec_type;
student_a.first_name :=
'Walker';
-- reference to first_name in the record
• Record assignment is fine if two record types are
matched
e.g., student_b := student_a;
Using %ROWTYPE for declaring records
Syntax: RecordName TableName%ROWTYPE;
%ROWTYPE is used to declare the same record type as all the column types defined in a table (since the row is basically a record).
e.g., mycust customer%ROWTYPE; -- mycust is a record name, customer is a table name
Once we defined a row type record variable, we can use all the field names in the table, e.g., customer in this case, mycust.name, mycust.age, etc.
==> %TYPE and %ROWTYPE are really handy when you handle the columns and rows in a database table in PL/SQL program.
Array in PL/SQL is basically a collection (similar to the collection in other languages like VB, Java or similar to Hash array in Perl). In PL/SQL, unlike other languages, you need to create an array type first before you use it.
Syntax: TYPE tabletype_name IS TABLE OF element_type [NOT
NULL]
INDEX
BY BINARY_INTEGER;
e.g., TYPE mytable IS TABLE OF student.first_name%TYPE INDEX BY BINARY_INTEGER;
names mytable;
--
two tables in DECLARE section
newNames mytable;
--
note that there is no size specification
names(10) := 'scott';
• PL/SQL Arrays are not like Arrays in C (it is more similar to the Iterator in C++, the Array in Java, or the Collection in VB).
Key (index) is BINARY_INTEGER, Value (table element), the number of elements is only limited to the size of BINARY_INTEGER, elements are not necessarily in any order
Names(0) := 'aaa';
--
key is 0, value is 'aaa'
Names(-5) := 'bbb';
--
key is -5, value is 'bbb'
Names(3) := 'ccc';
--
key is 3, value is 'ccc'
Names(15.45) := 'ddd'
-- 15.45
is converted to 16 automatically by ORACLE.
NewNames : = names;
-- table
assignment (actually this is an object assignment)
Array of structure
TYPE student_table IS TABLE OF
students%ROWTYPE INDEX BY BINARY_INTEGER;
mystudents student_table;
mystudents(100).first_name := 'tommy';
Characteristics of PL/SQL Tables
(a)
One-dimensional
A PL/SQL table
can have only one column (one-dimensional array).
(b) Unbounded or
Unconstrained
There is no
predefined limit to the number of rows in a PL/SQL table. The table grows
dynamically as you add more rows to
the table.
(c) Sparse
A row
exists in the table only when a value is assigned to that row. Rows do not have
to be defined sequentially.
(d) Homogeneous
elements
All rows in a table contain values
of the same data type.
(e) Indexed by integers (32 bit
BINARY_INTEGER)
You can have
that many of rows.
Table Attributes in array (table.attribute)
=>
attributes are ORACLE defined methods or properties, e.g., COUNT, DELETE,
EXISTS, FIRST, LAST, NEXT, PRIOR, etc. You
can use index (like C/C++) along with these attributes.
(COUNT returns number, DELETE returns n/a but delete, EXISTS returns boolean, FIRST, LAST, NEXT, PRIOR all return index)
tableName.COUNT
--
number of elements currently contained in the table
tableName.EXISTS(i)
--
check if ith row exists
tableName.DELETE(i)
--
deletes the ith row
tableName.DELETE(i,j)
--
deletes all rows between index i and j
tableName.NEXT(i)
--
next to ith row
tableName.PRIOR(i)
--
prior of ith row
• Index is better to be start with 1, 2, ….
-- for possible compatibility
with other language
• mystudents(100) := NULL;
--
delete everything in mystudents table but not
remove
--
element space
PL/SQL Execution Environment
Different PL/SQL Engines
PL/SQL engine on Server side
(SQL*PLUS), PL/SQL engine on client side (Oracle's tools like Developer/2000
forms, reports, … communicate with server through remote procedure calls)
What about SQL statements in PL/SQL blocks?
• PL/SQL
statements are executed on PL/SQL engine (either on server side or client
side)
• SQL statements are sent to SQL statement executor on Server.
Server-side PL/SQL (using
SQL*PLUS)
SQL*PLUS
• SQL*PLUS interacts with
Server
• In SQL, semicolon is the statement
terminator and the statement sent to the server.
• In PL/SQL block,
semicolon is a syntactic part of the block,
and slash, / is required to execute it.
Example PL/SQL
code
DECLARE
-- anonymous block
/* Declare variables to be used in the block.
*/
v_custid
NUMBER(3)
:= 222;
v_first
VARCHAR2(20)
:= 'john';
v_last
VARCHAR2(20)
:=
'smith';
v_city
VARCHAR2(20) := 'New York';
BEGIN
/* Add a row to the customer table,
using the values of the variables. */
INSERT INTO customer (customer_number, last, first, city) VALUES (v_custid, v_first, v_last, v_city);
END;
When you executes a PL/SQL program using SQL*PLUS, you need to type / to execute it. The semicolon does not work any more since the semicolon in PL/SQL is just a statement separator.
Bind variables
Syntax: VARIABLE
variable_name variable_type
--
VARIABLE is the command
e.g., SQL> VARIABLE my_name
VARCHAR2(20);
--
at SQL*Plus prompt
• Binded variables are valid from the SQL prompt
• Inside of PL/SQL block,
the variables are delimited by the leading colon, :
Within the PL/SQL block, use it preceded by colon, :
BEGIN
:my_name
:= 'Tom';
END;
• To see the content of a binded variable at SQL*PLUS, use the command, PRINT
variable_name, e.g., PRINT my_name;
• To see all variables defined -- use the
command, VARIABLE
e.g., SQL> variable vcount
NUMBER
SQL>
BEGIN
SELECT
COUNT(*) INTO
:vcount
FROM
students;
END;
/
--
to execute this PL/SQL block
SQL> PRINT vcount
--
to see the content of bind variables
Define variables -- usually used for report (or for environment
variables)
• DEFINE define_variable = values --
usually text value
• ACCEPT variable_name variable_type
PROMPT 'any prompt text'
ACCEPT my_company PROMPT 'Enter your company'
or
DEFINE
my_company = 'ORACLE'
• To see all the defined variables -- use the command,
DEFINE
• To remove a defined variable -- use the command,
UNDEFINE variable_name
Within the PL/SQL block, you can
reference the defined variables preceded by & with ' '
Substitution variables (or Macro variable)
• Define a variable preceded by
&
-- used for values (R-value), not for LHS variable (L-value)
SELECT
company_name
FROM company
WHERE company_id = '&my_company';
• The following set commands will disable the 'Old and New' prompt
SET VERIFY OFF
SET ECHO OFF
Binded variables, Defined variables only exist within a
session!
Substitution variables (no memory allocated)
• PL/SQL does not have capabilities for input or output from/to I/O devices -- so DBMS_OUTPUT (built-in package) and UTL_FILE (for file I/O with operating system) are used. Here DBMS_OUTPUT and UTL_FILE are Oracle built-in packages. There are many other packages including DBMS_SQL package for dynamic PL/SQL which we can execute a SQL statement in a string, like in other programming languages like Visual Basic, Java, etc.
• Input is accomplished through substitution variables (processed by SQL*PLUS
like C macros)
&var_name
studentID students.id%TYPE := &student_id;
SELECT &columns FROM
classes;
Show ERRORS command
Any compile errors are stored in the user_errors data
dictionary view
SHOW ERRORS
--
to see errorsS
Input from keyboard: use substitution
variables
Output to screen: use DBMS_OUTPUT
package or bind variables (print command)
DBMS_OUTPUT package -- input/output with buffer
PUT routines: PUT (number or date), PUT_LINE(varchar,
number, date),
NEW_LINE
procedures.
The difference between PUT and PUT_LINE is that PUT_LINE can handle prints a
carriage return
PUT
does not.
GET routines: GET, GET_LINES.
ENABLE and DISABLE control the buffer
The important point to remember is that the dbms_output package is using an in-memory buffer as it's target, it is not actually doing any 'real' output. If you want to display something on the screen, type this setting command first:
SET SERVEROUTPUT ON [SIZE buffer_size]
The SIZE buffer_size command limits the buffer available to your session. The size can be between 2,000 and 1,000,000 bytes (watch out for the side-effect of multi-byte character sets) although SQL*Plus and svrmgr seem to disagree on whether you can actually use the value 1,000,000. The default size is 2,000.
For example, within the PL/SQL block,
begin
DBMS_OUTPUT.PUT_LINE('Hello, world'); -- will print the "Hello, world"
end;
If you use a simple set serveroutput on in SQL*Plus, then leading spaces are trimmed, and the newline() command is ignored, so your carefully planned output easily becomes unreadable.
In SQL*Plus, the same command will work, but the presentation rules are different and you are better off using the command:
set serveroutput on format wrapped size 5000
Usually DML statements (SELECT, INSERT, UPDATE, DELETE) or Transaction control statements (COMMIT, ROLLBACK, SAVEPOINT) are allowed in PL/SQL program (except for using a package, DBMS_SQL in v2.1 or higher) -- since PL/SQL intentionally designed to use early binding. The DDL commands are possible through the dynamic PL/SQL.
SELECT field_list INTO PL/SQL record or variable
FROM
table_list
WHERE condition;
=> The SELECT statement should return no more than one row/value, otherwise it returns error.
DECLARE
Student_record
students%ROWTYPE;
v_department
class.department%TYPE
BEGIN
SELECT
*
INTO
student_record
FROM
students
WHERE
id =
1111;
SELECT
department
INTO
v_department
FROM
classes
WHERE
room_id = 999;
END;
Syntax for the other statements (INSERT INTO … VALUES, UPDATE,
DELETE)
are the same, for example, you can use INSERT command as
follows:
SQL> CREATE SEQUENCE student_sequence START WITH 100
BEGIN
INSERT
INTO students (id, first_name, last_name)
VALUES (student_sequence.NEXTVAL,
'john', 'smith');
END;