- •Dedication
- •Preface
- •Objectives of This Book
- •Structure of This Book
- •About the Contents
- •What This Book Does Not Cover
- •Conventions Used in This Book
- •Which Platform or Version?
- •About the Code
- •Comments and Questions
- •Acknowledgments
- •Part I: Programming in pl/sql
- •Chapter 1. Introduction to pl/sql
- •1.1 What Is pl/sql?
- •1.2 The Origins of pl/sql
- •1.2.1 The Early Years of pl/sql
- •1.2.2 Improved Application Portability
- •1.2.3 Improved Execution Authority and Transaction Integrity
- •1.2.4 Humble Beginnings, Steady Improvement
- •1.3 So This Is pl/sql
- •1.3.1 Integration with sql
- •1.3.2 Control and Conditional Logic
- •1.3.3 When Things Go Wrong
- •1.4 About pl/sql Versions
- •1.4.1 Oracle8i New Features
- •1.4.1.1 Autonomous transactions
- •1.4.1.2 Invoker rights
- •1.4.1.3 Native dynamic sql (nds)
- •1.4.1.4 Bulk binds and collects
- •1.4.1.5 New trigger capabilities
- •1.4.1.6 Calling Java from pl/sql
- •1.4.2 Oracle9i New Features
- •1.4.2.1 Record-based dml
- •1.4.2.2 Table functions
- •1.4.2.3 New and improved datatypes
- •1.4.2.4 Inheritance for object types
- •1.4.2.5 Enhancements to pl/sql collections
- •1.4.2.6 Native compilation of pl/sql code
- •1.4.3 Working with Multiple Versions of pl/sql
- •1.5 Resources for pl/sql Developers
- •1.5.1 The o'Reilly pl/sql Series
- •1.5.2 Other Printed Resources
- •1.5.3 Pl/sql on the Internet
- •1.5.4 Development Tools and Utilities
- •1.6 Some Words of Advice
- •1.6.1 Don't Be in Such a Hurry!
- •1.6.2 Don't Be Afraid to Ask for Help
- •1.6.3 Take a Creative, Even Radical Approach
- •Chapter 2. Creating and Running pl/sql Code
- •2.1 Sql*Plus
- •Figure 2-1. Sql*Plus in a console session
- •2.1.1 Starting Up sql*Plus
- •Figure 2-2. The gui login screen of sql*Plus
- •Figure 2-3. The iSql*Plus login page
- •2.1.2 Running a sql Statement
- •Figure 2-4. Query with result in iSql*Plus
- •2.1.3 Running a pl/sql Program
- •2.1.4 Running a Script
- •2.1.5 Other sql*Plus Tasks
- •2.1.5.1 Setting your preferences
- •2.1.5.2 Saving output to a file
- •2.1.5.3 Exiting sql*Plus
- •2.1.5.4 Editing a statement
- •2.1.5.5 Loading your own custom environment automatically on startup
- •2.1.6 Error Handling in sql*Plus
- •2.1.7 Why You Will Love and Hate sql*Plus
- •2.2 Performing Essential pl/sql Tasks
- •2.2.1 Creating a Stored Program
- •2.2.2 Executing a Stored Program
- •2.2.3 Showing Stored Programs
- •2.2.4 Managing Grants and Synonyms for Stored Programs
- •2.2.5 Dropping a Stored Program
- •2.2.6 Hiding the Source Code of a Stored Program
- •2.3 Oracle's pl/sql-Based Developer Tools
- •Figure 2-5. The programmer's user interface in Oracle Forms Builder
- •2.3.1 Moving pl/sql Programs Between Client and Server
- •Figure 2-6. The Object Navigator in Oracle Forms shows the result of dragging and dropping two packages from the server to the client
- •2.4 Calling pl/sql from Other Languages
- •2.4.1 C: Using Oracle's Precompiler (Pro*c)
- •2.4.2 Java: Using jdbc
- •2.4.3 Perl: Using Perl dbi and dbd::Oracle
- •2.4.4 Pl/sql Server Pages
- •Figure 2-7. Output from a pl/sql Server Page
- •2.5 And What Else?
- •Chapter 3. Language Fundamentals
- •3.1 Pl/sql Block Structure
- •3.1.1 Sections of the pl/sql Block
- •Figure 3-1. The pl/sql block structure
- •Figure 3-2. A procedure containing all four sections
- •3.1.2 Anonymous Blocks
- •Figure 3-3. An anonymous block without declaration and exception sections
- •3.1.2.1 The structure of an anonymous block
- •3.1.2.2 Examples of anonymous blocks
- •3.1.2.3 Anonymous blocks in different environments
- •3.1.3 Named Blocks
- •3.1.4 Nested Blocks
- •Figure 3-4. Anonymous blocks nested three levels deep
- •3.1.5 Scope
- •3.1.6 Visibility
- •3.1.6.1 "Visible" identifiers
- •3.1.6.2 Qualified identifiers
- •3.1.6.3 Qualifying identifier names with module names
- •3.2 The pl/sql Character Set
- •3.3 Identifiers
- •3.3.1 Reserved Words
- •3.3.1.1 Language keywords
- •3.3.1.2 Identifiers from standard package
- •3.3.1.3 Approaches to avoiding reserved words
- •3.3.2 Whitespace and Keywords
- •3.4 Literals
- •3.4.1 Embedding Single Quotes Inside a String
- •3.4.2 Numeric Literals
- •3.4.3 Boolean Literals
- •3.5 The Semicolon Delimiter
- •3.6 Comments
- •3.6.1 Single-Line Comment Syntax
- •3.6.2 Multiline Comment Syntax
- •3.7 The pragma Keyword
- •3.8 Labels
- •Part II: pl/sql Program Structure
- •Chapter 4. Conditional and Sequential Control
- •4.1 If Statements
- •4.1.1 The if-then Combination
- •4.1.2 The if-then-else Combination
- •4.1.3 The if-then-elsif Combination
- •4.1.4 Nested if Statements
- •4.2 Case Statements
- •4.2.1 Simple case Statements
- •4.2.2 Searched case Statements
- •4.2.3 Nested case Statements
- •4.2.4 Case Expressions
- •4.3 The goto Statement
- •4.3.1 Restrictions on the goto Statement
- •4.3.1.1 At least one executable statement must follow a label
- •4.3.1.2 The target label must be in the same scope as the goto statement
- •4.3.1.3 The target label must be in the same part of the pl/sql block as the goto
- •4.4 The null Statement
- •4.4.1 Improving Program Readability
- •4.4.2 Nullifying a Raised Exception
- •4.4.3 Using null After a Label
- •Chapter 5. Iterative Processing with Loops
- •5.1 Loop Basics
- •5.1.1 Examples of Different Loops
- •5.1.2 Structure of pl/sql Loops
- •Figure 5-1. The boundary and body of the while loop
- •5.2 The Simple Loop
- •5.2.1 Terminating a Simple Loop: exit and exit when
- •5.2.2 Emulating a repeat until Loop
- •5.3 The while Loop
- •5.4 The Numeric for Loop
- •5.4.1 Rules for Numeric for Loops
- •5.4.2 Examples of Numeric for Loops
- •5.4.3 Handling Nontrivial Increments
- •5.5 The Cursor for Loop
- •5.5.1 Example of Cursor for Loops
- •5.6 Loop Labels
- •5.7 Tips for Iterative Processing
- •5.7.1 Use Understandable Names for Loop Indexes
- •5.7.2 The Proper Way to Say Goodbye
- •5.7.3 Obtaining Information About for Loop Execution
- •5.7.4 Sql Statement as Loop
- •Chapter 6. Exception Handlers
- •6.1 How pl/sql Deals with Errors
- •Figure 6-1. Exception-handling architecture
- •6.1.1 Adopting an Exception-Handling Strategy
- •6.1.2 Exception-Handling Concepts and Terminology
- •6.2 Defining Exceptions
- •6.2.1 Declaring Named Exceptions
- •6.2.2 Associating Exception Names with Error Codes
- •6.2.2.1 Using exception_init
- •6.2.2.2 Recommended uses of exception_init
- •6.2.3 About Named System Exceptions
- •6.2.4 Scope of an Exception
- •6.3 Raising Exceptions
- •6.3.1 The raise Statement
- •6.3.1.1 Raising exceptions in nested blocks
- •6.3.2 Using raise_application_error
- •6.4 Handling Exceptions
- •6.4.1 Combining Multiple Exceptions in a Single Handler
- •6.4.2 Unhandled Exceptions
- •6.4.3 Using sqlcode and sqlerrm in Handler Clauses
- •6.4.4 Continuing Past Exceptions
- •Figure 6-2. Sequential deletEs, using two different approaches to scope
- •6.4.5 Propagation of an Unhandled Exception
- •Figure 6-3. Propagation of an exception through nested blocks
- •6.4.5.1 Losing exception information
- •Figure 6-4. Propagation of exception handling to first nested block
- •6.4.5.2 Examples of propagation
- •Figure 6-5. Exception raised in nested block handled by outermost block
- •6.4.6 Using Standardized Error Handler Programs
- •Part III: pl/sql Program Data
- •Chapter 7. Working with Program Data
- •7.1 Naming Your Program Data
- •7.2 Overview of pl/sql Datatypes
- •7.2.1 Character Data
- •7.2.2 Numbers
- •7.2.3 Dates, Timestamps, and Intervals
- •7.2.4 Booleans
- •7.2.5 Binary Data
- •7.2.6 RowiDs
- •7.2.7 Ref Cursors
- •7.2.8 Internet Datatypes
- •7.2.9 "Any" Datatypes
- •7.2.10 User-Defined Datatypes
- •7.3 Declaring Program Data
- •7.3.1 Declaring a Variable
- •7.3.2 Declaring Constants
- •7.3.3 Constrained Declarations
- •7.3.4 The not null Clause
- •7.3.5 Anchored Declarations
- •Figure 7-1. Anchored declarations with %type
- •7.3.6 Anchoring to Cursors and Tables
- •7.3.7 Benefits of Anchored Declarations
- •7.3.7.1 Synchronization with database columns
- •7.3.7.2 Normalization of local variables
- •7.3.8 Anchoring to not null Datatypes
- •7.4 Programmer-Defined Subtypes
- •7.5 Conversion Between Datatypes
- •7.5.1 Implicit Data Conversion
- •Figure 7-2. Implicit conversions performed by pl/sql
- •7.5.1.1 Limitations of implicit conversion
- •7.5.1.2 Drawbacks of implicit conversion
- •7.5.2 Explicit Datatype Conversion
- •7.5.2.1 The chartorowid function
- •7.5.2.2 The cast function
- •Figure 7-3. Casting built-in datatypes
- •7.5.2.3 The convert function
- •7.5.2.4 The hextoraw function
- •7.5.2.5 The rawtohex function
- •7.5.2.6 The rowidtochar function
- •Chapter 8. Strings
- •8.1 The Impact of Character Sets
- •8.1.1 What Is a Character Set?
- •8.1.2 Types of Character Sets
- •8.1.3 Database Character Set Versus National Language Character Set
- •Figure 8-1. Oracle's character set naming convention
- •8.1.4 Character Set Issues
- •8.1.4.1 Bytes versus characters
- •8.1.4.2 Oracle9i string declarations
- •8.1.4.3 Character function semantics
- •8.1.4.4 Code points and code units
- •8.1.4.5 Equality of Unicode strings
- •8.1.4.6 Sort order
- •8.2 String Datatypes
- •8.2.1 The varchar2 Datatype
- •8.2.2 The char Datatype
- •8.2.3 The nvarchar2 and nchar Datatypes
- •8.2.4 String Subtypes
- •8.3 String Issues
- •8.3.1 Empty Strings Are null Strings
- •8.3.2 Mixing char and varchar2 Values
- •8.3.2.1 Database-to-variable conversion
- •8.3.2.2 Variable-to-database conversion
- •8.3.2.3 String comparisons
- •8.3.2.4 Character functions and char arguments
- •8.3.3 Specifying String Constants
- •8.4 String Functions
- •Figure 8-2. Forward and reverse searches with instr
- •Figure 8-3. How arguments are used by substr
- •8.5 Nls Functions
- •Chapter 9. Numbers
- •9.1 Numeric Datatypes
- •9.1.1 The number Type
- •Figure 9-1. A typical fixed-point number declaration
- •Figure 9-2. The effect of scale exceeding precision
- •Figure 9-3. The effect of negative scale
- •9.1.2 The pls_integer Type
- •9.1.3 The binary_integer Type
- •9.1.4 Numeric Subtypes
- •9.2 Number Conversions
- •9.2.1 Number Format Models
- •9.2.2 The to_number Function
- •9.2.2.1 Using to_number with no format
- •9.2.2.2 Using to_number with a format model
- •9.2.2.3 Passing nls settings to to_number
- •9.2.3 The to_char Function
- •9.2.3.1 Using to_char with no format
- •9.2.3.2 Using to_char with a format model
- •9.2.3.3 The V format element
- •Figure 9-4. The V number format element
- •9.2.3.4 Rounding when converting numbers to character strings
- •9.2.3.5 Dealing with spaces when converting numbers to character strings
- •9.2.3.6 Passing nls settings to to_char
- •9.2.4 Using cast
- •9.2.5 Implicit Conversions
- •9.3 Numeric Functions
- •9.3.1 Rounding and Truncation Functions
- •Figure 9-5. Impact of rounding and truncating functions
- •Chapter 10. Dates and Timestamps
- •10.1 Date and Time Datatypes
- •10.1.1 The date Datatype
- •10.1.1.1 Declaring date variables
- •10.1.1.2 When to use date
- •10.1.1.3 Limitations of date
- •10.1.2 The timestamp Datatypes
- •Figure 10-1. Effect of different datetime datatypes
- •10.1.2.1 Declaring timestamp variables
- •10.1.2.2 When to use timestamPs
- •10.1.3 The interval Datatypes
- •10.1.3.1 Declaring interval variables
- •10.1.3.2 When to use intervaLs
- •10.2 Date and Timestamp Conversions
- •10.2.1 Date Format Models
- •10.2.2 String-to-Date Conversions
- •10.2.2.1 To_date
- •10.2.2.2 The to_timestamp family
- •10.2.2.3 Dealing with time zones
- •10.2.2.4 Date and timestamp literals
- •10.2.2.5 The fx element
- •10.2.2.6 The rr element
- •10.2.3 Date-to-String Conversions
- •10.2.3.1 To_char
- •10.2.3.2 Converting time zones to character strings
- •10.2.3.3 The fm element
- •10.2.4 Interval Conversions
- •10.2.4.1 The numto family of functions
- •10.2.4.2 The to_xxInterval functions
- •10.2.4.3 Interval value expressions
- •10.2.4.4 Formatting intervals for display
- •10.2.5 The cast and extract Functions
- •10.2.5.1 The cast function
- •10.2.5.2 The extract function
- •10.3 Date/Time Arithmetic
- •10.3.1 Traditional Date Arithmetic
- •10.3.1.1 Adding and subtracting numeric values
- •10.3.1.2 Computing the difference between two dates
- •10.3.2 Interval Arithmetic
- •10.3.2.1 Adding and subtracting intervals to/from datetimes
- •10.3.2.2 Computing the interval between two datetimes
- •10.3.2.3 CasTing datEs to timestamPs
- •10.3.2.4 Adding and subtracting intervals
- •10.3.2.5 Multiplying and dividing intervals
- •10.3.2.6 Unconstrained interval types
- •10.4 Date/Time Functions
- •10.4.1 Functions to Get the Date and Time
- •10.4.2 The Time Zone Functions
- •10.4.3 The add_months Function
- •10.4.4 The from_tz Function
- •10.4.5 The last_day Function
- •10.4.6 The months_between Function
- •10.4.7 The round and trunc Functions
- •10.4.8 The new_time Function
- •10.4.9 The next_day Function
- •Chapter 11. Records and Collections
- •11.1 Records in pl/sql
- •11.1.1 Benefits of Using Records
- •11.1.1.1 Data abstraction
- •11.1.1.2 Aggregate operations
- •11.1.1.3 Leaner, cleaner code
- •11.1.2 Declaring Records
- •11.1.3 Programmer-Defined Records
- •11.1.3.1 Declaring programmer-defined record typEs
- •11.1.3.2 Declaring the record
- •11.1.3.3 Examples of programmer-defined record declarations
- •11.1.4 Working with Records
- •11.1.4.1 Record-level operations
- •11.1.4.2 Field-level operations
- •11.1.5 Comparing Records
- •11.2 Collections in pl/sql
- •11.2.1 A Simple Collection Example
- •11.2.2 Types of Collections
- •11.2.3 Glossary of Collection Terms
- •11.2.4 Making Sense of Collections
- •11.3 Declaring Collection Types and Collections
- •11.3.1 Declaring an Associative Array
- •11.3.1.1 Defining the table type
- •11.3.1.2 Declaring the collection
- •11.3.2 Declaring a Nested Table or varray
- •11.3.2.1 Examples of declaring nested tables and varraYs
- •11.4 Where Collections Can Be Used
- •11.4.1 Collections as Components of a Record
- •11.4.2 Collections as Program Parameters
- •11.4.3 Collections as Datatypes of a Function's Return Value
- •11.4.4 Collection as "Columns" in a Database Table
- •11.4.5 Collections as Attributes of an Object Type
- •11.5 Collection Built-Ins (Methods)
- •11.5.1 The count Method
- •11.5.1.1 Boundary considerations
- •11.5.1.2 Exceptions possible
- •11.5.2 The delete Method
- •11.5.2.1 Boundary considerations
- •11.5.2.2 Exceptions possible
- •11.5.3 The exists Method
- •11.5.3.1 Boundary considerations
- •11.5.3.2 Exceptions possible
- •11.5.4 The extend Method
- •11.5.4.1 Boundary considerations
- •11.5.4.2 Exceptions possible
- •11.5.5 The first and last Methods
- •11.5.5.1 Boundary considerations
- •11.5.5.2 Exceptions possible
- •11.5.6 The limit Method
- •11.5.6.1 Boundary considerations
- •11.5.6.2 Exceptions possible
- •11.5.7 The prior and next Methods
- •11.5.7.1 Boundary considerations
- •11.5.7.2 Exceptions possible
- •11.5.8 The trim Method
- •11.5.8.1 Boundary considerations
- •11.5.8.2 Exceptions possible
- •11.6 Working with Collections
- •11.6.1 Initializing Collection Variables
- •11.6.1.1 Initializing explicitly with a constructor
- •11.6.1.2 Initializing implicitly during direct assignment
- •11.6.1.3 Initializing implicitly via fetch
- •11.6.1.4 Varray integration
- •11.6.2 Assigning Values to Elements
- •11.6.3 Referencing an Undefined Row
- •11.6.4 Working with Collections of Composites
- •11.6.4.1 Collections of records
- •11.6.4.2 Collections of other complex datatypes
- •11.6.4.3 Multilevel collections
- •11.6.4.4 Unnamed nested collections
- •11.6.5 Sequential and Nonsequential Associative Arrays
- •11.6.5.1 Sequential usage
- •11.6.5.2 Nonsequential usage
- •11.6.6 Passing Associative Arrays as Parameters
- •11.6.7 Pl/sql-to-Server Integration
- •11.6.8 Using varchar2 Associative Arrays
- •11.6.9 Emulating Alternative Indexes in Collections
- •Figure 11-1. Populating and accessing a hash index
- •11.7 Collection Pseudo-Functions
- •11.7.1 The the Pseudo-Function
- •11.7.2 The table Pseudo-Function
- •11.7.3 The cast Pseudo-Function
- •11.7.3.1 Casting a named collection
- •11.7.3.2 Casting an unnamed collection
- •11.7.4 The multiset Pseudo-Function
- •11.7.5 Sorting Contents of Collections
- •11.8 Maintaining Collections
- •11.8.1 Privileges
- •11.8.2 Collections and the Data Dictionary
- •11.9 Choosing a Collection Type
- •Chapter 12. Miscellaneous Datatypes
- •12.1 The boolean Datatype
- •12.2 The raw Datatype
- •12.3 The urowid and rowid Datatypes
- •Figure 12-1. RowiDs take you directly to rows in a table
- •12.3.1 Getting at Rowids
- •12.3.2 Using Rowids
- •12.3.2.1 Do rowids ever change?
- •12.3.2.2 Using rowids in Oracle Forms
- •12.3.2.3 Using rowids in a cursor for loop
- •12.3.2.4 Is the use of rowids worth the effort?
- •12.4 The lob Datatypes
- •12.4.1 The bfile Datatype
- •12.4.2 The blob Datatype
- •12.4.3 The clob Datatype
- •12.4.4 The nclob Datatype
- •12.5 Working with loBs
- •Figure 12-2. The Dryer Hose in Munising, Michigan
- •12.5.1 Understanding lob Locators
- •Figure 12-3. A lob locator points to its associated large object data within the database
- •12.5.2 Empty Versus null loBs
- •12.5.3 Creating a lob
- •12.5.4 Writing into a lob
- •12.5.5 Reading from a lob
- •12.5.6 BfilEs Are Different
- •12.5.6.1 Creating a bfile locator
- •12.5.6.2 Accessing bfilEs
- •12.5.6.3 Using bfilEs to load lob columns
- •12.5.7 Temporary loBs
- •12.5.7.1 Creating a temporary lob
- •12.5.7.2 Freeing a temporary lob
- •12.5.7.3 Checking to see whether a lob is temporary
- •12.5.7.4 Managing temporary loBs
- •12.5.8 Native lob Operations in Oracle9i
- •12.5.8.1 Sql semantics may yield temporary loBs
- •12.5.8.2 Performance impact of using sql semantics
- •12.5.9 Lob Conversion Functions
- •12.6 Predefined Object Types
- •12.6.1 The xmlType Type
- •2 From falls f
- •2 From falls
- •12.6.2 The uri Types
- •12.6.3 The "Any" Types
- •Part IV: sql in pl/sql
- •Chapter 13. Dml and Transaction Management
- •13.1.1 A Quick Introduction to dml
- •13.1.1.1 The insert statement
- •13.1.1.2 The update statement
- •13.1.1.3 The delete statement
- •13.1.2 Cursor Attributes for dml Operations
- •13.1.3 Returning Information from dml Statements
- •13.1.4 Dml and Exception Handling
- •13.1.5 Dml and Records
- •13.1.5.1 Record-based inserts
- •13.1.5.2 Record-based updates
- •13.1.5.3 Using records with the returning clause
- •13.1.5.4 Restrictions on record-based inserts and updates
- •13.2 Bulk dml with the forall Statement
- •Figure 13-1. Context switching between pl/sql and sql
- •Figure 13-2. One context switch with forall
- •13.2.1 The forall Statement
- •13.2.2 Context-Switching Problem Scenarios
- •Figure 13-3. Excessive context switching for multiple updatEs
- •13.2.3 Forall Examples
- •13.2.4 Cursor Attributes for forall
- •13.2.5 Rollback Behavior with forall
- •13.2.6 Continuing Past Exceptions with forall
- •13.3 Transaction Management
- •13.3.1 The commit Statement
- •13.3.2 The rollback Statement
- •13.3.3 The savepoint Statement
- •13.3.4 The set transaction Statement
- •13.3.5 The lock table Statement
- •13.4 Autonomous Transactions
- •Figure 13-4. Flow of transaction control between main, nested, and autonomous transactions
- •13.4.1 Defining Autonomous Transactions
- •13.4.2 When to Use Autonomous Transactions
- •13.4.3 Rules and Restrictions on Autonomous Transactions
- •13.4.3.1 Using autonomous transactions from within sql
- •13.4.3.2 Transaction visibility
- •13.4.4 Autonomous Transactions Examples
- •13.4.4.1 Building an autonomous logging mechanism
- •13.4.4.2 Using autonomous transactions in a database trigger
- •13.4.4.2.1 Creating a database trigger
- •13.4.4.2.2 Fine-tuning the database trigger
- •Chapter 14. Data Retrieval
- •14.1 Cursor Basics
- •14.1.1 Some Data Retrieval Terms
- •14.1.2 Typical Query Operations
- •Figure 14-1. Simplified view of cursor fetch operation
- •14.1.3 Introduction to Cursor Attributes
- •14.1.3.1 The %found attribute
- •14.1.3.2 The %notfound attribute
- •14.1.3.3 The %rowcount attribute
- •14.1.3.4 The %isopen attribute
- •14.1.3.5 The %bulk_rowcount attribute
- •14.1.3.6 The %bulk_exceptions attribute
- •14.1.4 Referencing pl/sql Variables in a Cursor
- •14.1.4.1 Identifier precedence in a cursor
- •14.1.4.2 Using standard naming conventions
- •14.1.5 Choosing Between Explicit and Implicit Cursors
- •14.2 Working with Implicit Cursors
- •14.2.1 Implicit Cursor Examples
- •14.2.2 Error Handling with Implicit Cursors
- •14.2.3 Implicit sql Cursor Attributes
- •14.3 Working with Explicit Cursors
- •14.3.1 Declaring Explicit Cursors
- •14.3.1.1 Naming your cursor
- •14.3.1.2 Declaring cursors in packages
- •14.3.2 Opening Explicit Cursors
- •14.3.3 Fetching from Explicit Cursors
- •14.3.3.1 Examples of explicit cursors
- •14.3.3.2 Fetching past the last row
- •14.3.4 Column Aliases in Explicit Cursors
- •14.3.5 Closing Explicit Cursors
- •14.3.6 Explicit Cursor Attributes
- •14.3.7 Cursor Parameters
- •14.3.7.1 Generalizing cursors with parameters
- •14.3.7.2 Opening cursors with parameters
- •14.3.7.3 Scope of cursor parameters
- •14.3.7.4 Cursor parameter modes
- •14.3.7.5 Default values for parameters
- •14.4 Bulk collect
- •14.4.1 Limiting Rows Retrieved with bulk collect
- •14.4.2 Bulk Fetching of Multiple Columns
- •14.4.3 Using the returning Clause with Bulk Operations
- •14.5 Select...For update
- •14.5.1 Releasing Locks with commit
- •14.5.2 The where current of Clause
- •14.6 Cursor Variables
- •Figure 14-2. Referencing a cursor variable across two programs
- •14.6.1 Why Cursor Variables?
- •14.6.2 Similarities to Static Cursors
- •14.6.3 Declaring ref cursor Types
- •14.6.4 Declaring Cursor Variables
- •Figure 14-3. The referencing character of cursor variables
- •14.6.5 Opening Cursor Variables
- •Figure 14-4. Compatible ref cursor rowtype and select list
- •14.6.6 Fetching from Cursor Variables
- •14.6.6.1 Handling the rowtype_mismatch exception
- •14.6.7 Rules for Cursor Variables
- •14.6.7.1 Compile-time rowtype matching rules
- •14.6.7.2 Runtime rowtype matching rules
- •14.6.7.3 Cursor variable aliases
- •14.6.7.4 Scope of cursor object
- •14.6.8 Passing Cursor Variables as Arguments
- •14.6.8.1 Identifying the ref cursor type
- •14.6.8.2 Setting the parameter mode
- •14.6.9 Cursor Variable Restrictions
- •14.7 Cursor Expressions (Oracle9i)
- •14.7.1 Using Cursor Expressions
- •14.7.2 Restrictions on Cursor Expressions
- •Chapter 15. Dynamic sql and Dynamic pl/sql
- •15.1 Nds Statements
- •15.1.1 The execute immediate Statement
- •15.1.2 The open for Statement
- •15.2 Multirow Queries with Cursor Variables
- •15.2.1 Fetch into Variables or Records
- •15.2.2 The using Clause in open for
- •15.2.3 Generic group by Procedure
- •15.2.4 Generic group by Package
- •15.3 Binding Variables
- •15.3.1 Binding Versus Concatenation
- •15.3.2 Limitations on Binding
- •15.3.3 Argument Modes
- •15.3.4 Duplicate Placeholders
- •15.3.5 Passing null Values
- •15.4 Working with Objects and Collections
- •15.5 Building Applications with nds
- •15.5.1 Sharing nds Programs with Invoker Rights
- •15.5.2 Error Handling
- •15.5.3 Dynamic pl/sql
- •15.5.3.1 Dramatic code reduction
- •15.5.3.2 Generic calculator function
- •15.6 Nds Utility Package
- •15.7 Comparing nds and dbms_sql
- •15.7.1 Eyeballing Equivalent Implementations
- •15.7.2 What Are nds and dbms_sql Good For?
- •Part V: pl/sql Application Construction
- •Chapter 16. Procedures, Functions,and Parameters
- •16.1 Modular Code
- •16.2 Procedures
- •Figure 16-1. The apply_discount procedure
- •16.2.1 Calling a Procedure
- •16.2.2 The Procedure Header
- •16.2.3 The Procedure Body
- •16.2.4 The end Descriptor
- •16.2.5 The return Statement
- •16.3 Functions
- •16.3.1 Structure of a Function
- •Figure 16-2. The tot_sales function
- •16.3.2 The return Datatype
- •16.3.3 The end Descriptor
- •16.3.4 Calling a Function
- •16.3.5 Functions Without Parameters
- •16.3.6 The Function Header
- •16.3.7 The Function Body
- •16.3.8 The return Statement
- •16.3.8.1 Return any valid expression
- •16.3.8.2 Multiple returNs
- •16.3.8.3 Return as last executable statement
- •16.4 Parameters
- •16.4.1 Defining Parameters
- •16.4.2 Actual and Formal Parameters
- •16.4.3 Matching Actual and Formal Parameters in pl/sql
- •16.4.3.1 Positional notation
- •Figure 16-3. Matching actual with formal parameters (positional notation)
- •16.4.3.2 Named notation
- •16.4.3.3 Benefits of named notation
- •16.4.4 Parameter Modes
- •16.4.4.1 In mode
- •16.4.4.2 Out mode
- •16.4.4.3 In out mode
- •16.4.5 The nocopy Parameter Mode Hint
- •16.4.5.1 Restrictions on nocopy
- •16.4.5.2 Impact of nocopy
- •16.4.6 Default Values
- •16.5 Local Modules
- •Figure 16-4. Local modules are hidden and inaccessible outside the program
- •16.5.1 Benefits of Local Modularization
- •16.5.1.1 Reducing code volume
- •16.5.1.2 Improving readability
- •16.5.2 Scope of Local Modules
- •16.5.3 Sprucing Up Your Code with Local Modules
- •16.6 Module Overloading
- •16.6.1 Benefits of Overloading
- •16.6.1.1 Supporting many data combinations
- •16.6.2 Restrictions on Overloading
- •16.7 Forward Declarations
- •16.8 Advanced Topics
- •16.8.1 Calling Your Function Inside sql
- •16.8.1.1 Requirements for calling functions in sql
- •16.8.1.2 Restrictions on user-defined functions in sql
- •16.8.1.3 Replacing decodEs with if statements
- •16.8.1.4 The pragma restrict_references (Oracle8 and earlier)
- •16.8.2 Table Functions
- •16.8.2.1 Calling a function in a from clause
- •16.8.2.2 Creating a pipelined function
- •16.8.2.3 Building a transformative function
- •16.8.2.4 Enabling a function for parallel execution
- •16.8.3 Deterministic Functions
- •16.9 Go Forth and Modularize!
- •Chapter 17. Packages
- •17.1 Why Packages?
- •17.1.1 Demonstrating the Power of the Package
- •17.1.2 Some Package-Related Concepts
- •17.1.3 Diagramming Privacy
- •Figure 17-1. Booch diagram showing public and private package elements
- •17.2 Rules for Building Packages
- •17.2.1 The Package Specification
- •17.2.2 The Package Body
- •17.2.3 Initializing Packages
- •17.2.3.1 Execute complex initialization logic
- •17.2.3.2 Cache static session information
- •17.2.3.3 Avoid side effects when initializing
- •17.2.3.4 When initialization fails
- •17.3 Rules for Calling Packaged Elements
- •17.4 Working with Package Data
- •17.4.1 Global Within a Single Oracle Session
- •17.4.2 Global Public Data
- •17.4.3 Packaged Cursors
- •17.4.3.1 Declaring packaged cursors
- •17.4.3.2 Working with packaged cursors
- •17.4.4 Serializable Packages
- •17.5 When to Use Packages
- •17.5.1 Encapsulating Data Manipulation
- •17.5.2 Avoiding Hardcoding of Literals
- •17.5.3 Improving Usability of Built-in Features
- •17.5.4 Grouping Together Logically Related Functionality
- •17.5.5 Caching Static Session Data
- •17.6 Packages and Object Types
- •Chapter 18. Triggers
- •18.1 Dml Triggers
- •Figure 18-1. Dml triggers fire in response to changes to a database table
- •18.1.1 Dml Trigger Concepts
- •18.1.1.1 Dml trigger scripts
- •18.1.1.2 Transaction participation
- •18.1.2 Creating a dml Trigger
- •18.1.2.1 The when clause
- •18.1.2.2 Working with new and old pseudo-records
- •18.1.2.3 Determining the dml action within a trigger
- •18.1.3 Dml Trigger Example: No Cheating Allowed!
- •18.1.3.1 Applying the when clause
- •18.1.3.2 Using pseudo-records to fine-tune trigger execution
- •18.1.4 Multiple Triggers of the Same Type
- •18.1.5 Mutating Table Errors: Problem and Solution
- •18.1.5.1 Mutating tables and foreign keys
- •18.1.5.2 Getting around the mutating table error
- •Figure 18-2. Using a collection as a work list to bypass mutating trigger errors
- •18.1.5.3 The dwindling mutation zone
- •18.2 Ddl Triggers
- •18.2.1 Creating a ddl Trigger
- •18.2.2 Available Events
- •18.2.3 Available Attributes
- •18.2.4 Working with Events and Attributes
- •18.2.4.1 What column did I touch?
- •18.2.4.2 Lists returned by attribute functions
- •18.2.5 Dropping the Undroppable
- •18.3 Database Event Triggers
- •18.3.1 Creating a Database Event Trigger
- •18.3.2 The startup Trigger
- •18.3.3 The shutdown Trigger
- •18.3.4 The logon Trigger
- •18.3.5 The logoff Trigger
- •18.3.6 The servererror Trigger
- •18.3.6.1 Servererror examples
- •18.3.6.2 Central error handler
- •18.3.7 Impact of Invalid Triggers
- •18.4 Instead of Triggers
- •18.4.1 Creating an instead of Trigger
- •18.4.2 The instead of insert Trigger
- •18.4.3 The instead of update Trigger
- •18.4.4 The instead of delete Trigger
- •18.4.5 Populating the Tables
- •18.5 After suspend Triggers
- •18.5.1 Setting Up for the after suspend Trigger
- •18.5.2 Looking at the Actual Trigger
- •18.5.3 Creating the after suspend Trigger
- •18.5.4 The ora_space_error_info Function
- •18.5.5 The dbms_resumable Package
- •18.5.6 Trapped Multiple Times
- •18.6 Maintaining Triggers
- •18.6.1 Disabling, Enabling, and Dropping Triggers
- •18.6.2 Viewing Triggers
- •18.6.3 Checking the Validity of Triggers
- •Chapter 19. Managing pl/sql Applications
- •19.1 Managing and Analyzing Code in the Database
- •19.1.1 Data Dictionary Views for pl/sql Programmers
- •19.1.2 Displaying Information About Stored Objects
- •19.1.3 Displaying and Searching Source Code
- •19.2 Protecting Stored Code
- •19.2.1 How to Wrap Code
- •19.2.2 Working with Wrapped Code
- •19.3 Using Native Compilation
- •19.3.2 Interpreted Versus Native Compilation Modes
- •19.4 Testing pl/sql Programs
- •19.4.1 Typical, Tawdry Testing Techniques
- •Figure 19-1. The utPlsql architecture
- •19.4.2 For More Information...
- •19.5 Debugging pl/sql Programs
- •19.5.1 The Wrong Way to Debug
- •19.5.1.1 Disorganized debugging
- •19.5.1.2 Irrational debugging
- •19.5.2 Debugging Tips and Strategies
- •19.5.2.1 Use a source code debugger
- •19.5.2.2 Gather data
- •19.5.2.3 Remain logical at all times
- •19.5.2.4 Analyze instead of trying
- •19.5.2.5 Take breaks and ask for help
- •19.5.2.6 Change and test one area of code at a time
- •19.6 Tuning pl/sql Programs
- •19.6.1 Analyzing Performance of pl/sql Code
- •19.6.2 Tracing Execution of Your Code
- •19.6.2.1 Installing dbms_trace
- •19.6.2.2 Dbms_trace programs
- •19.6.2.3 Controlling trace file contents
- •19.6.2.4 Pausing and resuming the trace process
- •19.6.2.5 Format of collected data
- •19.7 Improving Application Performance
- •19.7.1 Avoid Unnecessary Code Execution
- •19.7.1.1 The search for unnecessary code
- •19.7.1.2 Check your loops
- •19.7.1.3 Defer execution until needed
- •19.7.2 Be a Good Listener
- •19.7.3 Use Package Data to Minimize sql Access
- •19.7.4 Use bulk collect and forall
- •Part VI: Advanced pl/sql Topics
- •Chapter 20. Pl/sql's Runtime Architecture
- •20.1 Looking Under the Hood
- •20.1.1 Pl/sql Concepts
- •Figure 20-1. Execution of a do-nothing anonymous block
- •Figure 20-2. Execution of an anonymous block that contains sql
- •Figure 20-3. Execution of a program that calls a stored procedure
- •Figure 20-4. Execution of an anonymous block that calls a natively compiled program
- •20.1.2 Physical Storage of Server-Side pl/sql
- •20.1.3 DianAs Who Grew Too Much
- •20.2 Dependency Management
- •20.2.1 Dependencies in Server-Side pl/sql
- •Figure 20-5. Dependency graph of the bookworm package
- •20.2.2 Healing Invalids
- •20.2.2.1 Recompiling by hand
- •20.2.2.2 Recompiling by script
- •20.2.2.3 Automatic recompilation
- •20.2.3 Dependencies in Client-Side pl/sql
- •Figure 20-6. Viewing "References" information for a package body in a client-side pl/sql library
- •Figure 20-7. "Referenced By" information shows only those dependencies in the current module
- •20.2.4 Remote Dependencies
- •20.3 Pl/sql's Use of Memory in the Oracle Server
- •20.3.1 Server Memory 101
- •Figure 20-8. Oracle memory and process architecture in dedicated vs. Shared server configurations
- •20.3.2 Cursors and Memory
- •20.3.3 Tips on Reducing Memory Use
- •20.3.3.1 Statement sharing
- •20.3.3.2 Bind variables
- •20.3.3.3 Packaging to improve memory use
- •20.3.3.4 Large collections in pl/sql
- •20.3.3.5 Preservation of state
- •20.3.3.6 Global, but only within a single Oracle session
- •Figure 20-9. Two Oracle connections between Oracle Forms and Oracle Graphics
- •20.3.4 A Trace of Memory
- •20.4 The Processing of Server-Side pl/sql
- •20.4.1 Compiling an Anonymous Block
- •20.4.2 Compiling a Stored Object
- •20.4.3 Executing pl/sql
- •20.5 Pl/sql Code on the Client
- •Figure 20-10. Oracle client-side runtime environment invoking a stored procedure
- •20.5.1 Supported Versions and Features
- •20.5.2 Limitations of Oracle's Remote Invocation Model
- •20.5.3 Client-Side pl/sql Libraries
- •20.5.3.1 Client pl/sql libraries at design time
- •20.5.3.2 Client pl/sql libraries at runtime
- •20.6 Execution Authority Models
- •20.6.1 The Definer Rights Model
- •Figure 20-11. Controlling access to data with the definer rights model
- •20.6.1.1 Advantages of definer rights
- •20.6.1.2 Disadvantages of definer rights
- •20.6.1.2.1 Where'd my table go?
- •20.6.1.2.2 How do I maintain all that code?
- •Figure 20-12. Repetitive installations of code needed with definer rights
- •20.6.1.2.3 Dynamic sql and definer rights
- •20.6.2 The Invoker Rights Model
- •Figure 20-13. Use of invoker rights model
- •20.6.2.1 Invoker rights syntax
- •20.6.2.2 Some rules and restrictions
- •20.6.3 Combining Rights Models
- •20.7.1 The Single-Processor Variation
- •20.7.2 The Symmetric Multiprocessor (smp) Variation
- •20.7.3 The Clustered Variation
- •20.8 What You Need to Know
- •Chapter 21. Object-Oriented Aspects of pl/sql
- •21.1 Introduction to Oracle's Object Features
- •21.2 An Extended Example
- •21.2.1 A Tree of Types
- •Figure 21-1. Type hierarchy for a trivial library catalog
- •21.2.1.1 Creating a base type
- •21.2.1.2 Creating a subtype
- •21.2.2 Methods
- •21.2.3 Storing, Retrieving, and Using Persistent Objects
- •21.2.3.1 Object identity
- •21.2.3.2 The value function
- •21.2.3.3 The treat function
- •21.2.4 Evolution and Creation
- •21.2.5 Back to Pointers?
- •21.2.5.1 Using reFs
- •21.2.5.2 The reftohex function
- •21.2.5.3 The utl_ref package
- •21.2.5.4 ReFs and type hierarchies
- •21.2.5.5 Dangling reFs
- •21.2.6 Generic Generics: The any Datatypes
- •21.2.6.1 Processing an anydata value
- •21.2.6.2 Creating a transient type
- •21.2.7 I Can Do It Myself
- •21.2.8 Comparing Objects
- •21.2.8.1 Attribute-level comparison
- •21.2.8.2 The map method
- •21.2.8.3 The order method
- •21.2.8.4 Additional comparison recommendations
- •21.3 Object Views
- •Figure 21-2. Object views allow you to bind an object type definition to (existing) relational tables
- •21.3.1 The Existing Relational System
- •21.3.2 Object View with a Collection Attribute
- •21.3.3 Object Subview
- •21.3.4 Object View with Inverse Relationship
- •21.3.5 Instead of Triggers
- •21.3.5.1 The case against
- •21.3.5.2 The case for
- •21.3.5.3 The bigger question
- •21.3.6 Differences Between Object Views and Object Tables
- •21.3.6.1 Oid uniqueness
- •21.3.6.2 "Storeability" of physical versus virtual reFs
- •21.3.6.3 ReFs to non-unique oiDs
- •21.4 Maintaining Object Types and Object Views
- •21.4.1 Privileges
- •21.4.1.1 The execute privilege
- •21.4.1.2 The under privilege
- •21.4.1.3 The debug privilege
- •21.4.1.4 The dml privileges
- •21.5 Pontifications
- •Chapter 22. Calling Java from pl/sql
- •22.1 Oracle and Java
- •Figure 22-1. Accessing jsPs from within the Oracle database
- •22.2 Getting Ready to Use Java in Oracle
- •22.2.1 Installing Java
- •22.2.2 Building and Compiling Your Java Code
- •22.2.3 Setting Privileges for Java Development and Execution
- •22.2.3.1 Oracle8i Java security
- •22.2.3.2 Oracle9i Java security
- •22.3 A Simple Demonstration
- •22.3.1 Finding the Java Functionality
- •22.3.2 Building a Custom Java Class
- •Figure 22-2. A simple Java class used to delete a file
- •22.3.3 Compiling and Loading into Oracle
- •22.3.4 Building a pl/sql Wrapper
- •22.3.5 Deleting Files from pl/sql
- •22.4 Using loadjava
- •Figure 22-3. Loading Java elements into Oracle
- •22.5 Using dropjava
- •22.6 Managing Java in the Database
- •22.6.1 The Java Namespace in Oracle
- •22.6.2 Examining Loaded Java Elements
- •22.7 Using dbms_ java
- •22.7.1 Longname: Converting Java Long Names
- •22.7.2 Get_, set_, and reset_compiler_option: Getting and Setting Compiler Options
- •22.7.3 Set_output: Enabling Output from Java
- •22.7.4 Export_source, export_resource, and export_class: Exporting Schema Objects
- •22.8 Publishing and Using Java in pl/sql
- •22.8.1 Call Specs
- •22.8.2 Some Rules for Java Wrappers
- •22.8.3 Mapping Datatypes
- •22.8.4 Calling a Java Method in sql
- •22.8.5 Exception Handling with Java
- •22.8.6 Extending File I/o Capabilities
- •22.8.6.1 Polishing up the delete method
- •22.8.6.2 Obtaining directory contents
- •22.8.7 Other Examples
- •Chapter 23. External Procedures
- •23.1 Introduction to External Procedures
- •23.1.1 Example: Invoking an Operating System Command
- •23.1.2 Architecture of External Procedures
- •Figure 23-1. Invoking an external procedure that uses the default agent
- •23.1.3 Limitations of External Procedures
- •23.2 The Oracle Net Configuration
- •23.2.1 Specifying the Listener Configuration
- •23.2.2 Security Characteristics of the Configuration
- •23.3 Creating an Oracle Library
- •23.4 Writing the Call Specification
- •23.4.1 The Call Spec: Overall Syntax
- •23.4.2 Parameter Mapping: The Example Revisited
- •23.4.3 Parameter Mapping: The Full Story
- •23.4.4 More Syntax: The parameters Clause
- •23.4.5 Parameters Properties
- •23.4.5.1 The indicator property
- •23.4.5.2 The length property
- •23.4.5.3 The maxlen property
- •23.4.5.4 The charsetid and charsetform properties
- •23.5 Raising an Exception from the Called c Program
- •23.6 Nondefault Agents
- •23.7 Maintaining External Procedures
- •23.7.1 Dropping Libraries
- •23.7.2 Data Dictionary
- •23.7.3 Rules and Warnings
Figure 8-2. Forward and reverse searches with instr
We have found INSTR to be a very handy function, especially when used to the fullest extent possible. Most programmers make use of (and may only be aware of) only the first two parameters. Use INSTR to search from the end of the string? Search for the nth appearance as opposed to just the first appearance? "Wow!" many programmers would say, "I didn't know it could do that." Take the time to get familiar with INSTR and use all of its power.
|
Let's look at some examples of INSTR. In these examples, you will see all four parameters used in all their permutations. As you write your own programs, keep in mind the different ways in which INSTR can be used to extract information from a string; it can greatly simplify the code you write to parse and analyze character data.
Find the first occurrence of "archie" in "bug-or-tv-character?archie":
INSTR ('bug-or-tv-character?archie', 'archie') --> 21
The starting position and the nth appearance both defaulted to 1.
Find the last occurrence of "ar" in "bug-or-tv-character?archie".
INSTR ('bug-or-tv-character?archie', 'ar', -1) --> 21
Were you thinking that the answer might be 6? Remember that the character position returned by INSTR is always calculated from the leftmost character of the string as position 1. The easiest way to find the last of anything in a string is to specify a negative number for the starting position. I did not have to specify the nth appearance (leaving me with a default value of 1), because the last occurrence is also the first when searching backwards.
Find the second-to-last occurrence of "a" in "bug-or-tv-character?archie":
INSTR ('bug-or-tv-character?archie', 'a', -1, 2) --> 15
No surprises here. Counting from the back of the string, INSTR passes over the "a" in archie because that is the last occurrence, and searches for the next occurrence. Again, the character position is counted from the leftmost character, not the rightmost character, in the string.
Find the position of the letter "t" closest to (but not past) the question mark in the string "bug-or-tv-character?archie tophat":
search_string := 'bug-or-tv-character?archie tophat';
tee_loc :=
INSTR (search_string, 't',
-1 * (LENGTH (search_string) - INSTR (search_string, '?') +1));
I dynamically calculate the location of the question mark (actually, the first question mark in the string; I assume that there is only one). Then I subtract that from the full length of the string and multiply by -1 because I need to count the number of characters from the end of the string. I then use that value to kick off the search for the closest prior "t".
This example is a good reminder that any of the parameters to INSTR can be complex expressions that call other functions or perform their own calculations. This fact is also highlighted in the next INSTR example.
Use INSTR to confirm that a user entry is valid. In the following code, I check to see if the command selected by the user is found in the list of valid commands. If so, I execute that command:
IF INSTR ('|ADD|DELETE|CHANGE|VIEW|CALC|', '|' || cmd || '|') > 0
THEN
execute_command (cmd);
ELSE
DBMS_OUTPUT.PUT_LINE
(' You entered an invalid command. Please try again...');
END IF;
In this case, I use the concatenation operator to construct the string that I will search for in the command list. I have to append a vertical bar (|) to the selected command because it is used as a delimiter in the command list. I also use the call to INSTR in a Boolean expression. If INSTR finds a match in the string, it returns a nonzero value; the Boolean expression therefore evaluates to TRUE, and I can go on with my processing. Otherwise, I display an error message.
The following code example, generated using Unicode UTF-8 as the database character set, illustrates the difference in semantics between INSTR and INSTRB, the two variations of INSTR that you are most likely to use:
DECLARE
--The underlying database datatype for this example is Unicode UTF-8
x CHAR(30 CHAR) := 'The character ã is two-bytes.';
BEGIN
--Find the location of "is" in terms of characters
DBMS_OUTPUT.PUT_LINE(INSTR(x,'is'));
--Find the location of "is" in terms of bytes
DBMS_OUTPUT.PUT_LINE(INSTRB(x,'is'));
END;
The output is:
17
18
The difference in the location of the word "is" comes about because the character ã is represented in Unicode UTF-8 using two bytes. Thus, while "is" is 17 characters into the string, it is at the same time 18 bytes into the string.
The INSTRC function is capable of recognizing decomposed characters. As described in the section on COMPOSE, an alternate representation of ã is a\0303. The following example demonstrates that INSTRC recognizes this alternate representation, whereas INSTR does not:
DECLARE
--The underlying database datatype for this example is Unicode UTF-8
x CHAR(40 CHAR) := UNISTR('The character a\0303 could be composed.');
BEGIN
--INSTR won't see that a\0303 is the same as ã
DBMS_OUTPUT.PUT_LINE(INSTR(x,'ã'));
--INSTRC, however, will recognize that a\0303 = ã
DBMS_OUTPUT.PUT_LINE(INSTRC(x,'ã'));
END;
The output is:
0
15
According to INSTR, the string x does not contain ã at all. INSTRC, on the other hand, recognizes that a\0303 is an alternate representation for the same character. The UNISTR function is used in the declaration of x to convert the ASCII string that we can read into a Unicode string for the example.
The INSTR2 and INSTR4 functions allow you to search for code units and code points respectively, which may not correspond to complete characters. For the following example, AL16UTF16 is used as the national language character set. The character ã is represented in the string x as two code points: one for "a", and the other (\0303) for the tilde that goes above the "a". INSTRC and INSTR4 are then used to search for the location of the \0303 code point:
DECLARE
--The underlying database datatype for this example is Unicode UTF-16
x NCHAR(40) := UNISTR('The character a\0303 could be composed.');
BEGIN
--Find the location of "\0303" using INSTRC
DBMS_OUTPUT.PUT_LINE(INSTRC(x,UNISTR('\0303')));
--Find the location of "\0303" using INSTR4
DBMS_OUTPUT.PUT_LINE(INSTR4(x,UNISTR('\0303')));
END;
The output is:
0
16
The INSTRC function works with full characters, and is of no use when you need to search for a code point that does not represent a complete character. INSTR4, on the other hand, is able to locate the \0303 code point. The return value of 16 indicates that \0303 is the 16th code point in the string.
INSTR2 works like INSTR4, but allows you to search for UCS-4 code units. Look at the following example:
DECLARE
x NCHAR(40) := UNISTR('This is a \D834\DD1E test');
BEGIN
DBMS_OUTPUT.PUT_LINE (INSTR2(x, UNISTR('\D834')));
DBMS_OUTPUT.PUT_LINE (INSTR4(x, UNISTR('\D834')));
END;
11
0
\D834\DD1E (the musical G clef) is a surrogate pair; each value in a surrogate pair is a code unit. Together, the two code units represent a single code point. This example shows how INSTR2 matches on just the high surrogate, whereas INSTR4 does not. That's because INSTR2 matches in terms of code units, whereas INSTR4 matches in terms of code points. Matching on just one code unit of a surrogate is sort of equivalent to searching and matching on the first byte of a multibyte character. Normally, you don't want to do this.
LEAST |
|
|
|
LEAST 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 to be sorted in ascending order. The ordering of the strings is based on character code in the database character set. LEAST has the following specification:
FUNCTION LEAST (string1 IN VARCHAR2, string2 IN VARCHAR2,...)
RETURN VARCHAR2
Following is an example:
BEGIN
DBMS_OUTPUT.PUT_LINE(LEAST('Gennick','Pribyl','Feuerstein'));
END;
Feuerstein
Also see the GREATEST function, which is the opposite of LEAST.
LENGTH, LENGTHB, LENGTHC, LENGTH2, and LENGTH4 |
|
|
|
The LENGTH family of functions returns the length of a string. The length can be returned in any of the following units:
LENGTH
Characters
LENGTHB
Bytes
LENGTHC
Unicode characters, normalizing where possible
LENGTH2
Code units
LENGTH4
Code points
The same pattern is used for the specification of all LENGTH functions:
FUNCTION LENGTH (string1 VARCHAR2) RETURN NUMBER
If string1 is NULL, then LENGTH returns NULL—not zero! Remember that a NULL string is a "nonvalue." Therefore, it cannot have a length, even a zero length.
The LENGTH function, in fact, will never return zero; it will always return either NULL or a positive number.
|
Here are some examples of LENGTH:
LENGTH (NULL) --> NULL
LENGTH ('') --> NULL -- Same as a NULL string.
LENGTH ('abcd') --> 4
LENGTH ('abcd ') --> 5
If string1 is a fixed-length CHAR datatype, then LENGTH counts the trailing blanks in its calculation. So the LENGTH of a fixed-length string is always the declared length of the string. If you want to compute the length of the nonblank characters in string1, you will need to use the RTRIM function to remove the trailing blanks (RTRIM is discussed later in this chapter). In the following example, length1 is set to 60 and length2 is set to 14.
DECLARE
company_name CHAR(60) := 'ACME PLUMBING';
length1 NUMBER;
length2 NUMBER;
BEGIN
length1 := LENGTH (company_name);
length2 := LENGTH (RTRIM (company_name));
END;
The following example uses an NVARCHAR2 variable and the AL16UTF16 character set to highlight the differences between LENGTH, LENGTHB, and LENGTHC. Remember that AL16UTF16 is Oracle's name for UTF-16, a character set in which each character is represented using two bytes.
DECLARE
--NVARCHAR2 = UTF-16 in this example.
x NVARCHAR2(50) :=
UNISTR('The character ã and its decomposed equivalent: a\0303');
BEGIN
DBMS_OUTPUT.PUT_LINE(LENGTH(x));
DBMS_OUTPUT.PUT_LINE(LENGTHB(x));
DBMS_OUTPUT.PUT_LINE(LENGTHC(x));
END;
The output is:
49
98
48
LENGTH counts characters, but sees a\0303 as two separate, two-byte characters. LENGTHB counts bytes, returning a value that is twice the number of two-byte characters reported by LENGTH.LENGTHC counts Unicode characters, and recognizes that a\0303 really represents the single character ã.
LOWER |
|
|
|
The LOWERfunction converts all letters in the specified string to lowercase. The specifications for the LOWER function are:
FUNCTION LOWER (string1 IN CHAR) RETURN CHAR
FUNCTION LOWER (string1 IN VARCHAR2) RETURN VARCHAR2
As noted earlier, LOWER and UPPER return a fixed-length string if the incoming string is fixed-length. LOWER will not change any characters in the string that are not letters, as case is irrelevant for numbers and special characters.
Here are some examples of the effect of LOWER:
LOWER ('BIG FAT LETTERS') --> 'big fat letters'
LOWER ('123ABC') --> '123abc'
LOWER and UPPER are useful for guaranteeing a consistent case when comparing strings. PL/SQL is not a case-sensitive language with regard to its own syntax and names of identifiers, but it is sensitive to case in character strings, whether they are found in named constants, literals, or variables. The string "ABC" is not the same as "abc", and this can cause problems in your programs if you are not careful and consistent in your handling of such values.
LPAD |
|
|
|
By default, PL/SQL strips all trailing blanks from a character string unless it is declared with a fixed-length CHAR datatype. There are occasions, however, when you really want some spaces (or even some other characters) added to the front or back of your string. LPAD (and its right-leaning cousin, RPAD) give you this capability. The LPAD function returns a string padded to the left (hence the "L") to a specified length and with a specified pad string. The specification of the LPAD function is:
FUNCTION LPAD
(string1 IN VARCHAR2,
padded_length IN NUMBER
[, pad_string IN VARCHAR2])
RETURN VARCHAR2
LPAD returns string1 padded on the left to length padded_length with the optional character string pad_string. If you do not specify pad_string, then string1 is padded on the left with spaces. You must specify the padded_length. If string1 already has a length equal to padded_length, then LPAD returns string1 without any additional characters. If padded_length is smaller than the length of string1, LPAD effectively truncates string1—it returns only the first padded_length characters of the incoming string1.
As you can see, LPAD can do a lot more than just add spaces to the left of a string. Let's look at some examples of how useful LPAD can be.
Display the number padded left with zeros to a length of 10:
LPAD ('55', 10, '0') --> '0000000055'
Display the number padded left with zeros to a length of 5:
LPAD ('12345678', 5, '0') --> '12345'
LPAD interprets its padded_length as the maximum length of the string that it may return. As a result, it counts padded_length number of characters from the left (start of the string) and then simply returns that substring of the incoming value.
Place the phrase "sell!" in front of the names of selected stocks, up to a string length of 43.
LPAD ('HITOP TIES', 43, 'sell!')
-->
'sell!sell!sell!sell!sell!sell!selHITOP TIES'
Because the length of "HITOP TIES" is 10 and the length of "sell!" is 5, there is no room for seven full repetitions of the pad string. As a result, the seventh repetition (counting from the left) of "sell!" lost its last two characters. So you can see that LPAD does not pad by adding to the left of the original string until it runs out of room. Instead, it figures out how many characters it must pad by to reach the total, constructs that full padded fragment, and finally appends the original string to this fragment.
LTRIM |
|
|
|
The LTRIM function is the opposite of LPAD. Whereas LPAD adds characters to the left of a string, LTRIM removes, or trims, characters from the left of the string. And just like LPAD, LTRIM offers much more flexibility than simply removing leading blanks. The specification of the LTRIM function is:
FUNCTION LTRIM (string1 IN VARCHAR2 [, trim_string IN VARCHAR2])
RETURN VARCHAR2
LTRIM returns string1 with all leading characters removed up to the first character not found in the trim_string. The second parameter is optional and defaults to a single space.
There is one important difference between LTRIM and LPAD. LPAD pads to the left with the specified string, and repeats that string (or pattern of characters) until there is no more room. LTRIM, on the other hand, removes all leading characters that appear in the trim string, not as a pattern, but as individual candidates for trimming.
Here are some examples:
Trim all leading blanks from " Way Out in Right Field":
LTRIM (' Way Out in Right Field') --> 'Way Out in Right Field'
Because we did not specify a trim string, it defaults to a single space, so all leading spaces are removed.
Remove all numbers from the front of the string:
my_string := '70756234LotsaLuck';
LTRIM (my_string, '0987612345') --> 'LotsaLuck'
By specifying every possible digit in the trim string, we ensure that any and all numbers will be trimmed, regardless of the order in which they occur (and the order in which we place them in the trim string).
What if we wanted to remove only a specific pattern, say the letters "abc", from the front of the string? We couldn't use LTRIM because it trims off any matching individual characters. To remove a leading pattern from a string—or to replace one pattern with another—you will want to make use of the REPLACE function, which is discussed next.
|
REPLACE |
|
|
|
The REPLACE function returns a string in which all occurrences of a specifiedmatch string are replaced with a replacement string. REPLACE is useful for searching a pattern of characters, and then changing all instances of that pattern in a single function call. The specification of the REPLACE function is:
FUNCTION REPLACE (string1 IN VARCHAR2, match_string IN VARCHAR2
[, replace_string IN VARCHAR2])
RETURN VARCHAR2
If you do not specify the replacement string, then REPLACE simply removes all occurrences of the match_string in string1. If you specify neither a match string nor a replacement string, REPLACE returns NULL.
Here are several examples using REPLACE:
Remove all instances of the letter "C" in the string "CAT CALL":
REPLACE ('CAT CALL', 'C') --> 'AT ALL'
Because we did not specify a replacement string, REPLACE changed all occurrences of "C" to NULL.
Replace all occurrences of "99" with "100" in the following string:
REPLACE ('Zero defects in period 99 reached 99%!', '99', '100')
-->
'Zero defects in period 100 reached 100%!'
Handle occurrences of a single quote within a query criteria string. The single quote is a string terminator symbol, indicating the start and/or end of the literal string. I once ran into this requirement when building query-by-example strings in Oracle Forms. If the user enters a string with a single quote in it, such as:
Customer didn't have change.
and then the program concatenates that string into a larger string, the resulting SQL statement (created dynamically by Oracle Forms in Query Mode) fails because there are unbalanced single quotes in the string. You can resolve this problem by converting that single quote into two single quotes in a row, thereby telling SQL that you really intend for one quote to be part of the string. Use the following REPLACE to do this:
criteria_string := REPLACE (criteria_string, '''', '''''');
The four quotes in the second parameter resolve to a string containing one single quote. The six quotes in the third parameter resolve to a string containing two single quotes. In each case, the outer pair of quotes delimits the string, and each pair of single quotes inside that outer pair are interpreted as just one single quote within the string.
Remove all leading instances of "abc" from the string:
"abcabcccccI LOVE CHILIabc"
This is the behavior we were looking at in the previous section on LTRIM. We want to remove all instances of the pattern "abc" from the beginning of the string, but we do not want to remove that pattern throughout the rest of the string. In addition, we want to remove "abc" only as a pattern; if we encounter three contiguous c's ("ccc"), on the other hand, they should not be removed from the string. This task is less straightforward than it might at first seem. If we simply apply REPLACE to the string, it will remove all occurrences of "abc", instead of just the leading instances. For example:
REPLACE ('abcabccccI LOVE CHILIabc', 'abc') --> 'cccI LOVE CHILI'
That is not what we want in this case. If we use LTRIM, on the other hand, we will be left with none of the leading c's, as demonstrated in a previous example:
LTRIM ('abcabcccccI LOVE CHILIabc', 'abc') --> 'I LOVE CHILIabc'
And this is not quite right either. We want to be left with "cccI LOVE CHILIabc" (please do not ask why), and it turns out that the way to get it is to use a combination of LTRIM and REPLACE. Suppose that we create a local variable as follows:
my_string := 'abcabccccI LOVE CHILIabc';
Then the following statement will achieve the desired effect by nesting calls to REPLACE and LTRIM within one another:
REPLACE
(LTRIM
(REPLACE (my_string, 'abc', '@'), '@'), '@', 'abc')
-->
'cccI LOVE CHILIabc'
Here is how we would describe in English what the above statement does:
First replace all occurrences of "abc" with the special character "@" (which we are assuming does not otherwise appear in the string). Then trim off all leading instances of "@". Finally, replace all remaining occurrences of "@" with "abc".
Voilà, as they say in many of the finer restaurants in Paris. Now let's pull apart this single, rather complex statement into separate PL/SQL steps corresponding to the "natural language" description:
Replace all occurrences of "abc" with the special character "@" (which we are assuming does not otherwise appear in the string). Notice that this only affects the "abc" pattern, and not any individual appearances of "a", "b", or "c".
REPLACE ('abcabccccI LOVE CHILIabc', 'abc', '@')
--> '@@cccI LOVE CHILI@'
Trim off all leading instances of "@".
LTRIM ('@@cccI LOVE CHILI@', '@') --> 'cccI LOVE CHILI@'
Notice that LTRIM now leaves the c's in place, because we didn't ask it to remove "a" or "b" or "c"—just "@". In addition, it left the trailing "@" in the string because LTRIM deals only with characters on the leading end of the string.
Replace all remaining occurrences of "@" with "abc".
REPLACE ('cccI LOVE CHILI@', '@', 'abc') --> 'cccI LOVE CHILIabc'
And we are done. We used the first REPLACE to temporarily change the occurrences of "abc" so that LTRIM could distinguish between the pattern we wanted to get rid of and the extra characters that needed to be preserved. Then a final call to REPLACE restored the pattern in the string.
RPAD |
|
|
|
The RPAD function adds characters to the end of a character string. It returns a string padded to the right (hence the "R") to a specified length and with an optional pad string. The specification of the RPAD function is:
FUNCTION RPAD
(string1 IN VARCHAR2,
padded_length IN NUMBER
[, pad_string IN VARCHAR2])
RETURN VARCHAR2
RPAD returns string1 padded on the right to length padded_length with the optional character string pad_string. If you do not specify pad_string, then string1 is padded on the right with spaces. You must specify the padded_length. If string1 already has a length equal to padded_length, then RPAD returns string1 without any additional characters. If padded_length is smaller than the length of string1, RPAD effectively truncates string1, returning only the first padded_length characters of the incoming string1.
Let's look at some examples of RPAD:
Display the number padded right with zeros to a length of 10:
RPAD ('55', 10, '0') --> '5500000000'
I could also use TO_CHAR to convert from a number to a character (I don't know off-hand why you would do this, but it's good to remember that there are usually at least two or three ways to solve any problem):
TO_CHAR (55 * 10000000) --> '5500000000'
Display the number padded right with zeros to a length of 5:
RPAD ('12345678', 5) --> '12345'
RPAD interprets its padded_length as the maximum length of the string that it may return. As a result, it counts padded_length number of characters from the left (start of the string) and then simply returns that substring of the incoming value. This is the same behavior as that found with LPAD, described earlier in this chapter. Remember that RPAD does not return the rightmost five characters (in the above case "45678").
Place the phrase "sell!" after the names of selected stocks, up to a string length of 43:
RPAD ('HITOP TIES', 43, 'sell!')
-->
'HITOP TIESsell!sell!sell!sell!sell!sell!sel'
Because the length of "HITOP TIES" is 10 and the length of "sell!" is 5, there is no room for seven full repetitions of the pad string. As a result, the seventh repetition of "sell!" loses its last two characters.
You can use RPAD (and LPAD) to generate repetitive sequences of characters. For example, you can create a string of 60 dashes to use as a border in a report:
RPAD ('-', 60, '-')
-->
'------------------------------------------------------------'
I have used this technique in SQL*Reportwriter V1.1 where graphical objects like boxes are not really available. I can include the RPAD in a SELECT statement in the report, and then use the corresponding field in text elements to provide lines to break up the data in a report.
RTRIM |
|
|
|
The RTRIM function is the opposite of RPAD and the companion to LTRIM. While RPAD adds characters to the right of a string, RTRIM removes, or trims, characters from the end portion of the string. Just as with RPAD, RTRIM offers much more flexibility than simply removing trailing blanks. The specification of the RTRIM function is:
FUNCTION RTRIM (string1 IN VARCHAR2 [, trim_string IN VARCHAR2])
RETURN VARCHAR2
RTRIM returns string1 with all trailing characters removed up to the first character not found in the trim_string. The second parameter is optional and defaults to a single space.
Here are some examples of RTRIM:
Trim all trailing blanks from a string:
RTRIM ('Way Out in Right Field ')
--> 'Way Out in Right Field'
Because I did not specify a trim string, it defaults to a single space, so all trailing spaces are removed.
Trim all the characters in "BAM! ARGH!" from the end of a string:
my_string := 'Sound effects: BAM!ARGH!BAM!HAM';
RTRIM (my_string, 'BAM! ARGH!') --> 'Sound effects:'
This use of RTRIM strips off all the letters at the end of the string that are found in "BAM!ARGH!". This includes "BAM" and "HAM", so those words too are removed from the string even though "HAM" is not listed explicitly as a "word" in the trim string. Also, the inclusion of two exclamation marks in the trim string is unnecessary, because RTRIM is not looking for the word "ARGH!", but for each of the letters in "ARGH!".
|
SOUNDEX |
|
|
|
The SOUNDEX function allows you to perform string comparisons based on phonetics (the way a word sounds) as opposed to semantics (the way a word is spelled).[1]
[1] Oracle Corporation uses the algorithm in Donald Knuth's The Art of Computer Programming, Volume 3, to generate the phonetic representation.
SOUNDEX returns a character string that is the "phonetic representation" of the argument. The specification of the SOUNDEX function is as follows:
FUNCTION SOUNDEX (string1 IN VARCHAR2) RETURN VARCHAR2
Here are some of the values SOUNDEX generated, and their variations according to the input string:
SOUNDEX ('smith') --> 'S530'
SOUNDEX ('SMYTHE') --> ''S530'
SOUNDEX ('smith smith') --> 'S532'
SOUNDEX ('smith z') --> 'S532'
SOUNDEX ('feuerstein') --> 'F623'
SOUNDEX ('feuerst') --> 'F623'
Keep the following SOUNDEX rules in mind when using this function:
The SOUNDEX value always begins with the first letter in the input string.
SOUNDEX uses only the first five consonants in the string to generate the return value.
Only consonants are used to compute the numeric portion of the SOUNDEX value. Except for leading vowels, all vowels are ignored.
SOUNDEX is not case-sensitive. Upper- and lowercase letters return the same SOUNDEX value.
The SOUNDEX function is useful for ad hoc queries, and any other kinds of searches where the exact spelling of a database value is not known or easily determined.
|
SUBSTR, SUBSTRB, SUBSTRC, SUBSTR2, and SUBSTR4 |
|
|
|
The SUBSTR family of functions is one of the most common and useful set of character functions. The SUBSTR functions allow you to extract a subset of contiguous characters from a string. The substring is specified by starting position and length, and the functions differ in the units they use:
SUBSTR
Starting position and length are in terms of characters.
SUBSTRB
Starting position and length are in terms of bytes. When you use a single-byte character set, SUBSTRB and SUBSTR will return the same results.
SUBSTRC
Starting position and length are in terms of Unicode characters, after any decomposed characters have been composed.
SUBSTR2
Starting position and length are in terms of code units.
SUBSTR4
Starting position and length are in terms of code points.
All of the function specifications follow the same pattern:
FUNCTION SUBSTR
(string_in IN VARCHAR2,
start_position_in IN NUMBER
[, substr_length_in IN NUMBER])
RETURN VARCHAR2
where the arguments are as follows:
string_in
The source string
start_position_in
The starting position of the substring in string_in
substr_length_in
The length of the substring desired (the number of characters to be returned in the substring)
The last parameter, substr_length_in, is optional. If you do not specify a substring length, then SUBSTR returns all the characters to the end of string_in (from the starting position specified). The substr_length_in argument, if present, must be greater than zero.
The starting position cannot be zero. If it is less than zero, then the substring is retrieved from the back of the string. SUBSTR counts backwards substr_length_in number of characters from the end of string_in. In this case, however, the characters that are extracted are still to the right of the starting position. See Figure 8-3 for an illustration of how the different arguments are used by SUBSTR.
