Cover | Table of Contents | Colophon
http://www.construx.com site, along with his
book, Code Complete (Microsoft Press), offers
checklists on coding style, naming conventions and rules, and module
definitions.
|
Datatype
|
Issues and Recommendations
|
|---|---|
|
NUMBER
|
If you don't specify a precision, as in NUMBER(12,2), Oracle
supports up to 38 digits of precision. If you don't need this
precision, you're wasting memory.
|
|
CHAR
|
This is a fixed-length character string and is mostly available for
compatibility purposes with code written in earlier versions of
Oracle. The values assigned to CHAR variables are right-padded with
spaces, which can result in unexpected behavior. Avoid CHAR unless
it's specifically needed.
|
|
VARCHAR
|
This variation on the VARCHAR2 variable-length declaration is
provided by Oracle for compatibility purposes. Eschew VARCHAR in
favor of VARCHAR2.
|
|
VARCHAR2
|
The greatest challenge you will run into with VARCHAR2 is to avoid
the tendency to hard-code a maximum length, as in VARCHAR2(30). Use
%TYPE and SUBTYPE instead, as described later in this chapter.
Also, prior to Oracle8, VARCHAR2 variables are treated like
variable-length strings for purposes of manipulation and evaluation,
but Oracle does allocate the full amount of memory upon declaration.
If you declare a variable of VARCHAR2(2000), then Oracle allocates
2000 bytes, even if you use only three.
|
|
INTEGER
|
If your integer values fall within the range of
–231+1 .. 231–1
(a.k.a. –2147483647 .. 2147483647), you should declare your variables
as PLS_INTEGER. This is the most efficient format for integer
manipulation.
|
|
Datatype
|
Issues and Recommendations
|
|---|---|
|
NUMBER
|
If you don't specify a precision, as in NUMBER(12,2), Oracle
supports up to 38 digits of precision. If you don't need this
precision, you're wasting memory.
|
|
CHAR
|
This is a fixed-length character string and is mostly available for
compatibility purposes with code written in earlier versions of
Oracle. The values assigned to CHAR variables are right-padded with
spaces, which can result in unexpected behavior. Avoid CHAR unless
it's specifically needed.
|
|
VARCHAR
|
This variation on the VARCHAR2 variable-length declaration is
provided by Oracle for compatibility purposes. Eschew VARCHAR in
favor of VARCHAR2.
|
|
VARCHAR2
|
The greatest challenge you will run into with VARCHAR2 is to avoid
the tendency to hard-code a maximum length, as in VARCHAR2(30). Use
%TYPE and SUBTYPE instead, as described later in this chapter.
Also, prior to Oracle8, VARCHAR2 variables are treated like
variable-length strings for purposes of manipulation and evaluation,
but Oracle does allocate the full amount of memory upon declaration.
If you declare a variable of VARCHAR2(2000), then Oracle allocates
2000 bytes, even if you use only three.
|
|
INTEGER
|
If your integer values fall within the range of
–231+1 .. 231–1
(a.k.a. –2147483647 .. 2147483647), you should declare your variables
as PLS_INTEGER. This is the most efficient format for integer
manipulation.
|
IF total_sal BETWEEN 10000 AND 50000 AND
emp_status (emp_rec.empno) = 'N' AND
(MONTHS_BETWEEN
(emp_rec.hiredate, SYSDATE) > 10)
THEN
give_raise (emp_rec.empno);
END IF;
IF eligible_for_raise (totsal, emp_rec) THEN give_raise (emp_rec.empno); END IF;
DECLARE
eligible_for_raise BOOLEAN :=
total_sal BETWEEN 10000 AND 50000 AND
emp_status (emp_rec.empno) = 'N' AND
(MONTHS_BETWEEN
(emp_rec.hiredate, SYSDATE) > 10);
BEGIN
IF eligible_for_raise
THEN
give_raise (emp_rec.empno);
END IF;
http://oracle.oreilly.com/utplsql: utPLSQL, a unit test framework for PL/SQL developers.
CREATE OR REPLACE PACKAGE constants
IS
-- Standard string representation of TRUE/FALSE
tval CONSTANT CHAR(1) := 'T';
fval CONSTANT CHAR(1) := 'F';
-- Earliest valid date: 5 years past
min_date CONSTANT DATE :=
ADD_MONTHS (SYSDATE, -5 * 12);
CREATE OR REPLACE PACKAGE nightly_transform IS c_max_weeks CONSTANT INTEGER := 54; c_active CONSTANT CHAR(1) := 'A'; c_inactive CONSTANT CHAR(1) := 'I'; c_english CONSTANT INTEGER := 1; c_usa CONSTANT INTEGER := 1; c_namerica CONSTANT VARCHAR2(2) := 'N'; END nightly_transform;
IF condA THEN ... ELSIF condB THEN ... ELSIF condN THEN ... ELSE ... END IF;
PROCEDURE process_lineitem (line_in IN INTEGER)
IS
BEGIN
IF line_in = 1
THEN
process_line1;
END IF;
IF line_in = 2
THEN
process_line2;
END IF;
...
IF line_in = 2045
THEN
process_line2045;
END IF;
END;
PROCEDURE process_lineitem (line_in IN INTEGER)
IS
BEGIN
IF line_in = 1
THEN
process_line1;
ELSIF line_in = 2
THEN
process_line2;
...
ELSIF line_in = 2045
THEN
process_line2045;
END IF;
END;
IF condA THEN ... ELSIF condB THEN ... ELSIF condN THEN ... ELSE ... END IF;
PROCEDURE process_lineitem (line_in IN INTEGER)
IS
BEGIN
IF line_in = 1
THEN
process_line1;
END IF;
IF line_in = 2
THEN
process_line2;
END IF;
...
IF line_in = 2045
THEN
process_line2045;
END IF;
END;
PROCEDURE process_lineitem (line_in IN INTEGER)
IS
BEGIN
IF line_in = 1
THEN
process_line1;
ELSIF line_in = 2
THEN
process_line2;
...
ELSIF line_in = 2045
THEN
process_line2045;
END IF;
END;
l_count := titles.COUNT;
FOR indx IN 1 .. l_rowcount
LOOP
IF l_match_against = titles(indx)
THEN
RETURN indx;
END IF;
END LOOP;
RAISE Exit_Function;
EXCEPTION
WHEN Exit_Function THEN RETURN NULL;
END;
DECLARE
TYPE account_tabtype IS TABLE
OF account%ROWTYPE INDEX BY BINARY_INTEGER;
l_accounts account_tabtype;
l_name VARCHAR2(2000) :=
min_balance_account (SYSDATE);
BEGIN
IF balance_too_low (1056)
THEN
use_collection (l_accounts);
use_name (l_name);
ELSE
-- No use of l_accounts or l_name
...
END IF;
END;
BEGIN
IF balance_too_low (1056)
THEN
DECLARE
TYPE account_tabtype IS TABLE
OF account%ROWTYPE
INDEX BY BINARY_INTEGER;
l_accounts account_tabtype;
l_name VARCHAR2(2000) :=
min_balance_account (SYSDATE);
BEGIN
use_collection (l_accounts);
use_name (l_name);
END;
ELSE
-- No use of l_accounts or l_name
...
END IF;
END;
CREATE OR REPLACE PROCEDURE assert (
condition_in IN BOOLEAN,
message_in IN VARCHAR2,
raise_exception_in IN BOOLEAN := TRUE,
exception_in IN VARCHAR2
:= 'VALUE_ERROR'
)
IS
BEGIN
IF NOT condition_in
OR condition_in IS NULL
THEN
pl ('Assertion Failure!');
pl (message_in);
IF raise_exception_in
THEN
EXECUTE IMMEDIATE
'BEGIN RAISE ' || exception_in || '; END;';
END IF;
END IF;
END assert;
BEGIN
assert (isbn_in IS NOT NULL,
'The ISBN must be provided.');
assert (page_count_in < 2000,
'Readers don't like big, fat books!');
CREATE OR REPLACE FUNCTION book_title (
isbn_in IN book.isbn%TYPE)
RETURN book.title%TYPE
IS
l_ title book.title%TYPE;
BEGIN
SELECT title INTO l_title
FROM book
WHERE isbn =isbn_in;
RETURN l_rec.title;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
RETURN NULL;
END;
CREATE OR REPLACE PACKAGE overdue
IS
excessive_lateness EXCEPTION;
PRAGMA EXCEPTION_INIT (
excessive_lateness, -20700);
fetch_out_of_sequence EXCEPTION;
PRAGMA EXCEPTION_INIT (
fetch_out_of_sequence, -1003);
CREATE OR REPLACE PROCEDURE show_fav_flavor (
pref_type IN VARCHAR2)
IS
pref VARCHAR2(100);
BEGIN
SELECT preference INTO pref
FROM personal_preferences PP
WHERE PP.pref_type = pref_type;
pl (pref);
END;CREATE OR REPLACE PROCEDURE show_fav_flavor (
pref_type IN VARCHAR2)
IS
pref VARCHAR2(100);
BEGIN
SELECT preference INTO pref
FROM personal_preferences PP
WHERE PP.pref_type = pref_type;
pl (pref);
END;
CREATE OR REPLACE PROCEDURE show_fav_flavor (
pref_type_in IN VARCHAR2)
SELECT preference INTO pref
FROM personal_preferences PP
WHERE PP.pref_type = show_fav_flavor.pref_type;
BEGIN
SELECT title INTO l_title -- HARD-CODED
FROM book -- QUERY...
WHERE isbn =isbn_in; -- BAD IDEA!
PACKAGE te_book
IS
FUNCTION title (isbn_in IN book.isbn%TYPE)
RETURN book.title%TYPE;
BEGIN l_title := te_book.title (isbn_in);
INSERT INTO book ( isbn, title, author) VALUES ( '1-56592-675-7', 'Oracle PL/SQL Programming Guide to Oracle8i Features', 'Feuerstein, Steven');
add_book ( '1-56592-675-7', 'Oracle PL/SQL Programming Guide to Oracle8i Features', 'Feuerstein, Steven');
te_book.ins ( '1-56592-675-7', 'Oracle PL/SQL Programming Guide to Oracle8i Features', 'Feuerstein, Steven');
DECLARE
dyncur PLS_INTEGER := DBMS_SQL.open_cursor;
BEGIN
-- Whoops, forget the FROM clause!
DBMS_SQL.parse (
dyncur, 'select * dual', DBMS_SQL.native);
END;
CREATE OR REPLACE FUNCTION open_and_parse (
dynsql_in IN VARCHAR2,
dbms_mode_in IN INTEGER := NULL)
RETURN INTEGER
IS
dyncur INTEGER;
BEGIN
dyncur := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE (dyncur, dynsql_in,
NVL (dbms_mode_in, DBMS_SQL.NATIVE));
RETURN dyncur;
EXCEPTION
WHEN OTHERS
THEN
DBMS_SQL.CLOSE_CURSOR (dyncur);
pl (SQLERRM);
pl (dynsql_in);
RETURN NULL;
END;
/
CREATE OR REPLACE TRIGGER are_you_too_young
AFTER insert OR update
ON borrower FOR EACH ROW
BEGIN
IF :new.date_of_birth >
ADD_MONTHS (SYSDATE, -12 * 10)
THEN
RAISE_APPLICATION_ERROR (
-20703,
'Borrower must be at least 10 yrs old.');
END IF;
END;
/CREATE OR REPLACE TRIGGER are_you_too_young
AFTER insert OR update
ON borrower FOR EACH ROW
BEGIN
IF :new.date_of_birth >
ADD_MONTHS (SYSDATE, -12 * 10)
THEN
RAISE_APPLICATION_ERROR (
-20703,
'Borrower must be at least 10 yrs old.');
END IF;
END;
/
BEGIN
...
IF ADD_MONTHS (SYSDATE, -122) > rec.date_of_birth
THEN
err.log ('Borrower ' || rec.borrower_id ||
' is not ten years old.');
ELSE
...load the data
RETURN return value;
IF all_done THEN RETURN; END IF;
CREATE OR REPLACE FUNCTION status_desc (
cd_in IN VARCHAR2
)
RETURN VARCHAR2
IS
BEGIN
IF cd_in = 'C'
THEN
RETURN 'CLOSED';
ELSIF cd_in = 'O'
THEN
RETURN 'OPEN';
ELSIF cd_in = 'I'
THEN
RETURN 'INACTIVE';
END IF;
END;
ORA-06503: PL/SQL: Function returned without value
CREATE OR REPLACE TRIGGER check_employee_age
BEFORE INSERT OR UPDATE ON employee
BEGIN
IF ADD_MONTHS (SYSDATE, -216) < :NEW.hire_date
THEN
RAISE_APPLICATION_ERROR (-20706,
'Employee must be 18 to work here!');
END IF;
END;
CREATE OR REPLACE TRIGGER check_employee_age
BEFORE INSERT OR UPDATE ON employee
BEGIN
IF employee_rules.emp_too_young (:NEW.hire_date)
THEN
err_pkg.rase (employee_rules.c_errnum_emp_too_young,
:NEW.employee_id);
END IF;
END;