PL/SQL programing 第六版学习笔记-3
Working with Program Data
1. Naming Your Program Data
2. Overview of PL/SQL Datatypes
Character Data
CHAR and NCHAR are fixed-length datatypes; VARCHAR2
and NVARCHAR2 are variable-length datatypes.
PL/SQL has the CLOB (character large object) and NCLOB (National Language Support CLOB) datatypes. For backward compatibility,PL/SQL also supports the LONG datatype. These datatypes allow you to store and manipulate very large amounts of data; in Oracle Database 11g, a LOB can hold up to 128 terabytes of information.
Numbers
/* File on web: numbers.sql */
DECLARE
salary NUMBER(9,2); -- fixed-point, seven to the left, two to the right
raise_factor NUMBER; -- decimal floating-point
weeks_to_pay NUMBER(2); -- integer
BEGIN
salary := 1234567.89;
raise_factor := 0.05;
weeks_to_pay := 52;
END;
Dates, Timestamps, and Intervals
Booleans
Binary Data
including RAW, BLOB, and BFILE.
for example, you try to fetch a LONG RAW from the database into your PL/SQL variable that exceeds the 32,760 byte limit, you will encounter an ORA-06502: PL/SQL: numeric or value error exception. To work with LONG RAWs longer than PL/SQL’s limit, you need an OCI program; this is a good reason to migrate your legacy code from LONG RAWs to BLOBs, which have no such limit.
ROWIDs
Oracle provides two proprietary datatypes, ROWID and UROWID, used to represent
the address of a row in a table. ROWID represents the unique physical address of a row
in its table; UROWID represents the logical position of a row in an index-organized
table (IOT). ROWID is also a SQL pseudocolumn that can be included in SQL statements.
REF CURSORs
The REF CURSOR datatype allows developers to declare cursor variables. A cursor
variable can then be used with static or dynamic SQL statements to implement more
flexible programs. There are two forms of REF CURSORs: the strong REF CURSOR
and the weak REF CURSOR. PL/SQL is a statically typed language, and the weak REF
CURSOR is one of the few dynamically typed constructs supported.
Here is an example of a strong REF CURSOR declaration. I associate the cursor variable with a specific record structure (using a %ROWTYPE attribute):
DECLARE
TYPE book_data_t IS REF CURSOR RETURN book%ROWTYPE;
book_curs_var book_data_t;
And here are two weak REF CURSOR declarations in which I do not associate any
particular structure with the resulting variable. The second declaration (the last line)
showcases SYS_REFCURSOR, a predefined weak REF CURSOR type:
DECLARE
TYPE book_data_t IS REF CURSOR;
book_curs_var book_data_t;
book_curs_var_b SYS_REFCURSOR;
Internet Datatypes
XML (Extensible Markup Language) and URIs (universal resource identifiers).
“Any” Datatypes
User-Defined Datatypes
3. Declaring a Variable
The basic syntax for a declaration is:
name datatype [NOT NULL] [ := | DEFAULT default_assignment];
Declaring Constants
the syntax for the declaration of a constant is:
name CONSTANT datatype [NOT NULL] := | DEFAULT default_value;
Anchored Declarations
PL/SQL offers two kinds of anchoring:
-
Scalar anchoring Use the %TYPE attribute to define your variable based on a table’s column or some other PL/SQL scalar variable.
-
Record anchoring
Use the %ROWTYPE attribute to define your record structure based on a table or a predefined PL/SQL explicit cursor. -
The syntax for an anchored datatype is:
variable_name type_attribute%TYPE [optional_default_value_assignment];
variable_name table_name | cursor_name%ROWTYPE [optional_default_value_assignment];
Anchoring to Cursors and Tables
Suppose that I want to query a single row of information from the book table. Rather
than declare individual variables for each column in the table (which, of course, I should
do with %TYPE), I can simply rely on %ROWTYPE:
DECLARE
l_book book%ROWTYPE;
BEGIN
SELECT * INTO l_book FROM book WHERE isbn = '1-56592-335-9';
process_book (l_book);
END;
Suppose now that I only want to retrieve the author and title from the book table. In
this case, I build an explicit cursor and then %ROWTYPE against that cursor:
DECLARE
CURSOR book_cur IS
SELECT author, title FROM book WHERE isbn = '1-56592-335-9';
l_book book_cur%ROWTYPE;
BEGIN
OPEN book_cur;
FETCH book_cur INTO l_book; END;
Finally, here is an example of an implicit use of the %ROWTYPE declaration: the cursor FOR loop.
BEGIN
FOR book_rec IN (SELECT * FROM book)
LOOP
process_book (book_rec);
END LOOP;
END;
Benefits of Anchored Declarations
- Synchronization with database columns
- Normalization of local variables
Programmer-Defined Subtypes
- Constrained subtype
SUBTYPE POSITIVE IS BINARY_INTEGER RANGE 1 .. 2147483647;
- Unconstrained subtype
SUBTYPE FLOAT IS NUMBER;
Conversion Between Datatypes
- Implicit Data Conversion
- Explicit Datatype Conversion
- The CHARTOROWID function
The CHARTOROWID function converts a string of either type CHAR or type VARCHAR2 to a value of type ROWID.
- The CAST function
It converts from one (and almost any) built-in datatype or collection-typed value to another builtin datatype or collection-typed value.
- The CONVERT function
The CONVERT function converts strings from one character set to another character
set.
4.The HEXTORAW function
The HEXTORAW function converts a hexadecimal string from type CHAR or VARCHAR2 to type RAW.- The RAWTOHEX function
- The ROWIDTOCHAR function
3. STRINGS
- The VARCHAR2 Datatype
If you omit the CHAR or BYTE qualifier when declaring a VARCHAR2 variable, then
whether the size is in characters or bytes depends on the NLS_LENGTH_SEMANTICS
initialization parameter. You can determine your current setting by querying NLS_SESSION_PARAMETERS.
- The CHAR Datatype
Prior to 12c, the maximum length of the CHAR datatype in SQL was 2,000; in 12c, this
is now increased to match the PL/SQL maximum: 32,767 bytes. Note, however, that SQL supports these maximum sizes only if the MAX_SQL_STRING_SIZE initialization parameter is set to EXTENDED.
- String Subtypes
It’s unlikely that you’ll ever need to use these—I never do—but you should be aware that they exist.
图片.png
Specifying String Constants
If you want to embed a single quote within a string constant, you can do so by typing
the single quote twice:
'Aren''t you glad you''re learning PL/SQL with O''Reilly?'
If your program will be dealing with strings that contain embedded single-quote characters,a more elegant approach is to specify your own string delimiters. Do this using the q prefix (uppercase Q may also be specified). For example:
q'!Aren't you glad you're learning PL/SQL with O'Reilly?!'
or:
q'{Aren't you glad you're learning PL/SQL with O'Reilly?}'
Occasionally, however, you may need to explicitly specify a string constant to be represented in the national character set. You can do so using the n prefix:
n'Pils vom faß: 1₠'
If you need a string in the national character set, and you also want to specify some
characters by their Unicode code point, you can use the u prefix:
u'Pils vom fa\00DF: 1\20AC'
00DF is the code point for the German letter ß, while 20AC is the code point for the
Euro symbol. The resulting string constant is the same as for the preceding n-prefixed
example.
When they encounter an ampersand,these tools “see” the next word as a variable and prompt you to supply a value:
SET SERVEROUT ON;
SET DEFINE OFF;
begin
DBMS_OUTPUT.PUT_LINE ('Generating & saving test data.');
END;
/
There are several solutions to this problem. One that works well with SQL*Plus and
SQL Developer is to issue the command SET DEFINE OFF to disable the variable substitution feature.
- Using Nonprintable Characters
You can do that by concatenating all the address lines together into one long text value
and using CHR to insert linefeeds where desired. The location in the standard ASCII
collating sequence for the linefeed character is 10, so you can code:
SELECT name || CHR(10) || address1 || CHR(10) || address2 || CHR(10) || city || ', ' || state || ' ' || zipcode AS company_address FROM company;
You say your boss doesn’t want to see any blank lines? No problem. You can
eliminate those with a bit of cleverness involving the NVL2 function:
SELECT name || NVL2(address1, CHR(10) || address1, '') || NVL2(address2, CHR(10) || address2, '') || CHR(10) || city || ', ' || state || ' ' || zipcode AS company_address
FROM company;
Now the query returns a single formatted column per company. The NVL2 function
returns the third argument when the first is NULL, and otherwise returns the second
argument. In this example, when address1 is NULL, the empty string (‘’) is returned,
and likewise for the other address columns.
The ASCII function, in essence, does the reverse of CHR: it returns the decimal representation of a given character in the database character set. For example, execute the following code to display the decimal code for the letter J:
BEGIN
DBMS_OUTPUT.PUT_LINE(ASCII('J'));
END;
and you’ll find that, in UTF-8 at least, the underlying representation of J is the value 74.
- Concatenating Strings
There are two mechanisms for concatenating strings: the CONCAT function and the
concatenation operator, represented by two vertical bar characters (||).
use the CONCAT function, which takes two arguments as follows:
CONCAT (string1, string2)
CONCAT ('abc', 'defg') --> 'abcdefg'
CONCAT (NULL, 'def') --> 'def'
CONCAT ('ab', NULL) --> 'ab'
CONCAT (NULL, NULL) --> NULL
To perform the identical concatenation using CONCAT, you would need to nest one
call to CONCAT inside another:
x := CONCAT(CONCAT('abc','def'),'ghi');
- Forcing a string to all upper- or lowercase
One way to deal with case issues is to use the built-in UPPER and LOWER functions.
These functions let you force case conversion on a string for a single operation. For
example:
DECLARE
name1 VARCHAR2(30) := 'Andrew Sears';
name2 VARCHAR2(30) := 'ANDREW SEARS';
BEGIN
IF LOWER(name1) = LOWER(name2) THEN
DBMS_OUTPUT.PUT_LINE('The names are the same.');
END IF;
END;
- Making comparisons case insensitive P208
- Case insensitivity and indexes
Initially the code is using the emp_name_ix index, but when I set NLS_COMP=LINGUISTIC
and NLS_SORT=BINARY_CI to enable case insensitivity I stop using the
index and start doing full table scans instead—oops! One solution is to create a functionbased,case-insensitive index, like this:
CREATE INDEX last_name_ci ON EMPLOYEES (NLSSORT(last_name, 'NLS_SORT=BINARY_CI'))
Now when I do my case-insensitive query, I use the case-insensitive index and keep my
good performance.
- Capitalizing each word in a string
A third case-related function, after UPPER and LOWER, is INITCAP. This function forces the initial letter of each word in a string to uppercase, and all remaining letters to lowercase.
- Traditional Searching, Extracting, and Replacing
The INSTR function returns the character position of a substring within a larger string. The following code finds the locations of all the commas in a list of names:
DECLARE
names VARCHAR2(60) := 'Anna,Matt,Joe,Nathan,Andrew,Aaron,Jeff';
comma_location NUMBER := 0;
BEGIN
LOOP
comma_location := INSTR(names,',',comma_location+1);
EXIT WHEN comma_location = 0;
DBMS_OUTPUT.PUT_LINE(comma_location);
END LOOP;
END;
The output is:
5
10
14
21
28
34
例如:INSTR('CORPORATE FLOOR','OR', 3, 2)中,源字符串为'CORPORATE FLOOR',在字符串中查找'OR',从第三个字符位置开始查找"OR",取第三个字后第2个匹配项的位置。
- Padding
You can use LPAD and RPAD
to add spaces (or some other character) to either end of a string in order to make the
string a specific length.
DECLARE
a VARCHAR2(30) := 'Jeff';
b VARCHAR2(30) := 'Eric';
c VARCHAR2(30) := 'Andrew';
d VARCHAR2(30) := 'Aaron';
e VARCHAR2(30) := 'Matt';
f VARCHAR2(30) := 'Joe';
BEGIN
DBMS_OUTPUT.PUT_LINE( RPAD(a,10) || LPAD(b,10) );
DBMS_OUTPUT.PUT_LINE( RPAD(c,10) || LPAD(d,10) );
DBMS_OUTPUT.PUT_LINE( RPAD(e,10) || LPAD(f,10) );
END;
The output is:
Jeff Eric
Andrew Aaron
Matt Joe
The default padding character is the space. If you like, you can specify a fill character
as the third argument. Change the lines of code to read:
DBMS_OUTPUT.PUT_LINE( RPAD(a,10,'.') || LPAD(b,10,'.') );
DBMS_OUTPUT.PUT_LINE( RPAD(c,10,'.') || LPAD(d,10,'.') );
DBMS_OUTPUT.PUT_LINE( RPAD(e,10,'.') || LPAD(f,10,'.') );
And the output changes to:
Jeff............Eric
Andrew.........Aaron
Matt.............Joe
Your fill “character” can even be a string of characters:
DBMS_OUTPUT.PUT_LINE( RPAD(a,10,'-~-') || LPAD(b,10,'-~-') );
DBMS_OUTPUT.PUT_LINE( RPAD(c,10,'-~-') || LPAD(d,10,'-~-') );
DBMS_OUTPUT.PUT_LINE( RPAD(e,10,'-~-') || LPAD(f,10,'-~-') );
Now the output looks like:
Jeff-~--~--~--~-Eric
Andrew-~---~--~Aaron
Matt-~--~--~--~--Joe
- Trimming
What LPAD and RPAD giveth, TRIM, LTRIM, and RTRIM taketh away. For example:
DECLARE
a VARCHAR2(40) := 'This sentence has too many periods......';
b VARCHAR2(40) := 'The number 1';
BEGIN
DBMS_OUTPUT.PUT_LINE( RTRIM(a,'.') );
DBMS_OUTPUT.PUT_LINE(
LTRIM(b, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz')
);
END;
The output is:
This sentence has too many periods
1
The other trimming function is just plain TRIM. Oracle added TRIM when Oracle8i
Database was released in order to make the database more compliant with the ISO SQL standard. TRIM works a bit differently from LTRIM and RTRIM, as you can see:
DECLARE
x VARCHAR2(30) := '.....Hi there!.....';
BEGIN
DBMS_OUTPUT.PUT_LINE( TRIM(LEADING '.' FROM x) );
DBMS_OUTPUT.PUT_LINE( TRIM(TRAILING '.' FROM x) );
DBMS_OUTPUT.PUT_LINE( TRIM(BOTH '.' FROM x) );
-- The default is to trim from both sides
DBMS_OUTPUT.PUT_LINE( TRIM('.' FROM x) );
-- The default trim character is the space:
DBMS_OUTPUT.PUT_LINE( TRIM(x) );
END;
The output is:
Hi there!.....
.....Hi there!
Hi there!
Hi there!
.....Hi there!.....
- Regular Expression Searching, Extracting, and Replacing
Detecting a pattern
The general syntax for the REGEXP_LIKE function is:
REGEXP_LIKE (source_string, pattern [,match_modifier])
Locating a pattern
You can use REGEXP_INSTR to locate occurrences of a pattern within a string. The
general syntax for REGEXP_INSTR is:
REGEXP_INSTR (source_string, pattern [,beginning_position [,occurrence
[,return_option [,match_modifier [,subexpression]]]]])
Extracting text matching a pattern
I’ll use REGEXP_SUBSTR to extract a phone number from a string containing
contact information:
DECLARE
contact_info VARCHAR2(200) := 'address:1060 W. Addison St.Chicago, IL 60613
home 773-555-5253';
phone_pattern VARCHAR2(90) :=
'\(?\d{3}\)?[[:space:]\.\-]?\d{3}[[:space:]\.\-]?\d{4}';
BEGIN
DBMS_OUTPUT.PUT_LINE('The phone number is: '||
REGEXP_SUBSTR(contact_info,phone_pattern,1,1));
END;
This code shows me the phone number:
The phone number is: 773-555-5253
- Working with empty strings
Oracle database treats empty strings as NULLs. This is contrary to the ISO SQL standard, which recognizes the difference between an empty string and a string variable that is NULL.
DECLARE
empty_varchar2 VARCHAR2(10) := '';
empty_char CHAR(10) := '';
BEGIN
IF empty_varchar2 is null then
DBMS_OUTPUT.PUT_LINE('empty_varchar2 is Null');
end if;
IF '' IS NULL THEN
DBMS_OUTPUT.PUT_LINE(''''' is NULL');
END IF;
IF empty_char IS NULL THEN
DBMS_OUTPUT.PUT_LINE('empty_char is NULL');
ELSIF empty_char IS NOT NULL THEN
DBMS_OUTPUT.PUT_LINE('empty_char is NOT NULL');
END IF;
END;
You’ll notice in this example that the CHAR variable is not considered NULL. That’s
because CHAR variables, as fixed-length character strings, are never truly empty.
a NULL is never not equal, or equal,to any other value.
- Mixing CHAR and VARCHAR2 Values
Database-to-variable conversion When you SELECT or FETCH data from a CHAR database column into a VARCHAR2 variable, the trailing spaces are retained. If you SELECT or FETCH from a VARCHAR2 database column into a CHAR variable, PL/SQL automatically pads the value with spaces out to the maximum length. In other words, the type of the variable, not the column, determines the variable’s resulting value.
-
String comparisons
-
String Function Quick Reference
ASCII(single_character)
Returns the NUMBER code that represents the specified character in the database
character set.
ASCIISTR(string1)
Takes a string in any character set and converts it into a string of ASCII characters.
Any non-ASCII characters are represented using the form \XXXX, where
XXXX represents the Unicode value for the character.
CHR(code_location)
Returns a VARCHAR2 character (length 1) that corresponds to the location in the
collating sequence provided as a parameter. This is the reverse of ASCII.
COMPOSE(string1)
Takes a Unicode string as input and returns that string in its fully normalized
form.
CONCAT(string1, string2)
Appends string2 to the end of string1. You’ll get the same results as from the expression string1 || string2. I find the || operator so much more convenient
that I almost never invoke the CONCAT function.
CONVERT(string1, target_char_set)
Converts a string from the database character set to the specified target character
set. You may optionally specify a source character set:
CONVERT(string1, target_char_set, source_character_set)
DECOMPOSE(string1)
Takes a Unicode string as input and returns that string with any precomposed
characters decomposed into their separate elements. This is the opposite of COMPOSE.
For example, DECOMPOSE(‘ã’) yields ‘a˜’. (See COMPOSE.)
GREATEST(string1, string2, ...)
Takes one or more strings as input, and returns the string that would come last (i.e.,
that is the greatest) if the inputs were sorted in ascending order. Also see the LEAST
function, which is the opposite of GREATEST.
INITCAP(string1)
Reformats the case of the string argument, setting the first letter of each word to
uppercase and the remainder of the letters to lowercase. This is sometimes called
title case. A word is a set of characters separated by a space or nonalphanumeric
character (such as # or _). For example, INITCAP(‘this is lower’) gives ‘This Is
Lower’.
INSTR(string1, string2)
Returns the position at which string2 is found within string1; if it is not found,
returns 0.
Several variations are available:
INSTR(string1, string2, start_position)
Begins searching for string2 at the column in string1 indicated by start_position.
The default start position is 1, so INSTR(string1, string2, 1) is equivalent
to INSTR(string1, string2).
INSTR(string1, string2, negative_start_position)
Begins searching from the end of string1 rather than from the beginning.
INSTR(string1, string2, start_position, nth)
Finds the nth occurrence of string2 after the start_position.
INSTR(string1, string2, negative_start_position, nth)
Finds the nth occurrence of string2, counting from the end of string1.
LEAST(string1, string2, ...)
Takes one or more strings as input and returns the string that would come first (i.e.,
that is the least) if the inputs were sorted in ascending order.
LENGTH(string1)
LOWER(string1)
LPAD(string1, padded_length)
LTRIM(string1)
NCHR(code_location)
NLS_INITCAP(string1)
NLS_LOWER(string1) and NLS_LOWER(string1, ‘NLS_SORT=sort_se
quence_name’)
NLS_UPPER(string1) and NLS_UPPER(string1, ‘NLS_SORT=sort_se
quence_name’)
SUBSTR(string1, start, length)
- Page236 of the book