An Introduction to PL/SQL

John C. Lennon, Dulcian, Inc.

Introduction

PL/SQL is Oracle’s procedural language extension to the SQL language, It has its origins in ADA, a high-level programming language developed for the US Department of Defense. ADA was named after a daughter of Lord Byron, Augusta ADA, Countess of Lovelace, a 19th century mathematician, whom many regard as the world’s first computer programmer because of her work with Charles Babbage’s Analytical Engine.

PL/SQL introduces 3GL-style procedural logic into the world of declarative logic. Its first incarnation was in 1991 as the ‘procedural option’ for Oracle RDBMS Version 6.0. At about the same time, PL/SQL debuted on the client side in SQL*Forms Version 3. On both client and server, it was very basic in its functionality, but it was still warmly welcomed by the Oracle development community. From these humble beginnings, PL/SQL has evolved into a powerful, complex, and flexible programming tool. Unfortunately, the client-side versions did not kept pace with the server versions for some years. Only PL/SQL Versions 1.x were supported, ­ much to the frustration of many developers. However, since the introduction of Developer/2000 Release 2, the full range of features offered by PL/SQL Version 2.3 and now version 8 are also supported by Oracle’s development tools.

Today, PL/SQL is an extremely powerful structured language, which is not only tightly integrated with SQL but also incorporates many of the advanced features of other procedural languages. It provides the foundation for distributing and processing transactions across the network for both client/server and Web applications. Despite its structured approach, PL/SQL it is also a language that , is very easy to abuse and misuse. It is possible to produce code that works, but is highly inefficient and virtually impossible to maintain.

As the development environment leans towards the web, PL/SQL may be is in its death throes; However, there is a lot of PL/SQL used in production applications, which will  not be going away any time soon. It is still the language of choice for many Oracle developers..

Structure

PL/SQL is an algorithmic, block-structured language. A block is the basic unit from which all PL/SQL programs are built. A block can be named (functions and procedures) or anonymous. An anonymous block can exist only within a named block, a SQL script, or a trigger. A block consists of between one and four sections, as illustrated in Table 1.

Header

For named blocks only. Includes a return declaration for functions and optionally IN and/or OUT parameter declarations.

Declaration Section

Declares variables, cursors etc. to be used in the block.

BEGIN

Executable code

Exception Section (optional)

Exception (error) handling

END;

 

Table 1: Sections of a block

Note that only the executable section is mandatory and only named blocks have a header.

Sections of the PL/SQL Block

The following are descriptions of the PL/SQL block sections.

The Header

For named blocks the header contains the type and name of the block and optionally the names and data types of parameters (arguments). A procedure may have IN, OUT, or IN/OUT parameters. A function normally has only IN parameters. If a function is to be embedded in SQL, then OUT and IN/OUT parameters are not permitted. The header for a function must end with a RETURN statement declaring the datatype of the return value.

Declaration section

The declaration section is where variables, constants, cursors, and other objects such as PL/SQL tables are declared.

Executable section

The executable section contains the action(s) to be performed, i.e. the executable PL/SQL statements. There must be at least one executable statement in this section.

 


Exception section

The exception section traps warning and error conditions and programmer-defined exceptions. The section is optional. If it is omitted, exceptions will be trapped by the exception section in an enclosing block, if there is one. Otherwise,  PL/SQL will raise an unhandled exception error.

Procedures and Functions

As mentioned earlier, a function or procedure is a named PL/SQL block. It may optionally contain nested anonymous blocks that should reflect the logical flow of the program and/or provide for finer control of exception handling.

Code samples 1 and 2 show the same function constructed in two different ways. Sample 1 has single block and Sample 2 has a second, nested block. In Sample 2 the cursor cur_Salary is declared in the outer, or enclosing block.

 

