Professional Documents
Culture Documents
OracleProf - 2004 - 11
OracleProf - 2004 - 11
I
recently returned from a week in the United Kingdom teaching PL/SQL to
several hundred developers and DBAs. My time with these fans of PL/SQL
reinforced for me an unfortunate set of circumstances: Most programmers
are still not taking advantage of some of the most important new features in November 2004
PL/SQL—new as of Oracle8i, that is!
Volume 11, Number 11
Truly, we write code in rather deep ruts established over several or many
more years of programming. Truly, our managers didn’t often give us the time 1 Having Fun with
or space to explore new features in a language. Sadly, that puts so many of us PL/SQL Collections
at a severe disadvantage when writing new programs. Steven Feuerstein
Given that situation, I’ve decided to devote this article to sharing with
8 Alphanumeric Sequences
you the fun I have with PL/SQL collections. If you’re a regular reader of this in Oracle
newsletter, these list/array/table-like structures shouldn’t be new discoveries. Anunaya Shrivastava
Yet, I’m still willing to wager that you don’t use them as often or in as varied a and Alok Kundu
fashion as you could.
10 Working with
Rather than take up space explaining the syntax of declaring and Multi-Table Inserts
manipulating collections, I’m going to offer a few scenarios that I’ve Al Hetzel
encountered recently in which I found collections to be very helpful. Perhaps
they’ll inspire you to do the same with your own applications. 15 Tip: Foreign Key
Relationship Options
Dan Clamage
Avoid those long IF statements
My article in last month’s issue of Oracle Professional explored my efforts to 16 November 2004 Downloads
“fix” DBMS_UTILITY.NAME_RESOLVE, and make sure that it worked
correctly under all circumstances. DBMS_UTILITY.NAME_RESOLVE accepts
the name or possible name of a database object and resolves it into each of its
component pieces, including a “part type.” The part type is an integer value Indicates accompanying files are available online at
www.pinnaclepublishing.com.
Continues on page 3
D:
GE
A GE
PA LED
L W
FUL KNO
BAL
GLO
been introduced. For a given schema, object name, and alias name, set the
value in the cache.
Well... in this case, I must admit that I don’t really
FUNCTION get_value
have a distinct regression test for my code generation For a given schema, object name, and alias name, return
engine. On the other hand, I use Qnxo generation the value from the cache.
extensively myself to generate Qnxo code, and the
templates I use exercise the generation engine very
thoroughly. So I can make a change, generate a new set Listing 1. The sg_object_alias specification.
of package APIs, and compare those to the previous,
CREATE OR REPLACE PACKAGE sg_object_alias
current set of packages. Not bad, really. IS
Having, to some extent, conquered my fears, I SUBTYPE value_t IS VARCHAR2 (32767);
bypass the sg_doir table and instead use collections. END sg_object_alias;
/
• An alias value is set by and retrieved according to
an object name-alias name string. In other words,
A very simple API. Now, how do I go about
the index into my alias value is a string and not a
implementing such a cache? The answer varies greatly
number or integer.
and unfortunately depends on the version of Oracle
you’re running. If you’re running on Oracle9i Release
The first two points steer me in the direction of
2 and above, you can take advantage of string-based
creating an entirely separate package from sg_doir, which
indexes in your collections, which makes the
I’ll call sg_object_alias. That way, I automatically isolate
implementation of this package almost transparently
the bulk of coding activity. Even before I fully implement
easy. In Oracle8i, I can only index collections by integers.
this approach, I can easily define the specification—the
That means that I’ll need to implement a hashed index,
programs I’ll need to use my cache. The package
converting each string to an integer and using that as the
specification is shown in Listing 1 and described here:
key to my alias value.
SUBTYPE value_t
I define my own application datatype for the value of Easy and quick: String-based indexes
an alias. This just helps improve the self-documentation Assuming you’re on an Oracle9i or above version of
level of my code. Oracle, we can very easily implement the sg_object_alias
package body. Here are the steps I followed:
PROCEDURE clear_aliases Step 1: Define the collection to hold the alias values.
If I’m going to build a cache, I can easily realize in
advance that I’ll want to have a way to clear out CREATE OR REPLACE PACKAGE BODY
sg_object_alias
the cache. IS
c_delimiter CONSTANT CHAR(1) := '^';
FUNCTION is_object_alias SUBTYPE alias_name_t IS VARCHAR2 (200);
Since I only want to use this new approach in a limited
TYPE alias_values_tt IS
number of cases, I need a function to help me determine if TABLE OF VARCHAR2(32767)
I do indeed have one of those cases. This function returns INDEX BY alias_name_t;
TRUE if the alias that’s being set is an object-level alias g_alias_values alias_values_tt;
Alphanumeric Sequences
in Oracle
Anunaya Shrivastava and Alok Kundu
In this article, Anunaya Shrivastava and Alok Kundu define Let’s say we want to create a four-digit alphanumeric
techniques for developing alphanumeric sequences that can sequence. There are multiple ways the sequence can start.
be used in your Oracle database applications. For instance, the sequence can start as A000, or 100A, or
AAAA, or 0000. However, whatever sequence you come
T
HE term sequence means a succession of things in up with, it has to follow the aforementioned sequence
a definite order, size order, or logical order. In rules. The sequence should also work with all Oracle
mathematics, a sequence is a list of objects (or built-in functions. The sorting on VARCHAR2 datatype
events) that have been ordered in a sequential fashion, columns containing numeric or date data behaves in an
such that each member either comes before, or after, every unexpected manner. Keeping that factor in mind, an
other member. Sequences are needed in software database alphanumeric sequence in Oracle should:
implementations to uniquely identify a record. Oracle • Follow the Order by clause.
introduced numeric sequences back in Oracle 7. First, • Support the MAX function (SQL Max) and the
you create a numeric sequence as a database object, GREATEST function.
and then you can use it for generating your sequential • Support relational operators (=, >, <).
numbers. The numeric sequence has attributes— • Generate the next element of the sequence.
minimum and maximum value of the sequence, cache
size, recycle mode, and so on. However, Oracle doesn’t To accommodate these factors, our sequence should
provide any sequences that support alphanumeric start at 0000 and end at ZZZZ. The point of inflection is
characters, though you may have some physical entities 999A, where the sequence changes from numeric to
where numeric sequences don’t suffice. Examples of alphanumeric, from 9999 to 999A. Another point of
where alphanumeric sequences might be useful are: inflection is AAAA, where the sequence changes from
• In building interfaces with legacy code where the alphanumeric to completely alphabetical, from 9ZZZ to
primary keys are non-numeric. AAAA. The last element of the sequence is ZZZZ.
• When you have limited space in a column in which The reason this sequence was developed with the
you want to accommodate a lot of data elements— pattern described in Figure 1—from numeric to
more than what a numeric series can offer. alphanumeric to alphabetical—was to support most of
• To maintain some alphanumeric codes in certain the Oracle built-in functions. If we were to develop a
places, such as in a library where you want to sequence 000A, 000B, 000C or any other way, the sequence
maintain book codes (for example, 999R). would run into issues with standard Oracle functions.
The data element 0001 will be greater than 000A in this
To develop an alphanumeric sequence, we need to series. However, by Oracle functioning, the default
keep some basic tenets of numeric sequences in mind. sorting would put 0001 before 000A. Also, the MAX
The alphanumeric sequence should follow general function will bring an unexpected value from such a
mathematical induction principles. If a statement is true sequence. So this series won’t be able to support the
for all n elements in the domain, the following statements default sorting on a VARCHAR2 column in Oracle.
must be maintained: Therefore, we took the approach displayed in Figure 1.
• The statement is true for n=1 (first element of We developed the functions GET_CURRENT_VAL, to
the sequence). generate the current sequence element in the database,
• If the statement is true for n= k, then it has to be true and GET_NEXT_VALUE, to give us the next sequence
for n = k+1 (subsequent elements of the sequence). element. These are displayed in Listing 1 and Listing 2.
End LOOP;
IF v_max_numeric =-5 then
dbms_output.put_line('The num of elements is'||v_cnt);
--Extract the 1st byte of the digits
v_inp_str_char1 := SUBSTR(v_inp_str, 1, 1); end;
--Extract the 1st byte of the input string /
v_inp_str_char2:= SUBSTR (v_inp_str, 2, 1);
--Extract the 2nd byte of the input string The num of elements is 485253.
v_inp_str_char3 := SUBSTR (v_inp_str, 3, 1);
--Extract the 3rd byte of the input string
v_inp_str_char4 := SUBSTR (v_inp_str, 4, 1); Future scope
--Extract the 4th byte of the input string
IF v_inp_str_char4 = '9' THEN
We’ve just given you a simple example with some items
v_inp_str_char4 := 'A'; to consider when working with non-numeric sequences.
ELSE
--If the 4th byte of input string is '9' then set it You can enhance this methodology by developing a
--to 'A'. Else if the 3rd byte of input string is Z system that expands the aforesaid approach to any
--then set it to 0 and increment the 1st or 2nd byte.
--Otherwise increment the 3rd byte only. number of digits for generating an alphanumeric
IF v_inp_str_char4 = 'Z' THEN
v_inp_str_char4 := 'A'; sequence that meets your specific requirements.
IF v_inp_str_char3= '9' THEN
v_inp_str_char3:= 'A';
ELSE Key benefit
--If the 3rd byte of input string is '9' then set it
--to 'A'. Else if the 3rd byte of input str is Z The biggest advantage of having an alphanumeric
--then set it to 0 and increment the 1st or 2nd byte. sequence is that you can have many more data elements
--Otherwise increment the 3rd byte only.
in the alphanumeric sequence than in a numeric sequence
IF v_inp_str_char3 = 'Z' THEN
v_inp_str_char3 := 'A'; of comparable size. Using our example with four digits,
the number of data elements that you can have in a
--If the 2nd byte of input string is '9' then
--set it to 'A'. Else if the 2nd byte of input str numeric sequence is 9999 (excluding 0000), but in an
--is Z then set it to 0 and increment the 1st byte.
--Otherwise increment the 2nd byte only.
alphanumeric series that follows the Oracle sorting rules
(refer to Figure 1) you can have 485,253 data elements
IF v_inp_str_char2 = '9'THEN
v_inp_str_char2 := 'A'; (excluding 0000)—almost 49 times the number of
ELSE elements that you can have with a numeric sequence.
IF v_inp_str_char2 = 'Z' THEN
v_inp_str_char2 := 'A'; Continues on page 14
C
OMMITS seem to be such a simple command. When inserted into every table listed. Otherwise, it will only be
you want to save the data changes, you simply type inserted into the tables where the conditions are met.
in “commit” and a semicolon and press Enter. Or, To specify a first insert, you use the keyword FIRST.
you add them to a procedure in order to save the data A first insert will check every line of data against all of
changes made in that procedure. However, as simple as the conditions as well. However, it will stop once one of
the commit is, it can cause you some major problems, the conditions is met. If you specify a first insert, you
particularly when you’re inserting new rows. need at least one condition.
The problem is data integrity. Each row of data may
need to be inserted into many different tables. Unless you [when {condition}]
violate some foreign key, Oracle won’t tell you that you
forgot a table. Instead, you’ll be blissfully unaware until This line of the multi-table insert is the condition
the omission is found. line. Since you can create an insert without any
You may never make a mistake like the one I’ve conditions, this line is optional. However, if you do
just described. However, it’s unlikely that you’re the include one or more conditions, you have to do so in the
only person who’s using your database. Non-Oracle format shown. Each condition line starts with the WHEN
programmers are adding records all the time. It doesn’t keyword followed by the actual condition statement. This
make a difference whether these are Web developers or statement is a Boolean value that generally relates to the
client/server developers, as you’re responsible for line of data being processed. For instance, if you were
making sure that their data is correct. inserting customer data and put the condition line “when
With Oracle9i and 10g, you have a new type of insert customer_id < 100”, then only the customers that had an
that will solve many of these issues: the multi-table insert. id less than 100 would be subject to the into statement(s)
that follow.
Command format You can have as many condition lines as you need. If
As you can probably figure out from the name, you can you have a first insert, you can also use the ELSE keyword
use a multi-table insert to insert data into several tables at to create a special condition. This condition will only be
the same time. The complete format for this command is met if all of the other conditions have failed. It’s a catch-
shown in Listing 1. all condition.
Now let’s look at the next statement:
Listing 1. Command format for the multi-table insert. into {table_name}
( {column_names} )
INSERT {all|first} values
[WHEN {condition}] ( {select_column_names} )
INTO {table_name}
( {column_names} )
VALUES These four lines of code make up the INTO clause. In
( {select_column_names} )
… other words, these lines determine exactly what data will
{select_statement}; be inserted into which table. You’ll probably notice that
the format of these lines is very similar to a regular insert
Before getting to some examples, let’s take a closer statement. The differences are detailed in the remainder of
look at the different parts of the multi-table insert. this section.
insert {all|first} The first line determines where the data will go. You
start this line with the INTO keyword, and then you need
Since this is an insert statement, you start the to list a table name. This table can be any table on the
Performance issues
Another reason to use a multi-table insert is the
performance gains. In the six inserts shown previously,
each statement uses a similar select statement to provide
the data for the insert. This means that each insert will
have to complete a full access on the customer_load table.
Since this is a loader table, it could have a huge amount of
data on it. Cycling through this potentially large amount Figure 1. Comparing the explain plans.
Suppose the relationship you wish to enforce is that when you || SQL%ROWCOUNT);
END;
delete from table A, you must also delete the child rows from /
table B; but if you attempt to delete from table B directly, you
must forbid it. To accommodate this, I’ll create the tables and To see what the call stack looks like when I delete from B, I’ll
insert some data: create a trigger and then delete a row.
Pinnacle, A Division of Lawrence Ragan Communications, Inc. ▲ 800-493-4867 x.4209 or 312-960-4100 ▲ Fax 312-960-4106
BEGIN
DELETE FROM b Remove the dbms_output from the triggers and you’re
WHERE fk_a = 2;
dbms_output.put_line('anon: #B rows deleted: ' good to go. ▲
|| SQL%ROWCOUNT);
EXCEPTION WHEN app_except.e_delete THEN
dbms_output.put_line('not called from the trigger!'); Dan Clamage has been working with Oracle and especially PL/SQL since
END; 1995. He lives in Pittsburgh, PA, with his wife, Robin, and their two
/
b: ----- PL/SQL Call Stack ----- daughters, Patricia and Daniele. danielj@clamage.com.
For access to current and archive content and source code, log in at www.pinnaclepublishing.com.
Phone: 800-493-4867 x.4209 or 312-960-4100 Copyright © 2004 by Lawrence Ragan Communications, Inc. All rights reserved. No part
Fax: 312-960-4106 of this periodical may be used or reproduced in any fashion whatsoever (except in the
Email: PinPub@Ragan.com case of brief quotations embodied in critical articles and reviews) without the prior
written consent of Lawrence Ragan Communications, Inc. Printed in the United States
of America.
Advertising: RogerS@Ragan.com
Oracle, Oracle 8i, Oracle 9i, PL/SQL, and SQL*Plus are trademarks or registered trademarks of
Editorial: FarionG@Ragan.com Oracle Corporation. Other brand and product names are trademarks or registered trademarks
of their respective holders. Oracle Professional is an independent publication not affiliated
Pinnacle Web Site: www.pinnaclepublishing.com with Oracle Corporation. Oracle Corporation is not responsible in any way for the editorial
policy or other contents of the publication.
Subscription rates This publication is intended as a general guide. It covers a highly technical and complex
subject and should not be used for making decisions concerning specific products or
applications. This publication is sold as is, without warranty of any kind, either express or
United States: One year (12 issues): $199; two years (24 issues): $348 implied, respecting the contents of this publication, including but not limited to implied
Other:* One year: $229; two years: $408 warranties for the publication, performance, quality, merchantability, or fitness for any particular
purpose. Lawrence Ragan Communications, Inc., shall not be liable to the purchaser or any
other person or entity with respect to any liability, loss, or damage caused or alleged to be
Single issue rate:
caused directly or indirectly by this publication. Articles published in Oracle Professional
$27.50 ($32.50 outside United States)* reflect the views of their authors; they may or may not reflect the view of Lawrence Ragan
Communications, Inc. Inclusion of advertising inserts does not constitute an endorsement by
* Funds must be in U.S. currency. Lawrence Ragan Communications, Inc., or Oracle Professional.