FUNCTION get_salary(p_Employee_ID IN employee.salary%TYPE

 RETURN NUMBER

IS

CURSOR cur_Salary IS

  SELECT salary

    FROM employee

   WHERE employee_id = p_Employee_ID;

 v_Salary employee.salary%TYPE;

 BEGIN

   OPEN cur_Salary;

     FETCH cur_Salary INTO v_Salary;

   CLOSE cur_Salary;

   RETURN v_Salary;

END get_salary;

Code Sample 1: Single block

 

FUNCTION get_salary(p_Employee_ID IN employee.salary%TYPE

 RETURN NUMBER

IS

CURSOR cur_Salary IS

  SELECT salary

    FROM employee

   WHERE employee_id = p_Employee_ID;

BEGIN

  DECLARE

     v_Salary employee.salary%TYPE;

  BEGIN

   OPEN cur_Salary;

     FETCH cur_Salary INTO v_Salary;

   CLOSE cur_Salary;

   RETURN v_Salary;

  END;

Code Sample 2: Nested blocks

The inner, anonymous, block can open and close the cursor and fetch from it, because the block is within the scope of the cursor. The variable v_Salary is declared within the inner block, so that its scope is limited to that block. Any attempt to refer to v_Salary in the outer block, for instance by moving the RETURN statement between the two END statements, would result in an error. A procedure is a stand-alone executable module and is called as an executable PL/SQL statement. It may have no parameters (arguments) or any combination of IN, OUT, and IN/OUT parameters. Parameters may be any valid PL/SQL datatype.

A function differs from a procedure in that it has a return value and is not a stand-alone executable statement. A function is called as part of an executable statement or embedded in SQL. Like a procedure, a function may or may not have parameters ­ but normally it has only IN parameters. If a function is to be embedded in SQL, it may not have OUT parameters. It is mandatory that functions return a value, even if that value is NULL. Like parameters, the return value of a function may be any valid datatype. A function can be used in a PL/SQL statement in place of an expression having the same datatype as the return value. Code Examples 1 and 2 are examples of functions. Code Example 3 is an example of a simple procedure.

PROCEDURE Update_Salary(
           p_Employee_ID              IN employee.employee_id%TYPE
           ,p_Salary     IN employee.salary%TYPE)

IS

BEGIN

UPDATE    employee
    SET salary = p_Salary
WHERE  employee_id = p_Employee_id;
END Update_Salary;

Code Example 3: Simple Procedure

Packages

Packages provide a means of encapsulating logically related PL/SQL objects, such as functions, procedures, cursors, variables, etc, thus promoting the concept of structured programming. Packages provide considerable performance enhancement because, when any element of the package is referenced for the first time, the entire package (compiled and validated) is loaded into memory in the SGA and is available to all users without further disk access. Packages also minimize cascading dependencies, thus avoiding unnecessary compilation. The advantages of packages are as follows:

·            Modularity

·            Information Hiding

·            Top-down design

·            Global data

·            Better Performance

·            Overloading

 

The application developer can decide which elements of the package are public and accessible and which are hidden, thus protecting the integrity of the package and minimizing the effects of changes. A package specification can be compiled without its body, allowing other modules that reference it to compile before it is complete. Because a package has the access privileges of its owner, users can be given strictly controlled access to data that would otherwise be hidden from them.

A very useful feature of packages is overloaded names. This allows more than one function or procedure to have the same name, provided that the number and/or datatypes of the parameters of each are different.

A package consists of two parts: ­ the specification and the body. Objects that are declared in the package specification are public and can be referenced from anywhere, for example from another package. Objects that are declared only in the body of the package are private and can be referenced only by other elements of the package. Values assigned to public objects, such as variables, are persistent and can therefore be regarded as global. Functions and procedures are always defined in the package body and optionally may be declared in the specification to make them public. Code Example 4 illustrates a package with a public function and a public procedure.

 

PACKAGE Employee_Pkg      /* PACKAGE SPEC for employee table */

IS

FUNCTION Get_Salary(p_Emp1oyee_Id employee.employee_id%TYPE

                   ,p_Sa1ary      emp1oyee.salary%TYPE)

RETURN NUMBER;

PROCEDURE Update_Salary(p_Employee_Id IN employee.employee.id%TYPE

                       ,p_Salary      IN employee.salary%TYPE)

END Emp1oyee_Pkg:

       


PACKAGE BODY Employee_Pkg /* PACKAGE BODY for employee table */

IS

 FUNCTION Get_Salary(p_Employee_Id employee.employee_id%TYPE

                    ,p_Salary      emp1oyee.salary%TYPE)

 RETURN NUMBER

 IS

 BEGIN

 ....................................

 ....................................

 END Get_Sa1ary:

 PROCEDURE Update_Salary(p_Employee_Id IN employee.employee.id%TYPE

                        ,p_Salary      IN employee.salary%TYPE)

 IS

 BEGIN

 ....................................

 ....................................

 END Get_Sa1ary;

END Employee__Pkg;

Code Example 4: Sample package

Variables and Constants

Variables And Constants
The vast majority of PL/SQL programs require a means of storing data internally for use in the program. The data may be retrieved from the database or it may consist of derived, calculated, or assigned values. The internal storage is provided by variables and constants. Variables may be scalar, i.e. consisting of only a single value, or a composite structure such as a record or a PL/SQL table. Variables must be declared in the declaration section of a PL/SQL block and, optionally,  may be declared with an initial value assigned to them. If no value is assigned to it, a variable will have an initial value of NULL.


A variable has a name, a datatype, and a value or values. The name is a pointer to the location in memory where the value or values reside and the datatype governs the type of information that can be stored in the variable. Each element of a composite variable may have the same or a different datatype. 

Constants are identical to variables in almost every way except that a value must be assigned when the constant is declared. As the name suggests,  the value of a constant may not be changed from the initial value. Examples of declarations of  variables and constants are shown in Code Examples 5 and 6.

DECLARE
 v_Customer_Name customer.name%TYPE;
 c_Maximum_Orders CONSTANT NUMBER(2) := 25;
BEGIN
 --------------------
 --------------------
END;

Code Example 5: Declarations in an Anonymous Block

 

Declaration in a named block looks like:
PROCEDURE Process_Order IS
 v_Customer_Name customer.name%TYPE;
 c_Maximum_Orders CONSTANT NUMBER(2) := 25;
BEGIN
 --------------------
 --------------------
END Process_Order;

Code Example 6: Declarations in a named block

 

 


Variable Names

In common with most other Oracle objects, the name of a PL/SQL variable or constant can be up to 30 characters in length. The first character must be a letter, which may then be followed by letters, numerals, underscores, $, or #. This provides the flexibility to choose names that are descriptive of what the variable or constant represents as well as its context. In many cases, descriptive names for variables and other PL/SQL objects can,  make programs to some extent self-documenting, thus avoiding the time, effort, and even clutter of extensive commenting.

 Datatypes


The datatype controls the storage format, the valid values, and restrictions on use of a variable or constant. Oracle provides a wealth of datatypes, some of which the average developer is unlikely ever to use. There are four classes of datatype ­ Boolean, character, number, and date/time. The datatype ROWID, which stores a binary value and really fits into no class, has been included in the character class because it can, for most purposes, be treated as if it is of type CHAR. PL/SQL8 and Oracle8 have additional datatypes for NLS multi-byte character sets and large object (LOB) datatypes, which are considered beyond the scope of this paper.

The Boolean Datatype

A Boolean datatype is a logical datatype, and elements declared as Boolean may have only the values TRUE, FALSE, or NULL. The Oracle RDBMS does not support this datatype and it is valid only in PL/SQL. Booleans are particularly useful as the return value of a function, often enabling otherwise complex code to be hidden and replaced with a simple and elegant statement. An example of using a Boolean return value is shown in the Code Example 7.

IF Salary_Exceeds_Grade_Max (…….) THEN
 dbms_output.put_line('The salary entered exceeds

                                     the maximum for the grade');
END IF;

 

Code Example 7: Using a Boolean return value

 

In this example, the function might look up the maximum salary for the employee grade, apply possibly complex business rules such as exceptions for long service, and return either TRUE or FALSE. The name of the function makes its purpose clear and no further explanation is needed.

Character Datatypes

As can be seen from Table 2, the precision of PL/SQL character variables is very different from that of the equivalent database columns. Care must therefore be exercised when using PL/SQL variables to retrieve values from or insert values into database tables. For this reason alone, it is always advisable to declare elements that match up with database table columns using the %TYPE attribute.

 

Datatype

Maximum length (bytes) PL/SQL

Maximum length (bytes) in Oracle Tables

CHAR

32767

255  (2000 in ORACLE8)

CHARACTER

32767

255  (2000 in ORACLE8)

LONG

32760

2 Gigabytes

LONG RAW

32760

2 Gigabytes

VARCHAR

32767

2000  (4000 in ORACLE8)

VARCHAR2

32767

2000  (4000 in ORACLE8)

RAW

32767

255  (2000 in ORACLE8)

ROWID

18

18

Table 2: Character Datatypes

 

char and character

The two datatypes, CHAR and CHARACTER, are synonymous. Prior to Oracle7, these datatypes were of variable length. However, in order to comply with ANSI standards, they are now fixed length and the VARCHAR2 datatype, described later in this section, has replaced them as the normally preferred variable-length character datatype. Because of the inherent difficulties encountered when working with fixed-length strings, the use of these datatypes should be avoided except when absolutely necessary. An example of this is when working with DB2 data sources where there is a specific requirement for fixed-length strings.

If a value from a database column of datatype VARCHAR or VARCHAR2 (variable length) is FETCHED into a CHAR variable, the value will be padded to the right with spaces to the length of the variable. Conversely, if a value in a CHAR variable is INSERTed into a VARCHAR2 database column, any trailing spaces will be discarded.

During an upgrade from RDBMS Version 6 to Oracle7, CHAR table columns are automatically converted to VARCHAR2.

long

The PL/SQL LONG character datatype is very different from its database column counterpart. Its maximum length is 32,760 bytes as compared with 2GB, and character functions such as INSTR and SUBSTR, which are not permitted for a column, may be applied to it. Attempting to fetch a LONG database column value with a length greater than 32,760 bytes into a LONG PL/SQL variable will result in an error ­ ³ORA-06502: PL/SQL: numeric or value error². There is no automatic truncation and, because SUBSTR cannot be applied to the database column, no means of working around this restriction. Because the VARCHAR2 data type has a maximum length that¹s 7 bytes longer and can be manipulated and used in exactly the same manner, there is no point in using the LONG datatype in PL/SQL.

Long Raw

The LONG RAW datatype is exactly the same as LONG except that Oracle will not attempt to perform character set conversions when moving data between different platforms.

varchar and varchar2

At the time of writing, VARCHAR and VARCHAR2 are synonymous. VARCHAR is intended to provide compatibility with IBM and ANSI relational databases. However, it is likely that the definition of VARCHAR will change in a future version of the ANSI SQL standards. For this reason, Oracle suggests that VARCHAR should be avoided whenever possible.
VARCHAR2 is the character datatype most commonly used in PL/SQL programming. VARCHAR2 variables can be manipulated with a comprehensive set of built-in functions, similar to those found in SQL. VARCHAR2 elements should always be declared with a precision specified (1 to ­32,767). PL/SQL Versions 1.x permit declarations without a precision. This results in an element with a maximum length of 1one and can lead to unexpected errors showing up at run-time. Version 2 upwards will not compile without a precision being specified.


If a value from a database column of datatype CHAR (fixed length) is FETCHED into a VARCHAR2 variable, any trailing spaces will be automatically discarded.

raw

The RAW datatype is similar to VARCHAR2, except that Oracle will not attempt to perform character set conversions when moving data between different platforms and the maximum length of a RAW database column is only 255 bytes (2,000 in Oracle8).

rowid

ROWID is not strictly a character datatype but a binary value that identifies the exact physical location of a row in a table. It is internally created and maintained. The value of a ROWID can be examined by assigning it to an 18-byte character string, but there is usually no reason to do this. ROWID can be used to access a record in a database and is normally the fastest possible method of doing so. ROWID is internal to Oracle and is not part of the ANSI standard. Therefore, it cannot be used in programs intended to run against non-Oracle databases.

Numeric Datatypes
There are principally only two numeric datatypes in PL/SQL, NUMBER and BINARY_INTEGER. As shown in Figures 3 and 4, all other numeric datatypes are sub-types of these two and provide compatibility with ANSI SQL and IBM datatypes.

Number

The NUMBER datatype is used for both fixed and floating-point numbers. When a NUMBER element is declared, a precision and scale may be specified. If they are omitted, the element will be floating point. The precision of a NUMBER is the total number of digits, with a maximum of 38. If a precision is not specified, it defaults to the maximum. The scale must be in the range minus 84 to plus 127, and defines the number of digits to the left or right of the decimal point at which rounding occurs. If the scale is zero, then rounding will be to the nearest whole number.

 

binary integer

The BINARY_INTEGER datatype allows for storage of signed integers in the range ­231 to +231. Because the values assigned to this datatype are represented as signed binary numbers, it is possible that there could be some performance advantage over NUMBER datatypes. This is because PL/SQL can use them in calculations without any conversion. NATURAL and POSITIVE are both sub-types of BINARY_INTEGER and have a maximum value of 231 and minimum values of zero and 1one respectively.

 

The DATE Datatype

Both PL/SQL and the RDBMS provide a true DATE datatype that stores both date and time. Oracle provides a collection of powerful date functions that allow mathematical and formatting operations on dates.  The valid range of dates supported by Oracle is 1 January 4712 BC to 31 December 4712 AD (31 December 9999 AD in Oracle8) in one-second increments. If the time element is omitted, it defaults to midnight.

Built-In Functions

PL/SQL provides a wide range of very powerful built-in functions, which can be categorized as character, number, date, and miscellaneous. These almost exactly duplicate the range of functions found in Oracle’s implementation of the SQL language ­ the notable exception being the DECODE function which is not available in PL/SQL.

Character Functions

There are several types of character functions. Each type is discussed briefly below.

ASCII

ASCII returns the number that represents a single character in the current database character set. It should be noted that, despite the name of the function, the character set is not necessarily ASCII, but is dependant on the operating system character set. If more than one character is passed the second and subsequent characters will be ignored.

ASCII('A')       ð 65
ASCII('ABC')  ð  65

CHR

The CHR function is the inverse of ASCII. It returns a single character corresponding to the number in the character set passed as the parameter. It is especially useful for embedding a non-printing character such as a linefeed in a string.

chr('65')  ð  'A'

chr('65')  ð  'a'

The boy stood'||chr(10)||'on the burning deck'
Prints as:
'The boy stood
on the burning deck'

CONCAT

CONCAT is not a particularly useful function. It concatenates two strings passed as parameters. The concatenation operator (||) is easier to use and more powerful than CONCAT.

concat('abc','def')  ð  'abcdef'

 'abc'||'def'||'hij'  ð  'abcdefhij'

INITCAP

INITCAP reformats the string passed as a parameter. It sets the first letter of each Œword¹ to upper case and the remaining letters to lower case. A word is a string of characters separated by a space or non-alphanumeric character, such as #, $, *, _, etc.

initcap('ORACLE DEVELOPER')   ð  'Oracle Developer'


Unfortunately INITCAP lacks real intelligence, which limits its usefulness. It always follows arbitrary rules, which will sometimes not deliver the desired results as illustrated below:

 


initcap('XEPHON''S ORACLE UPDATE')  ð  'Xephon'S Oracle Update'
initcap('JAMES MACDONALD')                ð  'James Macdonald'
initcap('TOM AND JERRY')                        ð  'Tom And Jerry'

 

 

INSTR

INSTR searches a character string to find an occurrence within it of a second string. If found the position of the first character of the second string within the first string is returned. If it is not found, then 0 is returned.

instr('ORACLE UPDATE','UP')   ð  8
instr('ORACLE UPDATE','X')     ð  0

 

In addition to the two mandatory parameters, INSTR has two optional numeric parameters ­ start_position and nth_occurence. The default for both is 1, i.e. starting at the first character and searching for the first occurrence.
Specifying a negative number for the start position will cause the search to begin start_position characters from the end of the string.

instr('The King is dead. Long live the King','King')         ð  5
instr('The King is dead. Long live the King','King',6)      ð  33
instr('The King is dead. Long live the King','King',1,2)   ð  33
instr('The King is dead. Long live the King','King',-1,2)  ð  5

 

length

The LENGTH function returns the length of the specified string in characters. It always returns a positive number or NULL, never zero. A string variable with no value assigned to it is NULL and therefore has no length. If the string is of a fixed length, CHAR datatype LENGTH will always return the declared length of the variable. To find the length the value in  this type of variable the function RTRIM, which is described later in this paper, can be used to remove the trailing blanks.

 LENGTH('')                                    ð  NULL
 LENGTH('ORACLE UPDATE')   ð  13



lower  
The LOWER function will convert all the alphabetical characters in a string to lower case. It has no effect on the length of the string or on non-alphabetical characters in the string.

 

 lower('ORACLE UPDATE')        ð  'oracle update'
 lower('ORACLE UPDATE 27')   ð  'oracle update 27'

 

LOWER and its antonym, UPPER, are particularly useful for eliminating case-sensitivity problems when comparing character strings.

IF 'ORACLE UPDATE' = 'Oracle Update'                         ð  FALSE
IF lower('ORACLE UPDATE') = lower('Oracle Update')  ð  TRUE

 

Lpad

It is sometimes necessary to format a string with leading spaces or some other character(s). LPAD returns the string passed as the first parameter padded to the total length specified in the second parameter using the character or characters specified in the third parameter. The third parameter is optional and defaults to a single blank space. If the existing length of the string is the same as the length specified LPAD will have no effect. If the length of the string is less, the string will be truncated to the specified length.

lpad('ORACLE UPDATE',20)          ð  '       ORACLE UPDATE'
lpad('ORACLE UPDATE',20,'*')      ð  '*******ORACLE UPDATE'
lpad('ORACLE UPDATE',20,'123')    ð  '1231231ORACLE UPDATE'  
lpad('ORACLE UPDATE',6,'*')       ð  'ORACLE'

 

LTRIM
The LTRIM function is the opposite of LPAD. It removes characters from the leading portion of a string passed as the first parameter. It is most commonly used to remove leading spaces, and a single space is the default for the optional second parameter. If one or more characters are passed as a list to the second parameter, LTRIM will remove every occurrence of each character in the list (not the pattern) from the leading portion of the string, until a character not in the list is encountered.

 ltrim('         ORACLE UPDATE')                 ð  'ORACLE UPDATE'
 ltrim('ORACLE UPDATE','ELCARO')        ð  ' UPDATE'
 ltrim('ORACLE UPDATE',' ELCARO')       ð  'UPDATE'  
 ltrim('ORACLE UPDATE',' ELUPCARO')  ð  'DATE'

 

REPLACE
The REPLACE function returns the string passed as the first parameter with all occurrences of the pattern of characters passed as the second parameter replaced with the pattern of characters in third, optional, parameter. This third parameter has a default of NULL.

replace('ORACLE UPDATE','ORACLE ')         ð  'UPDATE'
replace('ORACLE UPDATE','ORACLE','NT')  ð  'NT UPDATE'
replace('ORACLE UPDATE','E')                       ð  'ORACL UPDAT'  
replace('ORACLE UPDATE','E','X')                  ð  'ORACLX UPDATX'

 

RPAD
The RPAD function is similar to the LPAD function except that it adds characters to the end of a string instead of the beginning. The string of characters passed as the first parameter is returned padded to the length specified in the second parameter with the pattern of characters passed as the third, optional, parameter. The default for the third parameter is a single space. If the string to be padded is the same length as the value passed as the second parameter it will be returned unchanged. If it is longer it will be truncated to the specified length.

rpad('ORACLE UPDATE',20)             ð  'ORACLE UPDATE       '
rpad('ORACLE UPDATE',20,'*')        ð  'ORACLE UPDATE*******'
rpad('ORACLE UPDATE',20,'123')    ð  'ORACLE UPDATE1231231'  
rpad('ORACLE UPDATE',6,'*')           ð  'ORACLE'

 

RTRIM
RTRIM is the opposite of RPAD and similar to LTRIM. The function removes characters from the right-hand portion of a string. The string passed as the first parameter is returned with all characters contained in the string passed as the second parameter removed from the right of the last character not found in the remove string. The second parameter is optional and defaults to a single space.

rtrim('ORACLE UPDATE       ')               ð  'ORACLE UPDATE'
 rtrim('ORACLE UPDATE','EDATPU')    ð  'ORACLE '
 rtrim('ORACLE UPDATE',' EDATPU')   ð  'ORACL'


 
SOUNDEX
The SOUNDEX function returns a character string, which is a phonetic representation of the string passed as the parameter. SOUNDEX uses the first five consonants in the string to generate the return value. All vowels, except where the first character is a vowel, are ignored. SOUNDEX is not case sensitive, both upper and lower case letters will return the same value. A common misapprehension is that when SOUNDEX is used in a SELECT, the LIKE operator should be used. This is not the case, ‘=’ is the correct syntax.

SELECT last_name  FROM employee
       WHERE soundex(last_name) = soundex('JONES')

 



soundex('ORACLE')                   ð  'O624'
 soundex('ORAKLE')                   ð  'O624 '
 soundex('ORACLE UPDATE')  ð  'O624'
 soundex('STEPHENSON')          ð  'S315'
 soundex('STEVENSON')            ð  'S315'

 


SUBSTR
The SUBSTR (sub-string) is probably the most used character functions. It returns the portion of the string passed as the first parameter specified by a starting position and a length passed as the second and third parameters. The last parameter (length) is optional and, if it is omitted, the characters from the start position to the end of the string are returned. If the start position exceeds the length of the string, the return value will be NULL. If it is zero it will default to 1, and, if it is negative, SUBSTR will count backwards from the end of the string to find the start position. If the length specified is less than 1, the return value will be NULL.

 


substr('ORACLE UPDATE',1,6)    ð  'ORACLE'
substr('ORACLE UPDATE',8)       ð  'UPDATE'
substr('ORACLE UPDATE',-6)      ð  'UPDATE'  
substr('ORACLE UPDATE',-6,2)   ð  'UP'  
substr('ORACLE UPDATE',-1)      ð  'E'  
substr('ORACLE UPDATE',1,0)    ð  NULL  
substr('ORACLE UPDATE',20,2)  ð  NULL

 

 

 

TRANSLATE
The TRANSLATE function is similar to the REPLACE function ­ the difference being that while REPLACE works with sets of characters TRANSLATE replaces single characters at a time. The string passed as the first parameter is returned with each character specified in the second parameter replaced by the character in the corresponding position in the third parameter. If there is no corresponding character, a NULL will be substituted. All three parameters are mandatory; there are no default values. If a NULL is passed as the value for any parameter the return value will be NULL.

 

 translate('ORACLE UPDATE','OU','*%')  ð  '*RACLE %PDATE'
 translate('ORACLE UPDATE','CE','KX')  ð  'ORAKLX UPDATX'
 translate('ORACLE UPDATE','CE','K')     ð  'ORAKL UPDAT'
 translate('ORACLE UPDATE','CE','')        ð  NULL

 

 

 

upper

The UPPER function works in exactly the same way as LOWER except that it converts all the alphabetical characters in a string to upper case.

upper('oracle update')        ð  'ORACLE UPDATE'
upper('oracle update 27')   ð  'ORACLE UPDATE 27'

 

IF 'oracle update' = 'Oracle Update'                         ð  FALSE
IF upper('oracle update') = upper('Oracle Update')  ð  TRUE



Date Functions

Pl/SQL provides eight functions for the manipulation of dates. These are in addition to the simple arithmetic DATE + n which adds n days to the specified date and DATE – n, which subtracts n days. The following examples assume that the default date format is ‘DD-MON-YYYY’. (E.G. ’20-JUN-2000’).

ADD_MONTHS

The ADD_MONTHS function returns a new date with the specified number of months added to the specified date. add_months is an overloaded function so the date and the number of months can be specified in either order, but both arguments are required. If the number of months parameter is positive, add_months returns a date for that number of months into the future. If the number is negative, add_months returns a date for that number of months in the past. add_months always shifts the date by whole months. A fractional value for the number of months parameter can be passed, but add_months will always round down to the next lowest integer. If the date passed is the last day of a month the last day of the resulting month is returned. If the day number of the input date is greater than the last day of the resulting month the date of  the last day in the new month is returned.

 add_months(’21-JUN-2000’,1)   ð  21-JUL-2000

 add_months(1,’21-JUN-2000’)   ð  21-JUL-2000

 add_months(’21-JUN-2000’,-1)  ð  21-MAY-2000

 add_months(’29-FEB-2000’,1)   ð  31-MAR-2000

 add_months(’31-JAN-2000’,1)   ð  29-FEB-2000

 

LAST_DAY

The LAST_DAY function returns the date of the last day of the month for  the date passed as the parameter. This function is useful because the number of days in a month varies from month to month and, in the case of February, from year to year.

 last_day(’21-JUN-2000’)                               ð  30-JUN-2000

 last_day(’21-FEB-2000’)                               ð  29-FEB-2000

 last_day(’21-FEB-2001’)                               ð  28-FEB-2001

 last_day(’21-FEB-2000’)                               ð  29-FEB-2000

 last_day(add_months(’21-FEB-2001’,-12))   ð  29-FEB-2000

 

MONTHS_BETWEEN

The months_between function returns the number of months between two dates.  If date l and date 2 are in different months and at least one of the dates is not the last day of the month, months_between returns a fractional number. It should be noted that the fractional component is always calculated on a 31-day month basis, regardless of the actual number of days in a month, and must therefore be used with care. Differences in the time component of date l and date 2 are also taken into account. If date l comes after date 2, then a positive number is returned. If date l comes before date 2, then the number returned is negative. If the two dates are in the same month then zero will be returned. If both dates are the last day of their respective months the fractional component is ignored and a whole number will be returned.

 months_between (‘21-JUN-2000’, ’25-JUN-2000’)    ð  0

 months_between (‘21-JUL-2000’, ’21-JUN-2000’)    ð  1

 months_between ('21-JUN-2000', '21-JUL-2000')      ð  -1

 months_between ('20-JUN-2000', '21-JUL-2000')      ð  -1.032258

 

NEW TIME

The new_time function takes the date and time passed as the first parameter and converts it from the tome zone passed as the second  parameter to the date and time in the time zone the third parameter. New_Time is very limited in its usefulness because it can be used only to convert to and from time in the western hemisphere and Greenwich Mean Time (now more correctly know as UTC). New_Time also does not take into account those places, such as Arizona, that do not observe daylight savings time. A further complication is that there is no international agreement for the dates on which time changes occur.

new_time(to_date('20-JUN-2000 01:01'

                              ,'DD-MON-YYYY hh24:mi’)

                              ,'PST','EST')                                ð 19-JUN-2000 22:01

new_time(to_date('20-JUN-2000 01:01'

                              ,'DD-MON-YYYY hh24:mi’)

                              ,'est','pst’     )                                ð 20-JUN-2000 04:01

new_time(to_date('20-JUN-2000 01:01'

                              ,'DD-MON-YYYY hh24:mi’)

                              ,'GMT','PST')                               ð 19-JUN-2000 17:01

 

The valid time zones for New_Time (which are not case sensitive) are shown in Table 3.

 

AST

Atlantic Standard Time

ADT

Atlantic Daylight Time

BST

Bering Standard Time

BDT

Bering Daylight Time

CST

Central Standard Time

CDT

Central Daylight Time

EST

Eastern Standard Time

EDT

Eastern Daylight Time

GMT

Greenwich Mean Time

HST

Alaska-Hawaii Standard Time

HDT

Alaska-Hawaii Daylight Time

MST

Mountain Standard Time

MDT

Mountain Daylight Time

NST

Newfoundland Standard Time

PST

Pacific Standard Time

PDT

Pacific Daylight Time

YST

Yukon Standard Time

YDT

Yukon Daylight Time

Table 3: Time Zones

 

NEXT_DAY

The Next_Day function returns the next occurrence of the day of the week specified in the second parameter after the date passed as the first parameter. The name of the day can be either the full or the abbreviated name and it is not case sensitive. It must however be in the language specified by NLS_DATE_LANGUAGE for the current session. If the date passed falls on the specified day of the week the next occurrence i.e., seven days into the future will be returned.

next_day('20-JUN-2000','Monday')   ð 26-JUN-2000

next_day('20-JUN-2000','Mon')         ð 26-JUN-2000

next_day('26-JUN-2000','Monday')   ð 03-JUL-2000

next_day('26-JUN-2000','TUE’)        ð 27-JUN-2000

 

ROUND

Round is an overloaded function and the argument to it can be either a date or a number. In its date version it returns a date rounded as specified by the format mask (see Table 4) in its second argument. The time component of the date is always rounded to midnight. If no format mask is specified it defaults to DAY and returns the same day if the time component is noon or earlier, the next day if it is later.


 

Format Mask

Description

SCC or CC

Century

SYYYY,YYYY, YEAR, SYEAR, Year, YYY, YY, Y

Year

IYYY IYY,IY, I

ISO year

Q

Quarter

MONTH, MOM, MM

Month (Rounds up on the sixteenth day)

WW

The day of the week of the first day of the year

IW

The day of the week of the first day of the ISO year

W

The day of the week of the first day of the month

DDD, DD, J

Day

DAY, DY, D

First day of the week

HH, HH12, HH24

Hour

MI

Minute

Table 4: Format Masks for Round and Trunc

 

TRUNC

TRUNC is also an overloaded function with a numeric equivalent. In its date version it returns a date truncated as specified by the format mask (see Figure 12) in its second argument. If no format mask is specified it defaults to DAY and returns the date with the time component set to midnight. This is particularly useful for comparing dates when the time component needs to be ignored.

round(to_date(’01-JUN-2049’),’CC’)            ð 01-JAN-2000

 round(to_date(’01-JUN-2050’),’CC’)            ð 01-JAN-2001

 trunc (to_date(’01-JUN-2050’),’CC’)            ð 01-JAN-2000

 round(to_date(’15-JUN-2000’),’MONTH’)   ð 01-JUN-2000

 round(to_date(’16-JUN-2000’),’MONTH’)   ð 01-JUL-2001

 trunc (to_date(’16-JUN-2000’),’MONTH’)   ð 01-JUN-2001

 

SYSDATE

Because it takes no parameters SYSDATE is often regarded, quite reasonably, as a variable. It is however a function that returns the current date and time to the nearest second from the database.

 to_char(sysdate,’DD-MON-YYYY’)                    ð 13-JUN-2000

 to_char(sysdate,’DD-MON-YYYY hh24:mi:ss’)  ð 13-JUN-2000 11:37:41

 

Numeric Functions

The numeric functions provided by PL/SQL are the normal functions provided by most programming languages and mainly self-explanatory. They are therefore not covered here in the same detail as the character, date and conversion functions. The list of numeric functions and brief descriptions of their functionality can be found in Table 5.


 

ABS

Returns the absolute (unsigned) value of the number.

ACOS

Returns the inverse sine.

ASIN

Returns the inverse cosine.

ATAN

Returns the inverse tangent.

ATAN2

Returns the result of the tan2 inverse trigonometric function.

CEIL

Returns the smallest integer greater than or equal to the specified number.

COS

Returns the sine.

COSH

Returns the cosine.

EXP(n)

Returns e raised to the nth power, where e – 2.71828183.

FLOOR

Returns the largest integer equal to or less than the specified number.

LN(x)

Returns the natural logarithm of x.

LOG (x,y)

Returns the logarithm, base x, of y.

MOD (x,y)

Returns the remainder of x divided by y.

POWER (x,y)

Returns x raised to the power y.

ROUND (x,y)

Returns x rounded to y decimal places. (y is optional and defaults to 0).

SIGN (x)

Returns 1 if x is positive, 0 if x is 0, and –1 if x is less than 0.

SIN

Returns the sine.

S1NH

Returns the hyperbolic sine.

SQRT

Returns the square root of the number.

TAN

Returns the tangent

TANH

Returns the hyperbolic tangent.

TRUNC(x,y)

Returns x truncated to y decimal places. (y is optional and defaults to 0).

Table 5: Numeric Functions

 

 

Conversion Functions

PL/SQL provides a number of conversion functions. The most commonly used are described here. The format models for these functions can be found in the Appendix.

 

TO_CHAR

To_Char is an overloaded function and can be used to convert both dates and numbers to variable length strings.

Date Conversion

This function has three parameters. The date, which is mandatory, a format mask and the NLS_DATE_LANGUAGE, both of which are optional. If the format mask is omitted then the format specified in NLS_LANGUAGE for the session is used. If the NLS_DATE_LANGUAGE parameter is specified then a format must also be provided.

 to_date(‘13-JUN-2000')                       ð 13-JUN-2000

 to_date(2451709,'J')                             ð 13-JUN-2000
 to_date(‘13/06/2000’,’dd/mm/yyyy’)  ð 13-JUN-2000

 

Number Conversion

This version of the TO_CHAR function also has three parameters: the number to be converted (which is mandatory), a format mask, and the NLS_LANGUAGE, both of which are optional. If the format mask is omitted then the default format specified in NLS_LANGUAGE for the session is used. If the NLS_LANGUAGE parameter is specified then a format must also be provided.

to_char(678.10)                   ð 678.1

to_char(678.10,’999.99’)    ð 678.10

to_char(678.10,’0999.99’)  ð 0678.10

to_char(678.10,’$999.99’)   ð $678.10

 

TO_DATE

The TO_DATE function converts a number or a character string to a date datatype. This function has three parameters. The character string or number, which is mandatory, a format mask and the NLS_DATE_LANGUAGE, both of which are optional. If the format mask is omitted then the format specified in NLS_LANGUAGE for the session is used. If the NLS_DATE_LANGUAGE parameter is specified then a format must also be provided. The only number that can be used in the first parameter is a valid Julian date and in this case the J format mask must be specified. If the first parameter is a character string it must be a valid date in the format specified by the format mask.

 to_char(sysdate)                                                  ð 13-JUN-2000 (default)

 to_char(sysdate,’dd-Mon-YYYY hh24:mi:ss’)  ð 13-Jun-2000 14:55:04

 to_char(sysdate,’YYYYMMDD’)                      ð 20000613

 

TO_NUMBER

The TO_NUMBER function converts a character string to a number. This function has three parameters. The character string to be converted, which is mandatory, a format mask and an NLS_LANGUAGE parameter, both of which are optional. If the format mask is omitted then the format specified in NLS_LANGUAGE for the session is used. If the NLS_LANGUAGE parameter is specified then a format must also be provided. The NLS_LANGUAGE parameter can contain up to three values, NLS_NUMERIC_CHARACTERS, NLS_CURRENCY and NLS_ISO_CURRENCY.

 to_number(‘678.10’)                ð 678.1

 to_number(‘678.10’,999.99)    ð 678.10

 

 

Miscellaneous Functions

The following are some additional miscellaneous functions.

GREATEST

The greatest function accepts a list of values as its input parameter and returns the greatest value in that list. There is no upper limit on the number of values can be passed GREATEST. It is an overloaded function and can accept the datatypes Date, Number and Varchar2. The return datatype is the same as the input datatype. The datatypes of all the values in the list must be compatible.

 

 greatest(’01-MAR-1999’,’01-MAR-2000’) ð 01-MAR-2001

 greatest('John','Paul','Peter')                          ð Peter

 greatest(10,12,15,20,21,24,25,29,30)           ð 30


LEAST

LEAST is the antonym of GREATEST. It functions in exactly the same way except that it returns the least or lowest value from the list.

 

least(’01-MAR-1999’,’01-MAR-2000’) ð 01-MAR-1999

least('John','Paul','Peter')                          ð John

least(10,12,15,20,21,24,25,29,30)           ð 10

 

NVL

NVL accepts two parameters and returns the value of the second parameter if the value of the first parameter is NULL, otherwise it returns the value of the first parameter. This is particularly useful for comparing values in variables where one or both potentially has a NULL value. NVL is an overloaded function and will accept any datatype, but both parameters must of the same datatype. (The following examples assume that the variable "myvar" has a null value).

 

Nvl(myvar,99)                    ð 99

nvl(myvar,'John')                ð John

nvl(myvar,’21-JUN-2000') ð 21-JUN-2000'

 

PL/SQL Control Structure

PL/SQL has a number of control structures, including:

·            Conditional controls

·             Sequential controls

·             Iterative or loop controls.

·            Exception or error controls

It is these controls, used singly or together, that allow the PL/SQL developer to direct the flow of execution through the program.

Conditional Controls

There are three levels of conditional controls or IF statements, which are similar to those found in most 3GL languages. These controls are:

·            IF....THEN....END IF

·            IF....THEN...ELSE....END IF

·            IF....THEN...ELSIF....THEN....ELSE....END IF

The statements between the conditions should always be indented for clarity and appropriate control should always be used. For instance, in the case of multiple or nested IF....THEN....END IF statements it is probably appropriate to use

IF...THEN..ELSE...END IF
or
IF....THEN...ELSIF....THEN....ELSIF….ELSE....END IF.


Multiple IF constructs may be nested and each logical level should also be indented. Again, consideration should be given as to whether the correct control is being used. Often, nested IF controls can be simplified by the use of AND or OR. Some examples are shown in Figure 1.

 

Figure 1: Conditional Controls

Sequential controls

 PL/SQL offers two sequential controls ­ GOTO and NULL.

GOTO branches unconditionally to a named label. The label name has to be enclosed in double angle brackets, as in <<my_label>>. The use of GOTO should be avoided at almost any cost. It is the start of the slippery slope that ends in unstructured spaghetti code that is difficult, if not impossible, to understand. There is almost always a better way to do something than with a GOTO.

NULL is used simply to tell PL/SQL to do nothing. It might for instance be used in an exception section to ignore a particular exception condition.

 

Iterative or look controls

There are four types of iterative controls in PL/SQL:

1.          The simple infinite loop.

2.          The WHILE loop.

3.          The numeric FOR loop.

4.          The cursor FOR loop.

It is preferable avoid using the simple loop. Rather  use a WHILE or FOR loop, which has a definite start and end point. In most cases one of these is better suited for the task and may even result in less coding. There are two methods for exiting a simple loop, EXIT and EXIT WHEN. The EXIT statement should be avoided. EXIT has to be within an IF-THEN construct and EXIT WHEN provides the same conditional logic. FOR loops should always be allowed to complete the specified iterations. EXIT and EXIT WHEN should never be used to jump out of a FOR loop. A RETURN statement must never be used to exit any loop.


If a loop has a lot of code within it or if loops are nested, labels may be used to clearly identify the beginning and end of each loop. These labels are created using double angle brackets and can then be used in the END and EXIT WHEN statements of the loop, as shown in Code Example   .

<<outer_loop>>
LOOP
  <<inner_loop>>

 LOOP

    my_var := Do_Something;

    EXIT inner_loop WHEN my_var = TRUE;

 END inner_loop;

  my_var2 := Do_Something_Different

  EXIT outer_loop WHEN my_var2 = TRUE;

Exit outer_loop;

 

Exception or error controls

One of the things that PL/SQL has inherited from ADA is very powerful exception or error handling. There are four types of exception:

·            Named system exceptions

·            Named developer-defined exceptions.

·            Unnamed system exceptions.

·            Unnamed developer-defined exceptions (raise application error)

Excluding functions that are to be embedded in SQL, as mentioned earlier, PL/SQL modules should normally contain one or more exception handling sections. PL/SQL makes no distinction between system exceptions and developer-defined exceptions and they are handled in the same way by the exception handling section.

When declaring named developer-defined exceptions, care should be taken not to use the same name as a named system exception. The code will compile without error, but, when a run-time error occurs, the named system exception will take precedence. There should always be a WHEN OTHERS clause at the highest level of exception handling.

Conclusion

PL/SQL is a powerful and complex programming language. This paper provides only a beginning to understanding and mastering its intricacies. There is only so much that can be covered in the limited time and space available. The use of cursors to interact with the database, using packages to improve performance and efficiency, using PL/SQL tables and other collections are just a small part of what remains for the reader to learn.

References

The PL/SQL Users Guide and Reference, ©1997, Oracle Corporation.
Oracle PL/SQL Programming by Steven Feuerstein, ©1995 and 1997, O’Reilly and Associates.
Getting the Best from PL/SQL by John C. Lennon, ©1999 and 2000, Oracle Update, Xephon plc.

About The Author

John Lennon is a Project Manager with Dulcian, Inc. (http://www.dulcian.com/).  Originally from England; John resided for some years in South Africa before moving to the United States in 1992. He has many years of experience with applications development, primarily in engineering and utility environments. John has been working with Oracle products for over a decade. He has presented at user group meetings, international and regional Oracle users’ conferences, including previous ODTUG conferences, and has published articles in Oracle technical journals in the United States and Great Britain.

 

John C. Lennon
e-mail: jlennon@dulcian.com   
website: http://u1.lvcm.com/johnlennon


APPENDIX

 

Date Format Models

Format Mask

Description

SCC or CC

The century. If the SCC format is used BC dates are prefaced with a negative sign.

SYYYY or YYYY

The four-digit year. If the SYYYY format is used  BC dates are prefaced with a negative sign.

IYYY

The four-digit ISO standard year.

YYY or YY or Y

The last three, two, or one digits of the year.

IYY or IY or I

The last three, two, or one digits of the ISO year.

Y.YYY

The four-digit year with a comma.

YEAR or SYEAR

Year or SYear

The year spelled out in uppercase or title case. The S prefix places a negative sign in front

of B.C. dates. The language is always English.

BC or AD

The BC or AD indicator, without periods.

B.C. or A.D.

The BC or AD indicator, with periods.

Q

The quarter of the year from 1 through 4.

MM

The number of the month in the year, from 01 through 12.

RM

The Roman numeral representation of the month number.

MONTH or Month

The name of the month in uppercase or title case in the language set by NLS_LANGUAGE

MON or Mon

The abbreviated name of the month in uppercase or title case in the language set by NLS_LANGUAGE

WW

The week in the year, from 1 through 53.

IW

The ISO week in the year, from 1 through 52 or 53.

W

The week in the month, from 1 through 5.

DDD

The day in the year, from 1 through 366.

DD

The day in the month, from 1 through 31.

D

The day in the week, from 1 through 7. The starting day is determined by NLS_TERRITORY.

DAY or Day

The name of the day in uppercase or title case format.

DY

The 3 character abbreviated name of the day in the language set by NLS_LANGUAGE.

J

The Julian day of the date; i.e., the number of days since 1 January  4712 BC

AM or PM

The meridian indicator without periods.

A.M. or P.M.

The meridian indicator with periods.

HH or HH12

The hour in the day, from 1 through 12.

HH24

The hour in the day, from 0 through 23.

MI

The minutes component of the date/time, from 0 through 59.

SS

The seconds component of the date/time, from 0 through 59.

sssss

The number of seconds since midnight of the time component.

TH

Suffix that converts a number to its ordinal format. The language is always English.

SP

Suffix that converts a number to its spelled format. The language is always English.

SPTH

Suffix that converts a number to its spelled and ordinal format. The language is always English.

Other text

Any text in quotes will be reproduced in the formatted output of the conversion.

 


APPENDIX (Continued)

 

Number Format Models

 

Element

Description

9

Each 9 represents a significant digit. Leading zeros in a number are displayed as blanks.

0

Each 0 represents a significant digit. Leading zeros in a number are displayed as zeros.

$ (Prefix)

Puts a dollar sign in front of the number.

B (Prefix)

Displays a zero value as blank, even if the 0 format element was used to show a leading zero.

MI (Suffix)

Places a minus sign (-) after a negative number. For positive values returns a trailing space.

S (Prefix)

Places a plus sign (+) in front of a positive number and a minus sign (-) in front of a negative number.

PR (Suffix)

Places angle brackets around a negative value.

D

Specifies the location of the decimal point in the returned value.

G

 

Specifies the location of the group separator (for example, a comma to separate thousands as in 32,767) in the returned value. The character used as the separator is determined  by  the setting of NLS_NUMERIC_CHARACTERS.

C

Specifies the location of the ISO currency symbol in the returned value.

L

Specifies the location of the local currency symbol (such as $) in the returned value.

,

Specifies that a comma be returned in that location in the return value.

.

Specifies that a period be returned in that location in the return value.

V

 

Multiplies the number to the left of the V in the format model by 10 raised to the nth power,

where n is the number of 9s found after the V in the format model.

EEEE (Suffix)

Specifies that the value be returned in scientific notation.

RN or

rn

Specifies that the return value be converted to upper- or lowercase Roman numerals.

The range of valid numbers for conversion to Roman numerals is between 1 and 3999.

 

 

 

© 2000 Dulcian, Inc.