Guide to SQL Tutorial Version 117
Guide to SQL Tutorial Version 117
Informix
Version 11.70
SC27-3522-01
Informix Product Family
Informix
Version 11.70
SC27-3522-01
Note
Before using this information and the product it supports, read the information in “Notices” on page B-1.
Edition
This edition replaces SC27-3522-00.
This publication contains proprietary information of IBM. It is provided under a license agreement and is protected
by copyright law. The information contained in this publication does not include any product warranties, and any
statements provided in this publication should not be interpreted as such.
When you send information to IBM, you grant IBM a nonexclusive right to use or distribute the information in any
way it believes appropriate without incurring any obligation to you.
© Copyright IBM Corporation 1996, 2012.
US Government Users Restricted Rights – Use, duplication or disclosure restricted by GSA ADP Schedule Contract
with IBM Corp.
Contents
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix
In this introduction . . . . . . . . . . . . . . . . . . ix . . . . . . . . . . . . . . .
About this publication . . . . . . . . . . . . . . . . . ix . . . . . . . . . . . . . . .
Types of users . . . . . . . . . . . . . . . . . . ix . . . . . . . . . . . . . . .
Software dependencies . . . . . . . . . . . . . . . . ix . . . . . . . . . . . . . . .
Assumptions about your locale . . . . . . . . . . . . . ix . . . . . . . . . . . . . . .
Demonstration database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . x
What's new in IBM Informix Guide to SQL: Tutorial, Version 11.70 . . . . . . . . . . . . . . . . . x
Example code conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi
Additional documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi
Compliance with industry standards . . . . . . . . . . . . . . . . . . . . . . . . . . . xi
Contents v
Call the database server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-4
SQL Communications Area . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-4
SQLCODE field . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-4
SQLERRD array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-5
SQLWARN array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-6
SQLERRM character string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-7
SQLSTATE value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-7
Retrieve single rows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-8
Data type conversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-9
What if the program retrieves a NULL value? . . . . . . . . . . . . . . . . . . . . . . 8-9
Dealing with errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-10
Retrieve multiple rows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-11
Declare a cursor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-12
Open a cursor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-12
Fetch rows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-13
Cursor input modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-14
Active set of a cursor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-14
Parts-explosion problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-16
Dynamic SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-18
Prepare a statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-19
Execute prepared SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-19
Dynamic host variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-20
Free prepared statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-20
Quick execution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-21
Embed data-definition statements . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-21
Grant and revoke privileges in applications . . . . . . . . . . . . . . . . . . . . . . . . 8-21
Assign roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-23
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-23
Contents vii
Exception handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11-67
Error trapping and recovering . . . . . . . . . . . . . . . . . . . . . . . . . . . 11-67
Scope of control of an ON EXCEPTION statement . . . . . . . . . . . . . . . . . . . . 11-68
User-generated exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11-68
Check the number of rows processed in an SPL routine. . . . . . . . . . . . . . . . . . . . 11-70
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11-71
Notices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B-1
Trademarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B-3
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . X-1
This publication is one of a series of publications that discusses the IBM® Informix®
implementation of SQL. The IBM Informix Guide to SQL: Syntax contains all the
syntax descriptions for SQL and SPL. The IBM Informix Guide to SQL: Reference
provides reference information for aspects of SQL other than the language
statements. The IBM Informix Database Design and Implementation Guide shows how
to use SQL to implement and manage your databases.
Types of users
This publication is written for the following users:
v Database users
v Database administrators
v Database-application programmers
Software dependencies
This publication is written with the assumption that you are using IBM Informix
Version 11.70.
The examples in this publication are written with the assumption that you are
using the default locale, en_us.8859-1. This locale supports U.S. English format
If you plan to use nondefault characters in your data or your SQL identifiers, or if
you want to conform to the nondefault collation rules of character data, you need
to specify the appropriate nondefault locale.
For instructions on how to specify a nondefault locale, additional syntax, and other
considerations related to GLS locales, see the IBM Informix GLS User's Guide.
Demonstration database
The DB-Access utility, which is provided with the database server products,
includes one or more of the following demonstration databases:
v The stores_demo database illustrates a relational schema with information about
a fictitious wholesale sporting-goods distributor. Many examples in IBM
Informix publications are based on the stores_demo database.
v The superstores_demo database illustrates an object-relational schema. The
superstores_demo database contains examples of extended data types, type and
table inheritance, and user-defined routines.
For information about how to create and populate the demonstration databases,
see the IBM Informix DB-Access User's Guide. For descriptions of the databases and
their contents, see the IBM Informix Guide to SQL: Reference.
The scripts that you use to install the demonstration databases reside in the
$INFORMIXDIR/bin directory on UNIX platforms and in the
%INFORMIXDIR%\bin directory in Windows environments.
The following changes and enhancements are relevant to this publication. For a
complete list of what's new in this release, see the release notes or the information
center at http://publib.boulder.ibm.com/infocenter/idshelp/v117/topic/
com.ibm.po.doc/new_features.htm.
Table 1. What's new in IBM Informix Guide to SQL: Tutorial for Version 11.70.xC1
Overview Reference
New editions and product names For more information about the Informix product
family, go to http://www.ibm.com/software/data/
IBM Informix Dynamic Server editions were withdrawn and informix/.
new Informix editions are available. Some products were
also renamed. The publications in the Informix library
pertain to the following products:
v IBM Informix database server, formerly known as IBM
Informix Dynamic Server (IDS)
v IBM OpenAdmin Tool (OAT) for Informix, formerly
known as OpenAdmin Tool for Informix Dynamic Server
(IDS)
v IBM Informix SQL Warehousing Tool, formerly known as
Informix Warehouse Feature
If only SQL statements are listed in the example, they are not delimited by
semicolons. For instance, you might see the code in the following example:
CONNECT TO stores_demo
...
COMMIT WORK
DISCONNECT CURRENT
To use this SQL code for a specific product, you must apply the syntax rules for
that product. For example, if you are using an SQL API, you must use EXEC SQL
at the start of each statement and a semicolon (or other appropriate delimiter) at
the end of the statement. If you are using DB–Access, you must delimit multiple
statements with semicolons.
Tip: Ellipsis points in a code example indicate that more code would be added in
a full application, but it is not necessary to show it to describe the concept being
discussed.
Additional documentation
Documentation about this release of IBM Informix products is available in various
formats.
You can access or install the product documentation from the Quick Start CD that
is shipped with Informix products. To get the most current information, see the
Informix information centers at ibm.com®. You can access the information centers
and other Informix technical information such as technotes, white papers, and IBM
Redbooks® publications online at http://www.ibm.com/software/data/sw-
library/.
IBM Informix SQL-based products are fully compliant with SQL-92 Entry Level
(published as ANSI X3.135-1992), which is identical to ISO 9075:1992. In addition,
many features of IBM Informix database servers comply with the SQL-92
Intermediate and Full Level and X/Open SQL Common Applications Environment
(CAE) standards.
The IBM Informix Geodetic DataBlade® Module supports a subset of the data types
from the Spatial Data Transfer Standard (SDTS)—Federal Information Processing
Introduction xi
Standard 173, as referenced by the document Content Standard for Geospatial
Metadata, Federal Geographic Data Committee, June 8, 1994 (FGDC Metadata
Standard).
Your real use of a database begins with the SELECT statement, which Chapter 2,
“Compose SELECT statements,” on page 2-1, describes.
ORDERS
order
1003
05/22/98
order customer
1001 Anthony
05/20/98 Higgins
order
item 1011
tennis order 06/18/98
racquet 1013
$19.80 06/22/98
item item
2 1 case
volleyball tennis
nets balls
You design the data model when you create the database. You then insert units of
data according to the plan that the model lays out. Some books use the term
schema instead of data model.
Store data
Another difference between a database and a file is that the organization of the
database is stored with the database.
A file can have a complex inner structure, but the definition of that structure is not
within the file; it is in the programs that create or use the file. For example, a
document file that a word-processing program stores might contain detailed
structures that describe the format of the document. However, only the
word-processing program can decipher the contents of the file, because the
structure is defined within the program, not within the file.
A data model, however, is contained in the database it describes. It travels with the
database and is available to any program that uses the database. The model
defines not only the names of the data items but also their data types, so a
program can adapt itself to the database. For example, a program can find out that,
in the current database, a price item is a decimal number with eight digits, two to
the right of the decimal point; then it can allocate storage for a number of that
Query data
Another difference between a database and a file is the way you can access them.
You can search a file sequentially, looking for particular values at particular
physical locations in each line or record. That is, you might ask, “What records
have the number 1013 in the first field?” The following figure shows this type of
search.
ORDERS
In contrast, when you query a database, you use the terms that the model defines.
You can query the database with questions such as, “What orders have been placed
for products made by the Shimara Corporation, by customers in New Jersey, with
ship dates in the third quarter?” The following figure shows this type of query.
In other words, when you access data that is stored in a file, you must state your
question in terms of the physical layout of the file. When you query a database,
you can ignore the arcane details of computer storage and state your query in
terms that reflect the real world, at least to the extent that the data model reflects
the real world.
For information about how to build and implement your data model, see the IBM
Informix Database Design and Implementation Guide.
Modify data
The data model also makes it possible to modify the contents of the database with
less chance for error. You can query the database with statements such as, “Find
every stock item with a manufacturer of Presta or Schraeder, and increase its price by
13 percent.” You state changes in terms that reflect the meaning of the data. You do
not have to waste time and effort thinking about details of fields within records in
a file, so the chances for error are fewer.
The statements you use to modify stored data are covered in Chapter 6, “Modify
data,” on page 6-1.
Access-management strategies
IBM Informix supports two access-management systems:
Label-Based Access Control (LBAC)
Label-Based Access Control is an implementation of Mandatory Access
Control, which is typically used in databases that store highly sensitive
data, such as systems maintained by armed forces or security services. The
primary documentation of IBM Informix features relating to LBAC is the
IBM Informix Security Guide. IBM Informix Guide to SQL: Syntax describes
how LBAC security objects are created and maintained by the Database
Security Administrator (DBSECADM). Only the Database Server
Administrator (DBSA) can grant the DBSECADM role.
Discretionary Access Control (DAC)
Discretionary Access Control is a simpler system that involves less
overhead than LBAC. Based on access privileges and roles, DAC is enabled
in all Informix databases, including those that implement LBAC.
To support DAC, the database administrator (DBA) can define roles and assign
them to users to standardize the access privileges of groups of users who need
access to the same database objects. When the DBA assigns privileges to that role,
every user who is granted role holds those privileges when that role is activated.
In order to activate a specific role, a user must issue the SET ROLE statement. The
SQL statements used for defining and manipulating roles include: CREATE ROLE,
DROP ROLE, GRANT, REVOKE, and SET ROLE.
For more information on the SQL syntax statements for defining and manipulating
roles, see the IBM Informix Guide to SQL: Syntax.
The DBA can also define a default role to assign to individual users or to the
PUBLIC group for a specific database. The role is automatically activated when the
user establishes a connection with that database, without the requiring the user to
issue a SET ROLE statement. At connection time, each user who holds a default
role has whatever access privileges are granted to the user individually, as well as
the privileges of the default role.
Only one role that the CREATE ROLE statement defines can be in effect for a given
user at a given time. If a user who holds both a default role and one or more other
roles uses the SET ROLE statement to make a nondefault role the active role, then
any access privileges that were granted only to the default role (and not to the user
individually, nor to PUBLIC, nor to the new active role) are no longer in effect for
that user. The same user can issue the SET ROLE DEFAULT statement to reactivate
the default role, but this action disables any privileges that the user held only
through the previously enabled nondefault role.
If different default roles are assigned to the user and to PUBLIC, the default role of
the user takes precedence.
Restriction: Only the DBA or the database owner can remove the default role.
5. Use the SET ROLE DEFAULT statement to reset the current role back to the
default role.
Built-in roles:
For security reasons, IBM Informix supports certain built-in roles that are in effect
for any user who is granted the role and is connected to the database, regardless of
whether any other role is also active.
Similarly, in a database that implements LBAC security policies, the DBSA can
grant the built-in DBSECADM role. The grantee of this role becomes the Database
Security Administrator, who can define and implement LBAC security policies and
can assign security labels to data and to users.
For more information on the DBSECADM role or SQL statements for defining and
manipulating LBAC security objects, see the IBM Informix Security Guide.
For more information on default roles, see the IBM Informix Administrator's Guide.
For more information about how to grant and limit access to your database, see
the IBM Informix Database Design and Implementation Guide.
Centralized management
Databases that many people use are valuable and must be protected as important
business assets. You create a significant problem when you compile a store of
valuable data and simultaneously allow many employees to access it. You handle
this problem by protecting data while maintaining performance. The database
server lets you centralize these tasks.
Databases must be guarded against loss or damage. The hazards are many: failures
in software and hardware, and the risks of fire, flood, and other natural disasters.
Losing an important database creates a huge potential for damage. The damage
could include not only the expense and difficulty of re-creating the lost data, but
also the loss of productive time by the database users as well as the loss of
business and goodwill while users cannot work. A plan for regular backups helps
avoid or mitigate these potential disasters.
A large database that many people use must be maintained and tuned. Someone
must monitor its use of system resources, chart its growth, anticipate bottlenecks,
and plan for expansion. Users will report problems in the application programs;
someone must diagnose these problems and correct them. If rapid response is
important, someone must analyze the performance of the system and find the
causes of slow responses.
Some rules apply about how you choose entities and attributes, but they are
important only when you are designing a new database. (For more information
about database design, see the IBM Informix Database Design and Implementation
Guide.) The data model in an existing database is already set. To use the database,
you need to know only the names of the tables and columns and how they
correspond to the real world.
Tables
A database is a collection of information that is grouped into one or more tables. A
table is an array of data items organized into rows and columns. A demonstration
database is distributed with every IBM Informix database server product. A partial
table from the demonstration database follows.
A table represents all that the database administrator (DBA) wants to store about
one entity, one type of thing that the database describes. The example table, stock,
represents all that the DBA wants to store about the merchandise that a sporting
goods store stocks. Other tables in the demonstration database represent such
entities as customer and orders.
Columns
Each column of a table contains one attribute, which is one characteristic, feature,
or fact that describes the subject of the table. The stock table has columns for the
following facts about items of merchandise: stock numbers, manufacturer codes,
descriptions, prices, and units of measure.
Views
A view is a virtual table based on a specified SELECT statement. A view is a
dynamically controlled picture of the contents in a database and allows a
programmer to determine what information the user sees and manipulates.
Different users can be given different views of the contents of a database, and their
access to those contents can be restricted in several ways.
Sequences
A sequence is a database object that generates a sequence of whole numbers within
a defined range. The sequence of numbers can run in either ascending or
descending order, and is monotonic. For more information about sequences, see the
IBM Informix Guide to SQL: Syntax.
Operations on tables
Because a database is really a collection of tables, database operations are
operations on tables. The object-relational model supports three fundamental
operations: selection, projection, and joining. The following figure shows the
selection and projection operations. (All three operations are defined in detail, with
many examples, in the following topics.)
stock table
P R O J E C T I O N
When you select data from a table, you are choosing certain rows and ignoring
others. For example, you can query the stock table by asking the database
management system to, “Select all rows in which the manufacturer code is HSK
and the unit price is between 200.00 and 300.00.”
A table contains information about only one entity; when you want information
about multiple entities, you must join their tables. You can join tables in many
ways. For more information about join operations, refer to Chapter 5, “Compose
advanced SELECT statements,” on page 5-1.
You will use most of the statements only when you set up or tune your database.
You will use three or four statements regularly to query or update your database.
For details on SQL statements, see the IBM Informix Guide to SQL: Syntax.
One statement, SELECT, is in almost constant use. SELECT is the only statement
that you can use to retrieve data from the database. It is also the most complicated
statement, and the next two chapters of this book explore its many uses.
Standard SQL
The relational model and SQL and were invented and developed at IBM in the
early and middle 1970s. Once IBM proved that it was possible to implement
practical relational databases and that SQL was a usable language for manipulating
them, other implementations of SQL were developed.
Interactive SQL
To carry out the examples in this book and to experiment with SQL and database
design, you need a program that lets you execute SQL statements interactively.
DB-Access is such a program. It helps you compose SQL statements and then
passes your SQL statements to the database server for execution and displays the
results to you.
General programming
You can write programs that incorporate SQL statements and exchange data with
the database server. That is, you can write a program to retrieve data from the
database and format it however you choose. You can also write programs that take
data from any source in any format, prepare it, and insert it into the database.
You can also write programs called stored routines to work with database data and
objects. The stored routines that you write are stored directly in a database in
tables. You can then execute a stored routine from DB-Access or an SQL
Application Programming Interface (API) such as IBM Informix ESQL/C.
Chapter 8, “SQL programming,” on page 8-1, and Chapter 9, “Modify data through
SQL programs,” on page 9-1, present an overview of how SQL is used in
programs.
ANSI-compliant databases
Use the MODE ANSI keywords when you create a database to designate it as
ANSI compliant. Within such a database, certain characteristics of the ANSI/ISO
standard apply. For example, all actions that modify data take place within a
transaction automatically, which means that the changes are made in their entirety
or not at all. Differences in the behavior of ANSI-compliant databases are noted,
where appropriate, in the statement descriptions in the IBM Informix Guide to SQL:
Syntax. For a detailed discussion of ANSI-compliant databases, see the IBM
Informix Database Design and Implementation Guide.
Summary
A database contains a collection of related information but differs in a fundamental
way from other methods of storing data. The database contains not only the data,
but also a data model that defines each data item and specifies its meaning with
respect to the other items and to the real world.
More than one user can access and modify a database at the same time. Each user
has a different view of the contents of a database, and each user's access to those
contents can be restricted in several ways.
To manipulate and query a database, use SQL. IBM pioneered SQL and ANSI
standardized it. IBM Informix extensions that you can use to your advantage add
to the ANSI-defined language. IBM Informix tools also make it possible to
maintain strict compliance with ANSI standards.
Two layers of software mediate all your work with databases. The bottom layer is
always a database server that executes SQL statements and manages the data on
disk and in computer memory. The top layer is one of many applications, some
from IBM and some written by you, by other vendors, or your colleagues.
Middleware is the component that links the database server to the application, and
is provided by the database vendor to bind the client programs with the database
server. IBM Informix Stored Procedure Language (SPL) is an example of such a
tool.
This chapter introduces the basic methods for using the SELECT statement to
query and retrieve data from relational databases. It discusses how to tailor your
statements to select columns or rows of information from one or more tables, how
to include expressions and functions in SELECT statements, and how to create
various join conditions between database tables. The syntax and usage for the
SELECT statement are described in detail in the IBM Informix Guide to SQL: Syntax.
Most examples in this publication come from the tables in the stores_demo
database, which is included with the software for your IBM Informix SQL API or
database utility. In the interest of brevity, the examples show only part of the data
that is retrieved for each SELECT statement. For information on the structure and
contents of the demonstration database, see the IBM Informix Guide to SQL:
Reference. For emphasis, keywords are shown in uppercase letters in the examples,
although SQL is not case sensitive.
This chapter shows how to use five SELECT statement clauses. If you include all
five of these clauses, they must appear in the SELECT statement in the following
order:
1. Projection clause
2. FROM clause
3. WHERE clause
4. ORDER BY clause
5. INTO TEMP clause
Only the Projection clause and FROM clause are required. These two clauses form
the basis for every database query, because they specify the column values to be
retrieved, and the tables that contain those columns. Use one or more of the other
clauses from the following list:
v Add a WHERE clause to select specific rows or create a join condition.
v Add an ORDER BY clause to change the order in which data is produced.
Two additional SELECT statement clauses, GROUP BY and HAVING, let you
perform more complex data retrieval. They are introduced in Chapter 5, “Compose
advanced SELECT statements,” on page 5-1. Another clause, INTO, specifies the
program or host variable to receive data from a SELECT statement in an
application program. Complete syntax and rules for using the SELECT statement
are in the IBM Informix Guide to SQL: Syntax.
For more Global Language Support (GLS) information, see the IBM Informix GLS
User's Guide. For additional information on using NCHAR and NVARCHAR data
types with non-default code sets, see the IBM Informix Database Design and
Implementation Guide and the IBM Informix Guide to SQL: Reference.
Privileges
Before you make a query against data, make sure you have the Connect privilege
on the database and the Select privilege on the table. These privileges are normally
granted to all users. Database access privileges are discussed in the IBM Informix
Database Design and Implementation Guide and in the GRANT and REVOKE
statements in the IBM Informix Guide to SQL: Syntax.
Relational operations
A relational operation involves manipulating one or more tables, or relations, to result
in another table. The three kinds of relational operations are selection, projection,
and join. This chapter includes examples of selection, projection, and simple
joining.
The result contains the same number of columns as the customer table, but only a
subset of its rows. In this example, DB-Access displays the data from each column
on a separate line.
customer_num 119
fname Bob
lname Shorter
company The Triathletes Club
address1 2405 Kings Highway
address2
city Cherry Hill
state NJ
zipcode 08002
phone 609-663-6079
customer_num 122
fname Cathy
lname O'Brian
company The Sporting Life
address1 543d Nassau
address2
city Princeton
state NJ
zipcode 08540
phone 609-342-0054
The result contains the same number of rows as the customer table, but it projects
only a subset of the columns in the table. Because only a small amount of data is
selected from each row, DB-Access is able to display all of the data from the row
on one line.
Sunnyvale CA 94086
San Francisco CA 94117
Palo Alto CA 94303
Redwood City CA 94026
Los Altos CA 94022
Mountain View CA 94063
Palo Alto CA 94304
Redwood City CA 94063
Sunnyvale CA 94086
Redwood City CA 94062
Sunnyvale
. CA 94085
.
.
Oakland CA 94609
Cherry Hill NJ 08002
Phoenix AZ 85016
Wilmington DE 19898
Princeton NJ 08540
Jacksonville FL 32256
Bartlesville OK 74006
The most common kind of SELECT statement uses both selection and projection. A
query of this kind returns some of the rows and some of the columns in a table, as
the following figure shows.
Figure 2-6 on page 2-5 contains a subset of the rows and a subset of the columns in
the customer table.
Join
A join occurs when two or more tables are connected by one or more columns in
common, which creates a new table of results. The following figure shows a query
that uses a subset of the items and stock tables to illustrate the concept of a join.
The result consists of specified rows and columns from both the customer and
state tables.
The most basic SELECT statement contains only the two required clauses, the
Projection clause and FROM.
The following query uses the wildcard asterisk symbol (*) as shorthand in the
projection list to represent the names of all the columns in the table. You can use
the asterisk symbol (*) when you want all the columns in their defined order. An
implicit select list uses the asterisk symbol.
Because the manufact table has only three columns, Figure 2-10 on page 2-6 and
Figure 2-11 are equivalent and display the same results; that is, a list of every
column and row in the manufact table. The following figure shows the results.
SMT Smith 3
ANZ Anza 5
NRG Norge 7
HSK Husky 5
HRO Hero 4
SHM Shimara 30
KAR Karsten 21
NKL Nikolus 8
PRC ProCycle 9
The query result includes the same columns as the previous query result, but
because the columns are specified in a different order, the display is also different.
Smith SMT 3
Anza ANZ 5
Norge NRG 7
Husky HSK 5
Hero HRO 4
Shimara SHM 30
Karsten KAR 21
Nikolus NKL 8
ProCycle PRC 9
You can add an ORDER BY clause to your SELECT statement to direct the system
to sort the data in a specific order. The ORDER BY clause is a list of column names
from any remote or local table or view. Any expressions that are allowed in the
projection list are allowed in the ORDER BY list. If a column used in the ORDER
BY list has a Select trigger on it, the trigger will not be activated.
For IBM Informix, you do not need to include the columns that you want to use in
the ORDER BY clause in the projection list. That is, you can sort the data according
to a column that is not retrieved in the projection list. The following query returns
every row from the manu_code and manu_name columns in the manufact table,
sorted according to lead_time. The lead_time column is in the ORDER BY clause
although it is not included in the projection list.
Ascending order
The retrieved data is sorted and displayed, by default, in ascending order. In the
ASCII character set, ascending order is uppercase A to lowercase z for character
data types, and lowest to highest value for numeric data types. DATE and
DATETIME data is sorted from earliest to latest, and INTERVAL data is ordered
from shortest to longest span of time.
Descending order
Descending order is the opposite of ascending order, from lowercase z to uppercase
A for character types, and from highest to lowest for numeric data types. DATE
and DATETIME data is sorted from latest to earliest, and INTERVAL data is
ordered from longest to shortest span of time. The following query shows an
example of descending order.
The keyword DESC following a column name causes the retrieved data to be
sorted in descending order, as the result shows.
SHM Shimara 30
KAR Karsten 21
PRC ProCycle 9
NKL Nikolus 8
NRG Norge 7
HSK Husky 5
ANZ Anza 5
HRO Hero 4
SMT Smith 3
The following query and Figure 2-21 and the corresponding query results show
nested sorts. To modify the order in which selected data is displayed, change the
order of the two columns that are named in the ORDER BY clause.
In the query result, the manu_code column data appears in alphabetical order and,
within each set of rows with the same manu_code (for example, ANZ, HRO), the
unit_price is listed in ascending order.
The following query shows the reverse order of the columns in the ORDER BY
clause.
In the query result, the data appears in ascending order of unit_price and, where
two or more rows have the same unit_price (for example, $20.00, $48.00, $312.00),
the manu_code is in alphabetical order.
The order of the columns in the ORDER BY clause is important, and so is the
position of the DESC keyword. Although the statements in the following query
contain the same components in the ORDER BY clause, each produces a different
result (not shown).
If you want to find all the customer numbers in the orders table, use a statement
such as the one in the following query.
The result shows how the statement simply selects all data in the customer_num
column in the orders table and lists the customer numbers on all the orders,
including duplicates.
104
101
104
.
.
.
122
123
124
126
127
The output includes several duplicates because some customers have placed more
than one order. Sometimes you want to see duplicate rows in a projection. At other
times, you want to see only the distinct values, not how often each value appears.
To suppress duplicate rows, you can include the keyword DISTINCT or its
synonym UNIQUE at the start of the select list, once in each level of a query, as
the following query shows.
To produce a more readable list, Figure 2-26 limits the display to show each
customer number in the orders table only once, as the result shows.
customer_num
101
104
106
110
111
112
115
116
117
119
120
121
122
123
124
126
127
Suppose you are handling a customer call, and you want to locate purchase order
number DM354331. To list all the purchase order numbers in the orders table, use
a statement such as the following query shows.
The result shows how the statement retrieves data in the po_num column in the
orders table.
po_num
B77836
9270
B77890
8006
2865
Q13557
278693
.
.
.
However, the list is not in a useful order. You can add an ORDER BY clause to sort
the column data in ascending order and make it easier to find that particular
po_num, as shown in the following query.
po_num
278693
278701
2865
429Q
4745
8006
8052
9270
B77836
B77890
.
.
.
To select multiple columns from a table, list them in the projection list in the
Projection clause. The following query shows that the order in which the columns
are selected is the order in which they are retrieved, from left to right.
As “Sorting on multiple columns” on page 2-9 shows, you can use the ORDER BY
clause to sort the data in ascending or descending order and perform nested sorts.
The result shows ascending order.
When you use SELECT and ORDER BY on several columns in a table, you might
find it helpful to use integers to refer to the position of the columns in the ORDER
BY clause. When an integer is an element in the ORDER BY list, the database
server treats it as the position in the projection list. For example, using 3 in the
ORDER BY list (ORDER BY 3) refers to the third item in the projection list. The
statements in the following query retrieve and display the same data, as
Figure 2-35 on page 2-14 shows.
You can include the DESC keyword in the ORDER BY clause when you assign
integers to column names, as the following query shows.
Select substrings
To select part of the value of a character column, include a substring in the
projection list. Suppose your marketing department is planning a mailing to your
customers and wants their geographical distribution based on zip codes. You could
write a query similar to the following.
The query uses a substring to select the first three characters of the zipcode
column (which identify the state) and the full customer_num, and lists them in
ascending order by zip code, as the result shows.
021 125
080 119
085 122
198 121
322
. 123
.
.
943 103
943 107
946 118
If your database contains non-English data, you should store non-English data in
NCHAR (or NVARCHAR) columns to obtain results sorted by the language. The
ORDER BY clause should return data in the order appropriate to that language.
The following query uses a SELECT statement with an ORDER BY clause to search
the table, abonnés, and to order the selected information by the data in the nom
column.
SELECT numéro,nom,prénom
FROM abonnés
ORDER BY nom;
The collation order for the results of this query can vary, depending on the
following system variations:
v Whether the nom column is CHAR or NCHAR data type. The database server
sorts data in CHAR columns by the order the characters appear in the code set.
The database server sorts data in NCHAR columns by the order the characters
are listed in the collation portion of the locale.
v Whether the database server is using the correct non-English locale when it
accesses the database. To use a non-English locale, you must set the
CLIENT_LOCALE and DB_LOCALE environment variables to the appropriate
locale name.
For the query to return expected results, the nom column should be NCHAR data
type in a database that uses a French locale. Other operations, such as less than,
greater than, or equal to, are also affected by the user-specified locale. For more
information on non-English data and locales, see the IBM Informix GLS User's
Guide.
The following result and Figure 2-41 on page 2-16 show two sample sets of output.
The following query result follows the ISO 8859-1 code-set order, which ranks
uppercase letters before lowercase letters and moves names that contain an
accented character (Ålesund, Étaix, Ötker, and Øverst) to the end of the list.
The result shows that when the appropriate locale file is referenced by the
database server, names including non-English characters (Ålesund, Étaix, Ötker,
and Øverst) are collated differently than they are in the ISO 8859-1 code set. They
are sorted correctly for the locale. It does not distinguish between uppercase and
lowercase letters.
You can use the WHERE clause to set up a comparison condition or a join
condition. This section demonstrates only the first use. Join conditions are
described in a later section and in the next chapter.
For example, you might use one of the keywords BETWEEN, IN, LIKE, or
MATCHES to test for equality, or the keywords IS NULL to test for null values.
You can combine the keyword NOT with any of these keywords to specify the
opposite condition.
The following table lists the relational operators that you can use in a WHERE
clause in place of a keyword to test for equality.
Operator
Operation
= equals
!= or <>
does not equal
> greater than
>= greater than or equal to
< less than
<= less than or equal to
For CHAR expressions, greater than means after in ASCII collating order, where
lowercase letters are after uppercase letters, and both are after numerals. See the
ASCII Character Set chart in the IBM Informix Guide to SQL: Syntax. For DATE and
DATETIME expressions, greater than means later in time, and for INTERVAL
expressions, it means of longer duration.
You cannot use TEXT or BYTE columns to create a comparison condition, except
when you use the IS NULL or IS NOT NULL keywords to test for NULL values.
You can use the preceding keywords and operators in a WHERE clause to create
comparison-condition queries that perform the following actions:
v Include values
v Exclude values
v Find a range of values
v Find a subset of values
v Identify NULL values
The following section contains examples that illustrate these types of queries.
Include rows
Use the equal sign (=) relational operator to include rows in a WHERE clause, as
the following query shows.
Exclude rows
Use the relational operators != or <> to exclude rows in a WHERE clause.
The following query assumes that you are selecting from an ANSI-compliant
database; the statements specify the owner or login name of the creator of the
customer table. This qualifier is not required when the creator of the table is the
current user, or when the database is not ANSI compliant. However, you can
include the qualifier in either case. For a detailed discussion of owner naming, see
the IBM Informix Guide to SQL: Syntax.
Both statements in the query exclude values by specifying that, in the customer
table that the user odin owns, the value in the state column should not be equal to
CA, as the result shows.
Each statement in the query specifies a range for catalog_num from 10005 through
10008, inclusive. The first statement uses keywords, and the second statement uses
relational operators to retrieve the rows, as the result shows.
catalog_num 10005
stock_num 3
manu_code HSK
cat_advert High-Technology Design Expands the Sweet Spot
catalog_num 10006
stock_num 3
manu_code SHM
cat_advert Durable Aluminum for High School and Collegiate Athletes
catalog_num 10007
stock_num 4
manu_code HSK
cat_advert Quality Pigskin with Joe Namath Signature
catalog_num 10008
stock_num 4
manu_code HRO
cat_advert Highest Quality Football for High School
and Collegiate Competitions
Each statement in the query retrieves rows that include the subset of AZ or NJ in
the state column of the Aleta.customer table.
Also, when you use IBM Informix, you cannot test BLOB or CLOB columns with
the IN keyword.
The previous query adds the keywords NOT IN, so the subset changes to exclude
the subsets AZ and NJ in the state column. The following figure shows the results
in order of the state column.
The following query returns all rows that have a null paid_date, as the result
shows.
In the following query, the operator AND combines two comparison expressions in
the WHERE clause.
The query returns all rows that have NULL paid_date or a NOT NULL ship_date.
Exact-text comparisons
The following examples include a WHERE clause that searches for exact-text
comparisons by using the keyword LIKE or MATCHES or the equal sign (=)
relational operator. Unlike earlier examples, these examples illustrate how to query
a table that is not in the current database. You can access a table that is not in the
current database only if the database that contains the table has the same ANSI
compliance status as the current database. If the current database is an
ANSI-compliant database, the table you want to access must also reside in an
ANSI-compliant database. If the current database is not an ANSI-compliant
database, the table you want to access must also reside in a database that is not an
ANSI-compliant database.
Each statement in the following query retrieves all the rows that have the single
word helmet in the description column, as the result shows.
Variable-text searches
You can use the keywords LIKE and MATCHES for variable-text queries that are
based on substring searches of fields. Include the keyword NOT to indicate the
opposite condition. The keyword LIKE complies with the ISO/ANSI standard for
SQL, whereas MATCHES is an IBM Informix extension.
Variable-text search strings can include the wildcards listed with LIKE or
MATCHES in the following table.
You cannot test BLOB, CLOB, TEXT, or BYTE columns with the LIKE or
MATCHES operators.
A single-character wildcard
The statements in the following query illustrate the use of a single-character
wildcard in a WHERE clause. Further, they demonstrate a query on a table that is
For more information, see Chapter 7, “Access and modify data in an external
database,” on page 7-1 and the IBM Informix Guide to SQL: Syntax.
Each statement in the query retrieves only those rows for which the middle letter
of the manu_code is R, as the result shows. The comparison '_R_' (for LIKE) or '?R?'
(for MATCHES) specifies, from left to right, the following items:
v Any single character
v The letter R
v Any single character
The following query selects only those rows where the manu_code begins with A
through H and returns the rows that the result shows. The test '[A-H]' specifies any
single letter from A through H, inclusive. No equivalent wildcard symbol exists for
the LIKE keyword.
The statements in the following query use a wildcard at the end of a string to
retrieve all the rows where the description begins with the characters bicycle.
The following query narrows the search by adding another comparison condition
that excludes a manu_code of PRC.
When you select from a large table and use an initial wildcard in the comparison
string (such as '%cycle'), the query often takes longer to execute. Because indexes
cannot be used, every row is searched.
With IBM Informix, you can use the SET COLLATION statement to specify a
database locale for your session that is different from the DB_LOCALE setting. See
the IBM Informix Guide to SQL: Syntax for a description of SET COLLATION.
SELECT numéro,nom,prénom
FROM abonnés
WHERE nom MATCHES ’[E-P]*’
ORDER BY nom;
In the result, the rows for Étaix, Ötker, and Øverst are not selected and listed
because, with ISO 8859-1 code-set order, the accented first letter of each name is
not in the E through P MATCHES range for the nom column.
For more information on non-English data and locales, see the IBM Informix GLS
User's Guide.
The subscript [1,4] causes the query to retrieve all rows in which the first four
letters of the cat_advert column are High, as result shows.
catalog_num 10005
stock_num 3
manu_code HSK
cat_advert High-Technology Design Expands the Sweet Spot
cat_descr
Pro-style
. wood. Available in sizes: 31, 32, 33, 34, 35.
.
.
catalog_num 10045
stock_num 204
manu_code KAR
cat_advert High-Quality Beginning Set of Irons. Appropriate
for High School Competitions
cat_descr
Ideally balanced for optimum control. Nylon covered shaft.
catalog_num 10068
stock_num 310
manu_code ANZ
cat_advert High-Quality Kickboard
cat_descr
White. Standard size.
The keyword SKIP, followed by an unsigned integer, can precede the FIRST or
LIMIT keyword in the Projection clause. The SKIP offset clause instructs the
database server to exclude the first offset qualifying rows from the result set of the
query before returning the number of rows that the FIRST clause specifies. In SPL
routines, the parameter of SKIP, FIRST, or LIMIT can be a literal integer or a local
SPL variable. If the Projection clause includes SKIP offset but no FIRST or LIMIT
specification, then the query returns all of the qualifying rows except for the first
offset rows.
The Projection clause cannot include the SKIP, FIRST, or LIMIT keywords in these
contexts:
v when the SELECT statement is part of a view definition
v in a subquery, except in the FROM clause of the outer query
For information about restrictions on use of the FIRST clause, see the description of
the Projection clause of the SELECT statement in the IBM Informix Guide to SQL:
Syntax.
The following query uses the FIRST clause to return the first five rows from the
state table.
code sname
AK Alaska
HI Hawaii
CA California
OR Oregon
WA Washington
You can use a FIRST clause when you simply want to know the names of all the
columns and the type of data that a table contains, or to test a query that
otherwise would return many rows. The following query shows how to use the
FIRST clause to return column values for the first row of a table.
order_num 1001
order_date 05/20/1998
customer_num 104
ship_instruct express
backlog n
po_num B77836
ship_date 06/01/1998
ship_weight 20.40
ship_charge $10.00
paid_date 07/22/1998
code sname
AL Alabama
AK Alaska
AZ Arizona
AR Arkansas
CA California
The following query shows how to use a FIRST clause in a query with an ORDER
BY clause to find the 10 most expensive items listed in the stock table.
description unit_price
football $960.00
volleyball $840.00
baseball gloves $800.00
18-spd, assmbld $685.90
irons/wedge $670.00
basketball $600.00
12-spd, assmbld $549.00
10-spd, assmbld $499.99
football $480.00
bicycle brakes $480.00
Applications can use the SKIP and FIRST keywords of the Projection clause, in
conjunction with the ORDER BY clause, to perform successive queries that
incrementally retrieve all of the qualifying rows in subsets of some fixed size (for
example, the maximum number of rows that are visible without scrolling a screen
display). You can accomplish this by incrementing the offset parameter of the SKIP
clause by the max parameter of the FIRST clause after each query. By imposing a
unique order on the qualifying rows, the ORDER BY clause ensures that each
query returns a disjunct subset of the qualifying rows.
The following query shows a query that includes SKIP, FIRST, and ORDER BY
specifications to return (by alphabetical order) the sixth through tenth states in the
code sname
CO Colorado
CT Connecticut
DE Delaware
FL Florida
GA Georgia
If you use the SKIP, FIRST, or LIMIT keywords, you must take care to specify
parameters that correspond to the design goals of your application. If the offset
parameter of skip is larger than the number of qualifying rows, then any FIRST or
LIMIT specification has no effect, and the query returns nothing.
Arithmetic expressions
An arithmetic expression contains at least one of the arithmetic operators listed in
the following table and produces a number.
Operator
Operation
+ addition
- subtraction
* multiplication
/ division
With IBM Informix, you cannot specify BLOB or CLOB in arithmetic expressions.
The following query calculates a surcharge of $6.50 on orders when the quantity
ordered is less than 5.
Display labels:
You can assign a display label to a computed or derived data column to replace the
default column header expression. In Figure 2-84 on page 2-34, Figure 2-86 on page
2-34, and Figure 2-90, the derived data appears in the expression column. The
following query also presents derived values, but the column that displays the
derived values has the descriptive header taxed.
The result shows that the label taxed is assigned to the expression in the projection
list that displays the results of the operation unit_price * 1.07.
The following query assigns the label span to the column that displays the results
of subtracting the DATETIME column call_dtime from the DATETIME column
res_dtime.
CASE expressions
A CASE expression is a conditional expression, which is similar to the concept of
the CASE statement in programming languages. You can use a CASE expression
when you want to change the way data is represented. The CASE expression
Consider a column that represents marital status numerically as 1,2,3,4 with the
corresponding values meaning single, married, divorced, widowed. In some cases,
you might prefer to store the short values (1,2,3,4) for database efficiency, but
employees in human resources might prefer the more descriptive values (single,
married, divorced, widowed). The CASE expression makes such conversions
between different sets of values easy.
In IBM Informix, the CASE expression also supports extended data types and cast
expressions.
The following example shows a CASE expression with multiple WHEN clauses
that returns more descriptive values for the manu_code column of the stock table.
If none of the WHEN conditions is true, NULL is the default result. (You can omit
the ELSE NULL clause.)
SELECT
CASE
WHEN manu_code = "HRO" THEN "Hero"
WHEN manu_code = "SHM" THEN "Shimara"
WHEN manu_code = "PRC" THEN "ProCycle"
WHEN manu_code = "ANZ" THEN "Anza"
ELSE NULL
END
FROM stock;
You must include at least one WHEN clause within the CASE expression;
subsequent WHEN clauses and the ELSE clause are optional. If no WHEN
condition evaluates to true, the resulting value is NULL. You can use the IS NULL
expression to handle NULL results. For information on handling NULL values, see
the IBM Informix Guide to SQL: Syntax.
The following query shows a simple CASE expression that returns a character
string value to flag any orders from the orders table that have not been shipped to
the customer.
1001 05/20/1998
1002 05/21/1998
1003 05/22/1998
1004 05/22/1998
1005 05/24/1998
1006 05/30/1998 order not shipped
1007 05/31/1998
.
.
.
1019 07/11/1998
1020 07/11/1998
1021 07/23/1998
1022 07/24/1998
1023 07/24/1998
For information about how to use the CASE expression to update a column, see
“CASE expression to update a column” on page 6-19.
The query retrieves the same data from the cust_calls table as Figure 2-94 on page
2-36. In the query, the ORDER BY clause causes the data to be displayed in
ascending order of the derived values in the span column, as the result shows.
The following query uses an integer to represent the result of the operation
res_dtime - call_dtime and retrieves the same rows that appear in the above
result.
For more information about rowids, see the IBM Informix Database Design and
Implementation Guide and your IBM Informix Administrator's Guide.
The following query uses the rowid and the wildcard asterisk symbol (*) in the
Projection clause to retrieve each row in the manufact table and its corresponding
rowid.
A simple join combines information from two or more tables based on the
relationship between one column in each table. A composite join is a join between
two or more tables based on the relationship between two or more columns in
each table.
To create a join, you must specify a relationship, called a join condition, between at
least one column from each table. Because the columns are being compared, they
must have compatible data types. When you join large tables, performance
improves when you index the columns in the join condition.
Data types are described in the IBM Informix Guide to SQL: Reference and the IBM
Informix Database Design and Implementation Guide. Indexing is discussed in detail in
the IBM Informix Administrator's Guide.
The following query selects from two tables and produces a Cartesian product.
Although only 52 rows exist in the state table and 28 rows in the customer table,
the effect of the query is to multiply the rows of one table by the rows of the other
and retrieve an impractical 1,456 rows, as the result shows.
customer_num 101
fname Ludwig
lname Pauli
company All Sports Supplies
address1 213 Erstwild Court
address2
city Sunnyvale
state CA
zipcode 94086
phone 408-789-8075
code HI
sname Hawaii
customer_num 101
fname Ludwig
lname Pauli
company All Sports Supplies
address1 213 Erstwild Court
address2
city Sunnyvale
state CA
zipcode 94086
phone 408-789-8075
code CA
sname
. California
.
.
Create a join
Conceptually, the first stage of any join is the creation of a Cartesian product. To
refine or constrain this Cartesian product and eliminate meaningless combinations
of rows of data, include a WHERE clause with a valid join condition in your
SELECT statement.
This section illustrates cross joins, equi-joins, natural joins, and multiple-table joins.
Additional complex forms, such as self-joins and outer joins, are discussed in
Chapter 5, “Compose advanced SELECT statements,” on page 5-1.
The following query uses ANSI join syntax to create a cross join.
The results of the query are identical to the results of Figure 2-103 on page 2-40. In
addition, you can filter a cross join by specifying a WHERE clause.
For more information about Cartesian products, see “Create a Cartesian product”
on page 2-40. For more information about ANSI syntax, see “ANSI join syntax” on
page 5-11.
Equi-join
An equi-join is a join based on equality or matching column values. This equality is
indicated with an equal sign (=) as the comparison operator in the WHERE clause,
as the following query shows.
The query joins the manufact and stock tables on the manu_code column. It
retrieves only those rows for which the values of the two columns are equal, some
of which the result shows.
manu_code SMT
manu_name Smith
lead_time 3
stock_num 5
manu_code SMT
description tennis racquet
unit_price $25.00
unit each
unit_descr each
manu_code SMT
manu_name Smith
lead_time 3
stock_num 6
manu_code SMT
description tennis ball
unit_price $36.00
unit case
unit_descr 24 cans/case
manu_code ANZ
manu_name Anza
lead_time 5
stock_num 5
manu_code ANZ
description tennis racquet
unit_price $19.80
unit each
unit_descr
. each
.
.
In this equi-join, the result includes the manu_code column from both the
manufact and stock tables because the select list requested every column.
You can also create an equi-join with additional constraints, where the comparison
condition is based on the inequality of values in the joined columns. These joins
use a relational operator in addition to the equal sign (=) in the comparison
condition that is specified in the WHERE clause.
To join tables that contain columns with the same name, qualify each column name
with the name of its table and a period symbol (.), as the following query shows.
order_num 1004
order_date 05/22/1998
ship_date 05/30/1998
customer_num 106
call_dtime 1998-06-12 08:20
user_id maryj
call_code D
call_descr Order received okay, but two of the cans of
ANZ tennis balls within the case were empty
res_dtime 1998-06-12 08:25
res_descr Authorized credit for two cans to customer,
issued apology. Called ANZ buyer to report
the qa problem.
order_num 1008
order_date 06/07/1998
ship_date 07/06/1998
customer_num 110
call_dtime 1998-07-07 10:24
user_id richc
call_code L
call_descr Order placed one month ago (6/7) not received.
res_dtime 1998-07-07 10:30
res_descr Checked with shipping (Ed Smith). Order out
yesterday-was waiting for goods from ANZ.
Next time will call with delay if necessary.
order_num 1023
order_date 07/24/1998
ship_date 07/30/1998
customer_num 127
call_dtime 1998-07-31 14:30
user_id maryj
call_code I
call_descr Received Hero watches (item # 304) instead
of ANZ watches
res_dtime
res_descr Sent memo to shipping to send ANZ item 304
to customer and pickup HRO watches. Should
be done tomorrow, 8/1
Natural join
A natural join is a type of equi-join and is structured so that the join column does
not display data redundantly, as the following query shows.
Like the example for equi-join, the query joins the manufact and stock tables on
the manu_code column. Because the Projection list is more closely defined, the
manu_code is listed only once for each row retrieved, as the result shows.
manu_name Smith
lead_time 3
stock_num 5
manu_code SMT
description tennis racquet
unit_price $25.00
unit each
unit_descr each
manu_name Smith
lead_time 3
stock_num 6
manu_code SMT
description tennis ball
unit_price $36.00
unit case
unit_descr 24 cans/case
manu_name Anza
lead_time 5
stock_num 5
manu_code ANZ
description tennis racquet
unit_price $19.80
unit each
unit_descr
. each
.
.
All joins are associative; that is, the order of the joining terms in the WHERE clause
does not affect the meaning of the join.
Both statements in the following query create the same natural join.
Figure 2-112 on page 2-45 includes a TEXT column, cat_descr; a BYTE column,
cat_picture; and a VARCHAR column, cat_advert.
Multiple-table join
A multiple-table join connects more than two tables on one or more associated
columns; it can be an equi-join or a natural join.
The following query creates an equi-join on the catalog, stock, and manufact
tables.
catalog_num 10025
stock_num 106
manu_code PRC
cat_descr
Hard anodized alloy with pearl finish; 6mm hex bolt hard ware.
Available in lengths of 90-140mm in 10mm increments.
cat_picture <BYTE value>
The query uses a wildcard to select all columns from the table with the most
columns and then specifies columns from the other two tables. The result shows
the natural join that the query produces. It displays the same information as the
previous example, but without duplication.
catalog_num 10025
stock_num 106
manu_code PRC
cat_descr
Hard anodized alloy with pearl finish. 6mm hex bolt
hardware. Available in lengths of 90-140mm in 10mm increments.
cat_picture <BYTE value>
Aliases
You can assign aliases to the tables in the FROM clause of a SELECT statement to
make multiple-table queries shorter and more readable. You can use an alias
wherever the table name would be used, for instance, as a prefix to the column
names in the other clauses.
The associative nature of the SELECT statement allows you to use an alias before
you define it. In the query above, the aliases s for the stock table, c for the catalog
table, and m for the manufact table are specified in the FROM clause and used
throughout the SELECT and WHERE clauses as column prefixes.
Compare the length of Figure 2-118 with the following query, which does not use
aliases.
Figure 2-118 and Figure 2-119 are equivalent and retrieve the data that the
following query shows.
stock_num 110
manu_code HRO
description helmet
unit_price $260.00
catalog_num 10033
cat_advert Lightweight Plastic with Vents Assures Cool
Comfort Without Sacrificing Protection
lead_time 4
stock_num 110
manu_code HSK
description helmet
unit_price $308.00
catalog_num 10034
cat_advert Teardrop Design Used by Yellow Jerseys; You
Can Time the Difference
lead_time
. 5
.
.
You cannot use the ORDER BY clause for the TEXT column cat_descr or the BYTE
column cat_picture.
The following query joins columns from two tables that reside in different
databases and systems, neither of which is the current database or system.
For more information on how to access tables that are not in the current database,
see “Access other database servers” on page 7-1 and the IBM Informix Guide to
SQL: Syntax.
You can also use synonyms as shorthand references to the long names of tables
that are not in the current database as well as current tables and views. For details
on how to create and use synonyms, see the IBM Informix Database Design and
Implementation Guide.
The following query creates a temporary table called stockman and stores the
results of the query in it. Because all columns in a temporary table must have
names, the alias adj_price is required.
You can query this table and join it with other tables, which avoids a multiple sort
and lets you move more quickly through the database. For more information on
temporary tables, see the IBM Informix Guide to SQL: Syntax and the IBM Informix
Administrator's Guide.
Summary
This chapter presented syntax examples and results for basic kinds of SELECT
statements that are used to query a relational database. The section “Single-table
SELECT statements” on page 2-6 shows how to perform the following actions:
v Select columns and rows from a table with the Projection and FROM clauses
v Select rows from a table with the Projection, FROM, and WHERE clauses
v Use the DISTINCT or UNIQUE keyword in the Projection clause to eliminate
duplicate rows from query results
v Sort retrieved data with the ORDER BY clause and the DESC keyword
v Select and order data values that contain non-English characters
v Use the BETWEEN, IN, MATCHES, and LIKE keywords and various relational
operators in the WHERE clause to create comparison conditions
v Create comparison conditions that include values, exclude values, find a range
of values (with keywords, relational operators, and subscripting), and find a
subset of values
This chapter also introduced simple join conditions that enable you to select and
display data from two or more tables. The section “Multiple-table SELECT
statements” on page 2-40 describes how to perform the following actions:
v Create a Cartesian product
v Create a CROSS JOIN, which creates a Cartesian product
v Include a WHERE clause with a valid join condition in your query to constrain a
Cartesian product
v Define and create a natural join and an equi-join
v Join two or more tables on one or more columns
v Use aliases as a shortcut in multiple-table queries
v Retrieve selected data into a separate, temporary table with the INTO TEMP
clause to perform computations outside the database
ROW types have instances that combine one or more related data fields. The two
kinds of ROW types are named and unnamed.
Collection types have instances where each collection value contains a group of
elements of the same data type, which can be any fundamental or complex data
type. A collection can consist of a LIST, SET, or MULTISET datatype.
Important: There is no cross-database support for complex data types. They can
only be manipulated in local databases.
For a more complete description of the data types that the database server
supports, see the chapter on data types in the IBM Informix Guide to SQL: Reference.
For information about how to create and use complex types, see the IBM Informix
Database Design and Implementation Guide, IBM Informix Guide to SQL: Reference, and
IBM Informix Guide to SQL: Syntax.
The examples used throughout this section use the named ROW types zip_t,
address_t, and employee_t, which define the employee table. The following figure
shows the SQL syntax that creates the ROW types and table.
Figure 3-1. SQL syntax that creates the ROW types and table.
The named ROW types zip_t, address_t and employee_t serve as templates for the
fields and columns of the typed table, employee. A typed table is a table that is
defined on a named ROW type. The employee_t type that serves as the template
for the employee table uses the address_t type as the data type of the address
field. The address_t type uses the zip_t type as the data type of the zip field.
The following figure shows the SQL syntax that creates the student table. The
s_address column of the student table is defined on an unnamed ROW type. (The
s_address column could also have been defined as a named ROW type.)
The SELECT statement on the employee table returns all rows for all columns.
name Davis, J.
address ROW(133 First, San Jose, CA, 85744, 4900)
salary
. 75000
.
.
The following query shows how to construct a query that returns rows for the
name and address columns of the employee table.
name Paul, J.
address ROW(102 Ruby, Belmont, CA, 49932, 1000)
name Davis, J.
address
. ROW(133 First, San Jose, CA, 85744, 4900)
.
.
A query on a row-type column returns data from all the fields of the ROW type. A
field is a component data type within a ROW type. For example, the address
column of the employee table contains the street, city, state, and zip fields. The
following query shows how to construct a query that returns all fields of the
address column.
city state
Belmont CA
San Jose CA
Willits
. CA
.
.
You construct a query on an unnamed row-type column in the same way you
construct a query on a named row-type column. For example, suppose you want
to access data from the s_address column of the student table in Figure 3-2 on
page 3-2. You can use dot notation to query the individual fields of a column that
are defined on an unnamed row type. The following query shows how to construct
a SELECT statement on the student table that returns rows for the city and state
fields of the s_address column.
city state
Belmont CA
Mount Prospect IL
Greeley
. CO
.
.
Field projections
Do not confuse fields with columns. Columns are only associated with tables, and
column projections use conventional dot notation of the form name_1.name2 for a
table and column, respectively. A field is a component data type within a ROW
type. With ROW types (and the capability to assign a ROW type to a single
column), you can project individual fields of a column with single dot notation of
the form: name_a.name_b.name_c.name_d. IBM Informix database servers use the
following precedence rules to interpret dot notation:
1. table_name_a . column_name_b . field_name_c . field_name_d
2. column_name_a . field_name_b . field_name_c . field_name_d
When the meaning of a particular identifier is ambiguous, the database server uses
precedence rules to determine which database object the identifier specifies.
Consider the following two statements:
CREATE TABLE b (c ROW(d INTEGER, e CHAR(2)))
CREATE TABLE c (d INTEGER)
To avoid referencing the wrong database object, you can specify the full notation
for a field projection. Suppose, for example, you want to reference field d of
column c in table b (not column d of table c). The following statement specifies the
table, column, and field identifiers of the object you want to reference:
SELECT * FROM b,c WHERE b.c.d = 10
Important: Although precedence rules reduce the chance of the database server
misinterpreting field projections, it is recommended that you use unique names for
all table, column, and field identifiers.
For a discussion of how to use dot notation and asterisk notation with row-type
expressions, see the Expression segment in the IBM Informix Guide to SQL: Syntax.
Consider the address column of the employee table, which contains the fields
street, city, state, and zip. In addition, the zip field contains the nested fields:
z_code and z_suffix. (You might want to review the row type and table definitions
of Figure 3-1 on page 3-2.) A query on the zip field returns rows for the z_code
and z_suffix fields. However, you can specify that a query returns only specific
nested fields. The following query shows how to use dot notation to construct a
SELECT statement that returns rows for the z_code field of the address column
only.
z_code
39444
6500
76055
19004
.
.
.
The following query uses asterisk notation to return all fields of the address
column in the employee table.
The asterisk notation makes it easier to perform some SQL tasks. Suppose you
create a function new_row() that returns a row-type value and you want to call
this function and insert the row that is returned into a table. The database server
provides no easy way to handle such operations. However, the following query
shows how to use asterisk notation to return all fields of new_row() and insert the
returned fields into the tab_2 table.
For information about how to use the INSERT statement, see Chapter 6, “Modify
data,” on page 6-1.
The following figure shows the manager table, which is used in examples
throughout this section. The manager table contains both simple and nested
collection types. A simple collection is a collection type that does not contain any
fields that are themselves collection types. The direct_reports column of the
manager table is a simple collection. A nested collection is a collection type that
contains another collection type. The projects column of the manager table is a
nested collection.
A query on a column that is a collection type returns, for each row in the table, all
the elements that the particular collection contains. For example, the following
query shows a query that returns data in the department column and all elements
in the direct_reports column for each row of the manager table.
department marketing
direct_reports SET {Smith, Waters, Adams, Davis, Kurasawa}
department engineering
ddirect_reports SET {Joshi, Davis, Smith, Waters, Fosmire, Evans, Jones}
department publications
direct_reports SET {Walker, Fremont, Porat, Johnson}
department accounting
direct_reports
. SET {Baker, Freeman, Jacobs}
.
.
The output of a query on a collection type always includes the type constructor
that specifies whether the collection is a SET, MULTISET, or LIST. For example, in
the result, the SET constructor precedes the elements of each collection. Braces ({})
demarcate the elements of a collection; commas separate individual elements of a
collection.
SELECT projects
FROM manager
WHERE mgr_name = ’Sayles’
The query result shows a project column collection for a single row of the
manager table. The query returns the names of those projects that the manager
Sayles oversees. The collection contains, for each element in the LIST, the project
name (pro_name) and the SET of individuals (pro_members) who are assigned to
projects
. LIST {ROW(sapphire_project, SET{Villers, Reeves, Doyle, Strongin})}
.
.
mgr_name Sayles
department marketing
Although you can use a WHERE clause with the IN keyword to search for a
particular element in a simple collection, the query always returns the complete
collection. For example, the following query returns all the elements of the
collection where Adams is an element of a collection in the direct_reports column.
mgr_name Sayles
direct_reports SET {Smith, Waters, Adams, Davis, Kurasawa}
As the result shows, a query on a collection column returns the entire collection,
not a particular element within the collection.
You can use the IN keyword in a WHERE clause to reference a simple collection
only. You cannot use the IN keyword to reference a collection that contains fields
that are themselves collections. For example, you cannot use the IN keyword to
reference the projects column in the manager table because projects is a nested
collection.
mgr_name Williams
department engineering
mgr_name Lyman
department publications
mgr_name Cole
department accounting
For information about how to count the elements in a collection column, see
“Cardinality function” on page 4-13.
The following figure shows the statements that create the type and table
hierarchies that the examples in this section use.
Figure 3-29. Statements that create the type and table hierarchies.
The following figure shows the hierarchical relationships of the row types and
tables in the previous figure.
person_t person
employee_t employee
sales_rep_t sales_rep
Figure 2-31 on page 2-12 returns all columns in the supertable and those columns
in subtables (employee and sales_rep) that are inherited from the supertable. A
query on a supertable does not return columns from subtables that are not in the
supertable. The query result shows the name, address, and soc_sec columns in the
person, employee, and sales_rep tables.
name Rogers, J.
address ROW(102 Ruby Ave, Belmont, CA, 69055)
soc_sec 454849344
name Sallie, A.
address ROW(134 Rose St, San Carlos, CA, 69025)
soc_sec
. 348441214
.
.
name Rogers, J.
address ROW(102 Ruby Ave, Belmont, CA, 69055)
soc_sec
. 454849344
.
.
The section “Select from a collection” on page 3-6 shows how to perform the
following actions:
v Query columns that are defined on collection types
v Search for elements in a collection
v Query columns that are defined on nested collection types
The section “Select rows within a table hierarchy” on page 3-9 shows how to
perform the following actions:
v Query a supertable with or without the ONLY keyword
v Specify an alias for a supertable
For information about the syntax of the following SQL functions and other SQL
functions, see the Expressions segment in the IBM Informix Guide to SQL: Syntax.
Tip: You can also use functions that you create yourself. For information about
user-defined functions, see Chapter 11, “Create and use SPL routines,” on page
11-1, and IBM Informix User-Defined Routines and Data Types Developer's Guide.
A function expression uses a function that is evaluated for each row in the query.
All function expressions require arguments. This set of expressions contains the
time function and the length function when they are used with a column name as
an argument.
Aggregate functions
An aggregate function returns one value for a set of queried rows. The aggregate
functions take on values that depend on the set of rows that the WHERE clause of
the SELECT statement returns. In the absence of a WHERE clause, the aggregate
functions take on values that depend on all the rows that the FROM clause forms.
You cannot use aggregate functions for expressions that contain the following data
types:
v TEXT
v BYTE
v CLOB
v BLOB
v Collection data types (LIST, MULTISET, and SET)
v ROW types
v Opaque data types (except with user-defined aggregate functions that support
opaque types)
All IBM Informix database servers support the following aggregate functions.
(avg)
$197.14
The following query computes the average unit_price of just those rows in the
stock table that have a manu_code of SHM.
(avg)
$204.93
(count(*))
73
The following query includes a WHERE clause to count specific rows in the stock
table, in this case, only those rows that have a manu_code of SHM.
17
By including the keyword DISTINCT (or its synonym UNIQUE) and a column
name in the following query, you can tally the number of different manufacturer
codes in the stock table.
(count)
The query finds and displays both the highest and lowest ship_charge in the
orders table.
(max) (min)
$25.20 $5.00
You can apply the RANGE function only to numeric columns. The following query
finds the range of prices for items in the stock table.
955.50
As with other aggregates, the RANGE function applies to the rows of a group
when the query includes a GROUP BY clause, which the following query shows.
(range)
820.20
595.50
720.00
225.00
632.50
0.00
460.00
645.90
425.00
You can apply the STDEV function only to numeric columns. The following query
finds the standard deviation on a population:
SELECT STDEV(age) FROM u_pop WHERE age > 21;
As with the other aggregates, the STDEV function applies to the rows of a group
when the query includes a GROUP BY clause, as the following example shows:
SELECT STDEV(age) FROM u_pop
GROUP BY state
WHERE STDEV(age) > 21;
Nulls are ignored unless every value in the specified column is null. If every
column value is null, the STDEV function returns a null for that column. For more
information about the STDEV function, see the Expression segment in the IBM
Informix Guide to SQL: Syntax.
(sum)
130.5
In this example, Xi is each value in the column and N is the total number of values
in the column. You can apply the VARIANCE function only to numeric columns.
The following query finds the variance on a population:
SELECT VARIANCE(age) FROM u_pop WHERE age > 21;
As with the other aggregates, the VARIANCE function applies to the rows of a
group when the query includes a GROUP BY clause, which the following example
shows:
SELECT VARIANCE(age) FROM u_pop
GROUP BY birth
WHERE VARIANCE(age) > 21;
Nulls are ignored unless every value in the specified column is null. If every
column value is null, the VARIANCE function returns a null for that column. For
more information about the VARIANCE function, see the Expression segment in
the IBM Informix Guide to SQL: Syntax.
The query finds and displays the maximum, minimum, and average amounts of
time (in days, hours, and minutes) between the reception and resolution of a
customer call and labels the derived values appropriately. The query result shows
these qualities of time.
Time functions
You can use the time functions DAY, MONTH, WEEKDAY, and YEAR in either
the Projection clause or the WHERE clause of a query. These functions return a
value that corresponds to the expressions or arguments that you use to call the
function. You can also use the CURRENT or SYSDATE function to return a value
with the current date and time, or use the EXTEND function to adjust the
precision of a DATE or DATETIME value.
106 12 12
110 7 7
119 1 2
121 10 10
127 31
116 28 28
116 21 27
The following query uses the DAY and CURRENT functions to compare column
values to the current day of the month. It selects only those rows where the value
is earlier than the current day. In this example, the CURRENT day is 15.
The following query uses the CURRENT function to select all calls except those
that came in today.
customer_num 106
call_code D
call_descr Order was received, but two of the cans of ANZ tennis balls
within the case were empty
customer_num 110
call_code L
call_descr
. Order placed one month ago (6/7) not received.
.
.
customer_num 116
call_code I
call_descr Second complaint from this customer! Received two cases
right-handed outfielder gloves (1 HRO) instead of one case
lefties.
The SYSDATE function closely resembles the CURRENT function, but the default
precision of its returned value is DATETIME YEAR TO FRACTION(5), rather than
the default DATETIME YEAR TO FRACTION(3) precision of CURRENT when no
DATETIME qualifier is specified.
SELECT customer_num,
MONTH (call_dtime) call_month,
MONTH (res_dtime) res_month
FROM cust_calls;
106 6 6
110 7 7
119 7 7
121 7 7
127 7
116 11 11
116 12 12
The following query uses the MONTH function plus DAY and CURRENT to
show what month the customer call was received and resolved if DAY is earlier
than the current day.
SELECT customer_num,
MONTH (call_dtime) called,
MONTH (res_dtime) resolved
FROM cust_calls
WHERE DAY (res_dtime) < DAY (CURRENT);
106 6 6
119 7 7
121 7 7
SELECT customer_num,
WEEKDAY (call_dtime) called,
WEEKDAY (res_dtime) resolved
FROM cust_calls
ORDER BY resolved;
127 3
110 0 0
119 1 2
121 3 3
116 3 3
106 3 3
116 5 4
SELECT COUNT(*)
FROM cust_calls
WHERE WEEKDAY (call_dtime) IN (0,6);
(count(*))
SELECT customer_num,
EXTEND (call_dtime, month to minute) call_time,
EXTEND (res_dtime, month to minute) res_time
FROM cust_calls
ORDER BY res_time;
The query returns the month-to-minute range for the columns labeled call_time
and res_time and gives an indication of the work load.
The TO_CHAR function can also format DATETIME values. See “The TO_CHAR
function” on page 4-11 for information about this built-in function, which can also
accept DATE values or numeric values as an argument, and returns a formatted
character string.
Besides the built-in time functions that these examples illustrate, IBM Informix also
supports the ADD_MONTHS, LAST_DAY, MDY, MONTHS_BETWEEN, and
NEXT_DAY functions. In addition to these functions, the TRUNC and ROUND
functions can return values that change the precision of DATE or DATETIME
arguments. These additional time functions are described in the IBM Informix Guide
to SQL: Syntax.
Date-conversion functions
You can use a date-conversion function anywhere you use an expression.
The following conversion functions convert between date and character values:
The following query converts DATETIME values to DATE format and displays the
values, with labels, only when call_dtime is greater than or equal to the specified
SELECT customer_num,
DATE (call_dtime) called,
DATE (res_dtime) resolved
FROM cust_calls
WHERE call_dtime >= DATE (’1/1/98’);
You can also use the TO_CHAR function to convert a DATETIME or DATE value
to an LVARCHAR value.
The following query uses the TO_CHAR function to convert a DATETIME value
to a more readable character string.
SELECT customer_num,
TO_CHAR(call_dtime, "%A %B %d %Y") call_date
FROM cust_calls
WHERE call_code = "B";
customer_num 119
call_date Friday July 01 1998
The following query uses the TO_CHAR function to convert DATE values to more
readable character strings.
SELECT order_num,
TO_CHAR(ship_date,"%A %B %d %Y") date_shipped
FROM orders
WHERE paid_date IS NULL;
order_num 1006
date_shipped
order_num 1007
date_shipped Sunday June 05 1998
order_num 1012
date_shipped Wednesday June 29 1998
order_num 1016
date_shipped Tuesday July 12 1998
order_num 1017
date_shipped Wednesday July 13 1998
The TO_CHAR function can also format numeric values. For more information
about the built-in TO_CHAR function, see the IBM Informix Guide to SQL: Syntax.
You can also use the TO_DATE function to convert an LVARCHAR value to a
DATETIME value.
The following query uses the TO_DATE function to convert character string values
to DATETIME values whose format you specify.
customer_num 110
You can use the DATE or TO_DATE function to convert a character string to a
DATE value. One advantage of the TO_DATE function is that it allows you to
specify a format for the value returned. (You can use the TO_DATE function,
which always returns a DATETIME value, to convert a character string to a DATE
value because the database server implicitly handles conversions between DATE
and DATETIME values.)
department marketing 5
department engineering 7
department publications 4
department accounting 3
You can also evaluate the number of elements in a collection from within a
predicate expression, as the following query shows.
department accounting 3
department marketing 5
department publications 4
For detailed information and the syntax of smart-large-object functions, see the
Expression segment in the IBM Informix Guide to SQL: Syntax.
Suppose you create the inmate and fbi_list tables, as the following figure shows.
The following SELECT statement uses the LOTOFILE() function to copy data from
the felony column into the felon_322.txt file that is located on the client
computer:
SELECT id_num, LOTOFILE(felony, ’felon_322.txt’, ’client’)
FROM inmate
WHERE id = 322;
The first argument for LOTOFILE() specifies the name of the column from which
data is to be exported. The second argument specifies the name of the file into
which data is to be copied. The third argument specifies whether the target file is
located on the client computer ('client') or server computer ('server').
The following rules apply for specifying the path of a file name in a function
argument, depending on whether the file resides on the client or server computer:
v If the source file resides on the server computer, you must specify the full path
name to the file (not the path name relative to the current working directory).
v If the source file resides on the client computer, you can specify either the full or
relative path name to the file.
String-manipulation functions
String-manipulation functions accept arguments of type CHAR, NCHAR,
VARCHAR, NVARCHAR, or LVARCHAR. You can use a string-manipulation
function anywhere you use an expression.
The following functions convert between upper and lowercase letters in a character
string:
v LOWER
v UPPER
v INITCAP
The following query uses the LOWER function to convert any uppercase letters in
a character string to lowercase letters.
manu_code (expression)
PRC prc
KAR kar
PRC prc
SMT smt
HRO hro
The following query uses the UPPER function to convert any lowercase letters in a
character string to uppercase letters.
call_code (expression)
B BILLING ERROR
D DAMAGED GOODS
I INCORRECT MERCHANDISE SENT
L LATE SHIPMENT
O OTHER
The following query uses the INITCAP function to convert the first letter of every
word in a character string to an uppercase letter.
(expression)
Tennis Racquet
Tennis Ball
Volleyball
Volleyball Net
Helmet
Golf Shoes
3 Golf Balls
Running Shoes
Watch
Kick Board
Swim Cap
In the following query, the REPLACE function replaces the unit column value each
with item for every row that the query returns. The first argument of the
REPLACE function is the expression to be evaluated. The second argument
specifies the characters that you want to replace. The third argument specifies a
new character string to replace the characters removed.
1 case $250.00
2 case $126.00
4 case $480.00
7 case $600.00
110 case $260.00
205 case $312.00
301 item $42.50
302 item $4.50
304 box $280.00
305 case $48.00
309 case $40.00
312 box $72.00
Restriction: The units of measurement in the arguments to these two functions are
bytes, rather than logical characters. This is of no importance in the default locale,
nor in other single-byte locales, but you should not invoke SUBSTRING or
SUBSTR in locales in which the logical characters of the code set can differ in their
storage lengths.
sname (expression)
Arizona Ariz
sname (expression)
In the following query, the SUBSTRING function returns only the first character
for any sname column value that the query returns. For the SUBSTRING function,
a start position of -2 counts backward three positions (0, -1, -2) from the start
position of the string (for a start position of 0, the function counts backward one
position from the beginning of the string).
sname (expression)
Arizona A
To return a portion of a character string, specify the start position and length
(optional) to determine which portion of the character string the SUBSTR function
returns. The start position that you specify for the SUBSTR function can be a
positive or a negative number. However, the SUBSTR function treats a negative
number in the start position differently than does the SUBSTRING function. When
the start position is a negative number, the SUBSTR function counts backward
from the end of the character string, which depends on the length of the string, not
the character length of a word or visible characters that the string contains. The
SUBSTR function recognizes zero (0) or 1 in the start position as the first position
in the string.
The following query shows an example of the SUBSTR function that includes a
negative number for the start position. Given a start position of -15, the SUBSTR
function counts backward 15 positions from the end of the string to find the start
position and then returns the next five characters.
sname (expression)
California Calif
To use a negative number for the start position, you need to know the length of
the value that is evaluated. The sname column is defined as CHAR(15), so a
SUBSTR function that accepts an argument of type sname can use a start position
of 0, 1, or -15 for the function to return a character string that begins from the first
position in the string.
The data type of the source string and the character string that serves as padding
can be any data type that converts to VARCHAR or NVARCHAR.
The following query shows an example of the LPAD function with a specified
length of 21 bytes. Because the source string has a length of 15 bytes (sname is
defined as CHAR(15)), the LPAD function pads the first six positions to the left of
the source string.
sname (expression)
California ------California
Arizona ------Arizona
The data type of the source string and the character string that serves as padding
can be any data type that converts to VARCHAR or NVARCHAR.
The following query shows an example of the RPAD function with a specified
length of 21 bytes. Because the source string has a length of 15 bytes (sname is
defined as CHAR(15)), the RPAD function pads the first six positions to the right
of the source string.
sname (expression)
West Virginia West Virginia ------
Arizona Arizona ------
In addition to these functions, the LTRIM and RTRIM functions can return a value
that drops specified leading or trailing padding characters from their string
argument, and the ASCII function can return the numeric value of the codepoint
within the ASCII character set of the first character in its string argument. These
built-in functions for operations on string values are described in theIBM Informix
Guide to SQL: Syntax.
Other functions
You can also use the LENGTH, USER, CURRENT, SYSDATE, and TODAY
functions anywhere in an SQL expression that you would use a constant. In
addition, you can include the DBSERVERNAME function in a SELECT statement
to display the name of the database server where the current database resides.
You can use these functions to select an expression that consists entirely of constant
values or an expression that includes column data. In the first instance, the result
is the same for all rows of output.
In addition, you can use the HEX function to return the hexadecimal encoding of
an expression, the ROUND function to return the rounded value of an expression,
and the TRUNC function to return the truncated value of an expression. For more
information on the preceding functions, see the IBM Informix Guide to SQL: Syntax.
SELECT customer_num,
LENGTH (fname) + LENGTH (lname) namelength
FROM customer
WHERE LENGTH (company) > 15;
customer_num namelength
101 11
105 13
107 11
112 14
115 11
118 10
119 10
120 10
122 12
124 11
125 10
126 12
127 10
128 11
Although the LENGTH function might not be useful when you work with
DB-Access, it can be important to determine the string length for programs and
reports. The LENGTH function returns the clipped length of a CHARACTER or
VARCHAR string and the full number of bytes in a TEXT or BYTE string.
IBM Informix also supports the CHAR_LENGTH function, which returns the
number of logical characters in its string argument, rather than the number of
bytes. This function is useful in locales where a single logical character might
require more than a single byte of storage. For more information about the
CHAR_LENGTH function, see the IBM Informix Guide to SQL: Syntax and the IBM
Informix GLS User's Guide.
The following query returns the user name (login account name) of the user who
executes the query. It is repeated once for each row in the table.
If the user name of the current user is richc, the query retrieves only those rows in
the cust_calls table where user_id = richc.
customer_num 119
call_dtime 1998-07-01 15:00
user_id richc
call_code B
call_descr Bill does not reflect credit from previous order
res_dtime 1998-07-02 08:21
res_descr Spoke with Jane Akant in Finance. She found the error and is
sending new bill to customer
order_num 1018
order_date 07/10/1998
customer_num 121
ship_instruct SW corner of Biltmore Mall
backlog n
po_num S22942
ship_date 07/13/1998
ship_weight 70.50
ship_charge $20.00
paid_date 08/06/1998
In the following query, you assign the label server to the DBSERVERNAME
expression and also select the tabid column from the systables system catalog
table. This table describes database tables, and tabid is the table identifier.
server tabid
montague 1
montague 2
montague 3
montague 4
The WHERE clause restricts the numbers of rows displayed. Otherwise, the
database server name would be displayed once for each row of the systables table.
hexnum hexzip
0x00000065 0x00016F86
0x00000066 0x00016FA5
0x00000067 0x0001705F
0x00000068 0x00016F4A
0x00000069 0x00016F46
0x0000006A
. 0x00016F6F
.
.
You can use the DBINFO function anywhere within SQL statements and within
SPL routines.
The following query shows how you might use the DBINFO function to find out
the name of the host computer on which the database server runs.
(constant)
lyceum
Without the FIRST 1 clause to restrict the values in the tabid, the host name of the
computer on which the database server runs would be repeated for each row of
the systables table. The following query shows how you might use the DBINFO
function to find out the complete version number and the type of the current
database server.
For more information about how to use the DBINFO function to find information
about your current database server, database session, or database, see the IBM
Informix Guide to SQL: Syntax.
The DECODE function returns a_value when a equals test, and returns b_value
when b equals test, and, in general, returns n_value when n equals test.
If several expressions match test, DECODE returns n_value for the first expression
found. If no expression matches test, DECODE returns exp_m; if no expression
matches test and there is no exp_m, DECODE returns NULL.
Restriction: The DECODE function does not support arguments of type TEXT or
BYTE.
emp_id evaluation
012233 great
012344 poor
012677 NULL
012288 good
012555 very good
In some cases, you might want to convert a set of values. For example, suppose
you want to convert the descriptive values of the evaluation column in the
preceding example to corresponding numeric values. The following query shows
how you might use the DECODE function to convert values from the evaluation
column to numeric values for each row in the employee table.
emp_id evaluation
012233 100
012344 0
012677 -1
012288 50
012555
. 75
.
.
You can specify any data type for the arguments of the DECODE function
provided that the arguments meet the following requirements:
v The arguments test, a,b, ..., n all have the same data type or evaluate to a
common compatible data type.
v The arguments a_value, b_value, ..., n_value all have the same data type or
evaluate to a common compatible data type.
name address
The following query includes the NVL function, which returns a new value for
each row in the table where the address column contains a NULL value.
name address
You can specify any data type for the arguments of the NVL function provided
that the two arguments evaluate to a common compatible data type.
If both arguments of the NVL function evaluate to NULL, the function returns
NULL.
IBM Informix also supports the NULLIF function, which resembles the NVL
function, but has different semantics. NULLIF returns NULL if its two arguments
are equal, or returns its first argument if its arguments are not equal. For more
information about the NULLIF function, see the IBM Informix Guide to SQL: Syntax.
SPL routines contain special Stored Procedure Language (SPL) statements as well
as SQL statements. For more information on SPL routines, see Chapter 11, “Create
and use SPL routines,” on page 11-1.
IBM Informix allows you to write external routines in C and in Java. For more
information, see IBM Informix User-Defined Routines and Data Types Developer's
Guide.
SPL routines that return more than a single value are not supported in the
Projection clause of SELECT statements. In the preceding example, if test_func()
returns more than one value, the database server returns an error message.
SPL routines provide a way to extend the range of functions available by allowing
you to perform a subquery on each row you select.
For example, suppose you want a listing of the customer number, the customer's
last name, and the number of orders the customer has made. The following query
shows one way to retrieve this information. The customer table has customer_num
and lname columns but no record of the number of orders each customer has
made. You could write a get_orders routine, which queries the orders table for
each customer_num and returns the number of corresponding orders (labeled
n_orders).
101 Pauli 1
102 Sadler 9
103 Currie 9
104 Higgins 4
.
.
.
123 Hanlon 1
124 Putnum 1
125 Henry 0
126 Neelie 1
127 Satifer 1
128 Lessor 0
Use SPL routines to encapsulate operations that you frequently perform in your
queries. For example, the condition in the following query contains a routine,
conv_price, that converts the unit price of a stock item to a different currency and
adds any import tariffs.
Use the SET ENCRYPTION PASSWORD statement with the following built-in
encryption and decryption functions:
v ENCRYPT_AES
ENCRYPT_AES(data-string-expression
[, password-string-expression [, hint-string-expression ]])
v ENCRYPT_TDES
ENCRYPT_TDES (data-string-expression
[, password-string-expression [, hint-string-expression ]])
v DECRYPT_CHAR
DECRYPT_CHAR(EncryptedData [, PasswordOrPhrase])
v DECRYPT_BINARY
DECRYPT_BINARY(EncryptedData [, PasswordOrPhrase])
v GETHINT
GETHINT(EncryptedData)
You can use these SQL built-in functions to implement column-level or cell-level
encryption.
v Use column-level encryption to encrypt all values in a given column with the
same password.
v Use cell-level encryption to encrypt data within the column with different
passwords.
Tip: If you intend to select encrypted data from a large table, specify an
unencrypted column on which to select the rows. You can create indexes or
foreign-key constraints on columns that contain encrypted data, but to do so is an
inefficient use of resources, because such indexes and foreign-key constraints are
not used by the query optimizer.
Important: Encrypted data values occupy more storage space than the
corresponding unencrypted data. A column whose width is sufficient to store plain
text might need to be increased before it can support column-level encryption or
cell-level encryption. If you attempt to insert an encrypted value into a column
whose declared width is shorter than the encrypted string, the column stores a
truncated value that cannot be decrypted.
Summary
This chapter introduced sample syntax and results for functions in basic SELECT
statements to query a relational database and to manipulate the returned data.
“Functions in SELECT statements” on page 4-1 shows how to perform the
following actions:
v Use the aggregate functions in the Projection clause to calculate and retrieve
specific data.
v Include the time functions DATE, DAY, MDY, MONTH, WEEKDAY, YEAR,
CURRENT, and EXTEND plus the TODAY, LENGTH, and USER functions in
your SELECT statements.
v Use conversion functions in the SELECT clause to convert between date and
character values.
v Use string-manipulation functions in the SELECT clause to convert between
upper and lower case letters or to manipulate character strings in various ways.
“SPL routines in SELECT statements” on page 4-26 shows how to include SPL
routines in your SELECT statements.
“Data encryption functions” on page 4-28 shows how the use of the SET
ENCRYPTION statement and built-in encryption and decryption functions can
prevent users who cannot provide a password from viewing or modifying
sensitive data.
This section also extends the earlier discussion of joins. It illustrates self-joins,
which enable you to join a table to itself, and four kinds of outer joins, in which
you apply the keyword OUTER to treat two or more joined tables unequally. It
also introduces correlated and uncorrelated subqueries and their operational
keywords, shows how to combine queries with the UNION operator, and defines
the set operations known as union, intersection, and difference.
Examples in this section show how to use some or all of the SELECT statement
clauses in your queries. The clauses must appear in the following order:
1. Projection
2. FROM
3. WHERE
4. GROUP BY
5. HAVING
6. ORDER BY
7. INTO TEMP
For an example of a SELECT statement that uses all these clauses in the correct
order, see Figure 5-15 on page 5-6.
An additional SELECT statement clause, INTO, which you can use to specify
program and host variables in SQL APIs, is described in Chapter 8, “SQL
programming,” on page 8-1, as well as in the publications that come with the
product.
This section also describes nested SELECT statements, in which subqueries are
specified within the Projection, FROM, or WHERE clauses of the main query. Other
sections show how SELECT statements can define and manipulate collections, and
how to perform set operations on query results.
The GROUP BY clause combines similar rows, producing a single result row for
each group of rows that have the same values, for each column listed in the
Projection clause. The HAVING clause sets conditions on those groups after you
Using the GROUP BY clause without aggregates is much like using the DISTINCT
(or UNIQUE) keyword in the SELECT clause. The following query is described in
“Select specific columns” on page 2-10.
You could also write the statement as the following query shows.
customer_num
101
104
106
110
.
.
.
124
126
127
The GROUP BY clause collects the rows into sets so that each row in each set has
the same customer numbers. With no other columns selected, the result is a list of
the unique customer_num values.
The power of the GROUP BY clause is more apparent when you use it with
aggregate functions.
The following query retrieves the number of items and the total price of all items
for each order.
Figure 5-4 on page 5-2 returns one row for each group. It uses labels to give names
to the results of the COUNT and SUM expressions, as the result shows.
1001 1 $250.00
1002 2 $1200.00
1003 3 $959.00
1004 4 $1416.00
.
.
.
1021 4 $1614.00
1022 3 $232.00
1023 6 $824.00
The result collects the rows of the items table into groups that have identical order
numbers and computes the COUNT of rows in each group and the SUM of the
prices.
You cannot include a TEXT, BYTE, CLOB, or BLOB column in a GROUP BY clause.
To group, you must be able to sort, and no natural sort order exists for these data
types.
Unlike the ORDER BY clause, the GROUP BY clause does not order data. Include
an ORDER BY clause after your GROUP BY clause if you want to sort data in a
particular order or sort on an aggregate in the projection list.
The following query is the same as Figure 5-4 on page 5-2 but includes an ORDER
BY clause to sort the retrieved rows in ascending order of price, as the result
shows.
1010 2 $84.00
1011 1 $99.00
1013 4 $143.80
1022 3 $232.00
1001 1 $250.00
1020 2 $438.00
1006 5 $448.00
.
.
.
1002 2 $1200.00
1004 4 $1416.00
1014 2 $1440.00
1019 1 $1499.97
1021 4 $1614.00
1007 5 $1696.00
The topic “Select specific columns” on page 2-10 describes how to use an integer in
an ORDER BY clause to indicate the position of a column in the projection list. You
can also use an integer in a GROUP BY clause to indicate the position of column
names or display labels in the GROUP BY list.
The following query returns the same rows as Figure 5-6 on page 5-3 shows.
When you build a query, all non-aggregate columns that are in the projection list in
the Projection clause must also be included in the GROUP BY clause. A SELECT
statement with a GROUP BY clause must return only one row per group. Columns
that are listed after GROUP BY are certain to reflect only one distinct value within
a group, and that value can be returned. However, a column not listed after
GROUP BY might contain different values in the rows that are contained in the
group.
The following query shows how to use the GROUP BY clause in a SELECT
statement that joins tables.
The query joins the orders and items tables, assigns table aliases to them, and
returns the rows.
1008 $940.00
1015 $450.00
The following query returns the average total price per item on all orders that have
more than two items. The HAVING clause tests each group as it is formed and
selects those that are composed of more than two rows.
1003 3 $319.67
1004 4 $354.00
1005 4 $140.50
1006 5 $89.60
1007 5 $339.20
1013 4 $35.95
1016 4 $163.50
1017 3 $194.67
1018 5 $226.20
1021 4 $403.50
1022 3 $77.33
1023 6 $137.33
If you use a HAVING clause without a GROUP BY clause, the HAVING condition
applies to all rows that satisfy the search condition. In other words, all rows that
satisfy the search condition make up a single group.
average
$270.97
If Figure 5-13, like Figure 5-11 on page 5-5, had included the non-aggregate column
order_num in the Projection clause, you would have to include a GROUP BY
clause with that column in the group list. In addition, if the condition in the
HAVING clause was not satisfied, the output would show the column heading and
a message would indicate that no rows were found.
The following query contains all the SELECT statement clauses that you can use in
the IBM Informix version of interactive SQL (the INTO clause that names host
variables is available only in an SQL API).
The query joins the orders and items tables; employs display labels, table aliases,
and integers that are used as column indicators; groups and orders the data; and
puts the results in a temporary table, as the result shows.
1017 $584.00
1016 $654.00
1012 $1040.00
1019 $1499.97 26
1005 $562.00 28
1021 $1614.00 30
1022 $232.00 40
1010 $84.00 66
1009 $450.00 68
1020 $438.00 71
This section discusses how to use two more complex kinds of joins, self-joins and
outer joins. As described for simple joins, you can define aliases for tables and
assign display labels to expressions to shorten your multiple-table queries. You can
also issue a SELECT statement with an ORDER BY clause that sorts data into a
temporary table.
Self-joins
A join does not always have to involve two different tables. You can join a table to
itself, creating a self-join. Joining a table to itself can be useful when you want to
compare values in a column to other values in the same column.
To create a self-join, list a table twice in the FROM clause, and assign it a different
alias each time. Use the aliases to refer to the table in the Projection and WHERE
clauses as if it were two separate tables. (Aliases in SELECT statements are
discussed in “Aliases” on page 2-47 and in the IBM Informix Guide to SQL: Syntax.)
Just as in joins between tables, you can use arithmetic expressions in self-joins. You
can test for null values, and you can use an ORDER BY clause to sort the values in
a specified column in ascending or descending order.
The following query finds pairs of orders where the ship_weight differs by a
factor of five or more and the ship_date is not null. The query then orders the data
by ship_date.
If you want to store the results of a self-join into a temporary table, append an
INTO TEMP clause to the SELECT statement and assign display labels to at least
one set of columns to rename them. Otherwise, the duplicate column names cause
an error and the temporary table is not created.
If you query with SELECT * from table shipping, you see the following rows.
You can join a table to itself more than once. The maximum number of self-joins
depends on the resources available to you.
The self-join in the following query creates a list of those items in the stock table
that are supplied by three manufacturers. The self-join includes the last two
conditions in the WHERE clause to eliminate duplicate manufacturer codes in rows
that are retrieved.
If you want to select rows from a payroll table to determine which employees earn
more than their manager, you might construct the self-join as the following
SELECT statement shows:
SELECT emp.employee_num, emp.gross_pay, emp.level,
emp.dept_num, mgr.employee_num, mgr.gross_pay,
mgr.dept_num, mgr.level
FROM payroll emp, payroll mgr
WHERE emp.gross_pay > mgr.gross_pay
AND emp.level < mgr.level
AND emp.dept_num = mgr.dept_num
ORDER BY 4;
The following query uses a correlated subquery to retrieve and list the 10
highest-priced items ordered.
1018 $15.00
1013 $19.80
1003 $20.00
1005 $36.00
1006 $36.00
1013 $36.00
1010 $36.00
1013 $40.00
1022 $40.00
1023 $40.00
You can create a similar query to find and list the 10 employees in the company
who have the most seniority.
Outer joins
This section shows how to create and use outer joins in a SELECT statement.
“Create a join” on page 2-41 discusses inner joins. Whereas an inner join treats two
or more joined tables equally, an outer join treats two or more joined tables
asymmetrically. An outer join makes one of the tables dominant (also called the
outer table) over the other subordinate tables (also called inner tables).
In an inner join or in a simple join, the result contains only the combinations of
rows that satisfy the join conditions. Rows that do not satisfy the join conditions
are discarded.
In an outer join, the result contains the combinations of rows that satisfy the join
conditions and the rows from the dominant table that would otherwise be
discarded because no matching row was found in the subordinate table. The rows
from the dominant table that do not have matching rows in the subordinate table
contain NULL values in the columns selected from the subordinate table.
An outer join allows you to apply join filters to the inner table before the join
condition is applied.
Earlier versions of the database server supported only the IBM Informix extension
to the ANSI-SQL standard syntax for outer joins. This syntax is still supported.
However, the ANSI-SQL standard syntax provides for more flexibility with creating
queries. It is recommended that you use the ANSI-SQL standard syntax to create
new queries. Whichever form of syntax you use, you must use it for all outer joins
in a single query block.
Before you rely on outer joins, determine whether one or more inner joins can
work. You can often use an inner join when you do not need supplemental
information from other tables.
Restriction: You cannot combine IBM Informix and ANSI outer-join syntax in the
same query block.
An outer join must have a Projection clause, a FROM clause, and a WHERE clause.
The join conditions are expressed in a WHERE clause. To transform a simple join
into an outer join, insert the keyword OUTER directly before the name of the
subordinate tables in the FROM clause. As shown later in this section, you can
include the OUTER keyword more than once in your query.
The ANSI outer-join syntax begins an outer join with the LEFT JOIN, LEFT
OUTER JOIN, RIGHT JOIN, or RIGHT OUTER JOIN keywords. The OUTER
keyword is optional. Queries can specify a join condition and optional join filters
in the ON clause. The WHERE clause specifies a post-join filter. In addition, you
can explicitly specify the type of join using the LEFT or right clause. ANSI join
syntax also allows the dominant or subordinate part of an outer join to be the
result set of another join, when you begin the join with a left parenthesis.
If you use ANSI syntax for an outer join, you must use the ANSI syntax for all
outer joins in a single query block.
Tip: The examples in this section use table aliases for brevity. “Aliases” on page
2-47 discusses table aliases.
The following query uses ANSI syntax LEFT OUTER JOIN to achieve the same
results as Figure 5-30 on page 5-14, which uses the IBM Informix outer-join syntax:
In this example, you can use the ON clause to specify the join condition. You can
add an additional filter in the WHERE clause to limit your result set; such a filter
is a post-join filter.
The following query returns only rows in which customers have not made any
calls to customer service. In this query, the database server applies the filter in the
WHERE clause after it performs the outer join on the customer_num column of the
customer and cust_calls tables.
In addition to the previous examples, the following examples show various types
of query constructions that are available with ANSI join syntax:
SELECT *
FROM (t1 LEFT OUTER JOIN (t2 LEFT OUTER JOIN t3 ON t2.c1=t3.c1)
ON t1.c1=t3.c1) JOIN (t4 LEFT OUTER JOIN t5 ON t4.c1=t5.c1)
ON t1.c1=t4.c1;
SELECT *
FROM (t1 LEFT OUTER JOIN (t2 LEFT OUTER JOIN t3 ON t2.c1=t3.c1)
ON t1.c1=t3.c1),
(t4 LEFT OUTER JOIN t5 ON t4.c1=t5.c1)
WHERE t1.c1 = t4.c1;
SELECT *
FROM (t1 LEFT OUTER JOIN (t2 LEFT OUTER JOIN t3 ON t2.c1=t3.c1)
ON t1.c1=t3.c1) LEFT OUTER JOIN (t4 JOIN t5 ON t4.c1=t5.c1)
ON t1.c1=t4.c1;
SELECT *
FROM t1 LEFT OUTER JOIN (t2 LEFT OUTER JOIN t3 ON t2.c1=t3.c1)
ON t1.c1=t2.c1;
SELECT *
FROM t1 LEFT OUTER JOIN (t2 LEFT OUTER JOIN t3 ON t2.c1=t3.c1)
ON t1.c1=t3.c1;
SELECT *
FROM (t1 LEFT OUTER JOIN t2 ON t1.c1=t2.c1)
LEFT OUTER JOIN t3 ON t2.c1=t3.c1;
SELECT *
FROM (t1 LEFT OUTER JOIN t2 ON t1.c1=t2.c1)
LEFT OUTER JOIN t3 ON t1.c1=t3.c1;
SELECT *
FROM t9, (t1 LEFT JOIN t2 ON t1.c1=t2.c1),
(t3 LEFT JOIN t4 ON t3.c1=10), t10, t11,
(t12 LEFT JOIN t14 ON t12.c1=100);
SELECT * FROM
((SELECT c1,c2 FROM t3) AS vt3(v31,v32)
The last example above illustrates joins on derived tables. It specifies a left outer
join on the results of a subquery in the FROM clause of the outer query with the
results of another left outer join on two other subquery results. See the section
“Subqueries in the FROM clause” on page 5-20 for less complex examples of the
ANSI-compliant syntax for subqueries.
The following query is an example of a right outer join on the customer and
orders tables.
The query returns all rows from the dominant table orders and, as necessary,
displays the corresponding values from the subservient table customer as NULL.
Simple join
The following query is an example of a simple join on the customer and cust_calls
tables.
The query returns only those rows in which the customer has made a call to
customer service, as the result shows.
The addition of the keyword OUTER before the cust_calls table makes it the
subservient table. An outer join causes the query to return information on all
customers, whether or not they have made calls to customer service. All rows from
the dominant customer table are retrieved, and NULL values are assigned to
columns of the subservient cust_calls table, as the result shows.
customer_num 102
lname Sadler
company Sports Spot
phone 415-822-1289
call_dtime
call_descr
.
.
.
customer_num 107
lname Ream
company Athletic Supplies
phone 415-356-9876
call_dtime
call_descr
customer_num 108
lname Quinn
company Quinn’s Sports
phone 415-544-8729
call_dtime
call_descr
The query first performs a simple join on the orders and items tables, retrieving
information on all orders for items with a manu_code of KAR or SHM. It then
performs an outer join to combine this information with data from the dominant
customer table. An optional ORDER BY clause reorganizes the data into the
following form.
114 Albertson
118 Baxter
113 Beatty
.
.
.
105 Vector
121 Wallack 1018 302 KAR 3
106 Watson
The query individually joins the subservient tables orders and cust_calls to the
dominant customer table; it does not join the two subservient tables. An INTO
TEMP clause selects the results into a temporary table for further manipulation or
queries, as the result shows.
114 Albertson
118 Baxter
113 Beatty
103 Currie
115 Grant 1010 06/17/1998
.
.
.
117 Sipes 1012 06/18/1998
105 Vector
121 Wallack 1018 07/10/1998 1998-07-10 14:05
106 Watson 1004 05/22/1998 1998-06-12 08:20
106 Watson 1014 06/25/1998 1998-06-12 08:20
If Figure 5-34 had tried to create a join condition between the two subservient
tables o and x, as the following query shows, an error message would indicate the
creation of a two-sided outer join.
The query first performs an outer join on the orders and items tables, retrieving
information on all orders for items with a manu_code of KAR or SHM. It then
performs a second outer join that combines this information with data from the
dominant customer table.
114 Albertson
118 Baxter
113 Beatty
103 Currie
115 Grant 1010
.
.
.
117 Sipes 1012
117 Sipes 1007
105 Vector
121 Wallack 1018 302 KAR 3
106 Watson 1014
106 Watson 1004
You can specify the join conditions in two ways when you apply an outer join to
the result of an outer join to a third table. The two subservient tables are joined,
but you can join the dominant table to either subservient table without affecting
the results if the dominant table and the subservient table share a common
column.
You can also specify a subquery in various clauses of the INSERT, DELETE,
MERGE, or UPDATE statements where a subquery is valid.
Correlated subqueries
A correlated subquery is a subquery that refers to a column of a table that is not
listed in its FROM clause. The column can be in the Projection clause or in the
WHERE clause. To find the table to which the correlated subquery refers, search
the columns until a correlation is found.
The database server will use the outer query to get values. For example, if the
table taba has the column col1 and table tabb has the column col2 and they
contain the following:
taba.col1 aa,bb,null
tabb.col2 bb, null
Then the results might be meaningless. The database server will provide all values
in taba.col1 and then compare them to taba.col1 (outer query WHERE clause). This
will return all rows. You usually use the subquery to return column values from
the inner table. Had the query been written as:
select * from taba where col1 in (select tabb.col1 from tabb);
Then the error -217 column not found would have resulted.
A subquery selects and returns values to the first or outer SELECT statement. A
subquery can return no value, a single value, or a set of values, as follows:
v If a subquery returns no value, the query does not return any rows. Such a
subquery is equivalent to a NULL value.
v If a subquery returns one value, the value is in the form of either one aggregate
expression or exactly one row and one column. Such a subquery is equivalent to
a single number or character value.
v If a subquery returns a list or set of values, the values can represent one row or
one column.
v In the FROM clause of the outer query, a subquery can represent a set of rows
(sometimes called a derived table or a table expression).
SELECT customer.customer_num,
(SELECT SUM(ship_charge)
FROM orders
WHERE customer.customer_num = orders.customer_num)
AS total_ship_chg
FROM customer;
customer_num total_ship_chg
101 $15.30
102
103
104 $38.00
105
.
.
.
123 $8.50
124 $12.00
125
126 $13.00
127 $18.00
128
The following query uses asterisk notation in the outer query to return the results
of a subquery that retrieves all fields of the address column in the employee table.
This illustrates how to specify a derived table, but it is a trivial example of this
syntax, because the outer query does not manipulate any values in the table
expression that the subquery in the FROM clause returns. (See Figure 3-15 on page
3-6 for a simple query that returns the same results.)
The following query is a more complex example in which the outer query selects
only the first qualifying row of a derived table that a subquery in the FROM clause
specifies as a simple join on the customer and cust_calls tables.
The query returns only those rows in which the customer has made a call to
customer service, as the result shows.
customer_num 106
lname Watson
company Watson & Son
phone 415-389-8789
call_dtime 1998-06-12 08:20
call_descr Order was received, but two of the cans of
ANZ tennis balls within the case were empty
You can use any relational operator with ALL and ANY to compare something to
every one of (ALL) or to any one of (ANY) the values that the subquery produces.
You can use the keyword SOME in place of ANY. The operator IN is equivalent to
= ANY. To create the opposite search condition, use the keyword NOT or a
different relational operator.
The EXISTS operator tests a subquery to see if it found any values; that is, it asks
if the result of the subquery is not null. You cannot use the EXISTS keyword in a
subquery that contains a column with a TEXT or BYTE data type.
For the syntax that you use to create a condition with a subquery, see the IBM
Informix Guide to SQL: Syntax.
The following query lists the following information for all orders that contain an
item for which the total price is less than the total price on every item in order
number 1023.
The following query finds the order number of all orders that contain an item for
which the total price is greater than the total price of any one of the items in order
number 1005.
order_num
1001
1002
1003
1004
.
.
.
1020
1021
1022
1023
Single-valued subqueries
You do not need to include the keyword ALL or ANY if you know the subquery
can return exactly one value to the outer-level query. A subquery that returns
exactly one value can be treated like a function. This kind of subquery often uses
an aggregate function because aggregate functions always return single values.
The following query uses the aggregate function MAX in a subquery to find the
order_num for orders that include the maximum number of volleyball nets.
1012
The following query uses the aggregate function MIN in the subquery to select
items for which the total price is higher than 10 times the minimum price.
Correlated subqueries
A correlated subquery is a subquery that refers to a column of a table that is not in
its FROM clause. The column can be in the Projection clause or in the WHERE
clause.
po_num ship_date
4745 06/21/1998
278701 06/29/1998
429Q 06/29/1998
8052 07/03/1998
B77897 07/03/1998
LZ230 07/06/1998
B77930 07/10/1998
PC6782 07/12/1998
DM354331 07/13/1998
S22942 07/13/1998
MA003 07/16/1998
W2286 07/16/1998
Z55709 07/16/1998
C3288 07/25/1998
KF2961 07/30/1998
W9925 07/30/1998
If you use a correlated subquery, such as Figure 5-53 on page 5-23, on a large table,
you should index the ship_date column to improve performance. Otherwise, this
SELECT statement is inefficient, because it executes the subquery once for every
row of the table. For information about indexing and performance issues, see the
IBM Informix Administrator's Guide and your IBM Informix Performance Guide.
You cannot use a correlated subquery in the FROM clause, however, as the
following invalid example illustrates:
SELECT item_num, stock_num FROM items,
(SELECT stock_num FROM catalog
WHERE stock_num = items.item_num) AS vtab;
The database server issues this error because the items.item_num column in the
subquery also appears in the Projection clause of the outer query, but the FROM
clause of the inner query specifies only the catalog table. The term table expression
in the error message text refers to the set of column values or expressions that are
returned by a subquery in the FROM clause, where only uncorrelated subqueries
are valid.
You can often construct a query with EXISTS that is equivalent to one that uses IN.
The following query uses an IN predicate to construct a query that returns the
same result as the query above.
Figure 5-55 and Figure 5-56 return rows for the manufacturers that produce a kind
of shoe, as well as the lead time for ordering the product. The result shows the
return values.
manu_name lead_time
Anza 5
Hero 4
Karsten 21
Nikolus 8
ProCycle 9
Shimara 30
Add the keyword NOT to IN or to EXISTS to create a search condition that is the
opposite of the condition in the preceding queries. You can also substitute !=ALL
for NOT IN.
The following query shows two ways to do the same thing. One way might allow
the database server to do less work than the other, depending on the design of the
database and the size of the tables. To find out which query might be better, use
the SET EXPLAIN command to get a listing of the query plan. SET EXPLAIN is
discussed in your IBM Informix Performance Guide and IBM Informix Guide to SQL:
Syntax.
customer_num company
The keywords EXISTS and IN are used for the set operation known as intersection,
and the keywords NOT EXISTS and NOT IN are used for the set operation known
as difference. These concepts are discussed in “Set operations” on page 5-32.
The following query performs a subquery on the items table to identify all the
items in the stock table that have not yet been ordered.
No logical limit exists to the number of subqueries a SELECT statement can have,
but the size of any SQL statement as a character string is physically limited to 64
kilobytes. This limit is typically larger, however, than most queries that you are
likely to compose.
Perhaps you want to check whether information has been entered correctly in the
database. One way to find errors in a database is to write a query that returns
output only when errors exist. A subquery of this type serves as a kind of audit
The query returns only those rows for which the total price of an item on an order
is not equal to the stock unit price times the order quantity. If no discount has been
applied, such rows were probably entered incorrectly in the database. The query
returns rows only when errors occur. If information is correctly inserted into the
database, no rows are returned.
Certain restrictions apply. If the FROM clause of a subquery returns more than one
row, and the clause specifies the same table or view that the outer DML statement
is modifying, the DML operation will succeed under these circumstances:
v The DML statement is not an INSERT statement.
v No SPL routine within the subquery references the table that is being modified.
v The subquery does not include a correlated column name.
v The subquery is specified using the Condition with Subquery syntax in the
WHERE clause of the DELETE or UPDATE statement.
If any of these conditions are not met, the DML operation fails with error -360.
The following example updates the stock table by increasing the unit_price value
by 10% for a subset of prices. The WHERE clause specifies which prices to increase
by applying the IN operator to the rows returned by a subquery that selects only
the rows of the stock table where the unit_price value is less than 75.
UPDATE stock SET unit_price = unit_price * 1.1
WHERE unit_price IN
(SELECT unit_price FROM stock WHERE unit_price < 75);
Collection subqueries
A collection subquery enables users to construct a collection expression from a
subquery expression. A collection subquery uses the MULTISET keyword
immediately before the subquery to convert the values returned into a MULTISET
collection. When you use the MULTISET keyword before a subquery expression,
however, the database server does not change the rows of the underlying table but
only modifies a copy of those rows. For example, if a collection subquery is passed
to a user-defined routine that modifies the collection, then a copy of the collection
is modified but not the underlying table.
A collection subquery is an expression that can take either of the following forms:
MULTISET(SELECT expression1, expression2... FROM tab_name...)
MULTISET(SELECT ITEM expression FROM tab_name...)
Suppose you create the following table that contains a column of type MULTISET:
CREATE TABLE tab2
(
id_num INT,
ms_col MULTISET(ROW(a INT) NOT NULL)
);
The following query shows how you might use a collection subquery in a WHERE
clause to convert the rows of INT values that the subquery returns to a collection
of type MULTISET. In this example, the database server returns rows when the
ms_col column of tab2 is equal to the result of the collection subquery expression
The query includes the ITEM keyword in the subquery, so the int_col values that
the query returns are converted to a collection of type MULTISET (INT NOT
NULL). Without the ITEM keyword, the collection subquery would return a
collection of type MULTISET (ROW(a INT) NOT NULL).
The query examples in the section “Collection subqueries” on page 5-28 specify
collection subqueries by using the TABLE keyword followed (within parentheses)
by the MULTISET keyword, followed by a subquery. This syntax is an IBM
Informix extension to the ANSI/ISO standard for the SQL language.
In the FROM clause of the SELECT statement, and only in that context, you can
substitute syntax that complies with the ANSI/ISO standard for SQL by specifying
a subquery, omitting the TABLE and MULTISET keywords and the nested
parentheses, to specify a collection subquery.
The following query uses the IBM Informix extension syntax to join two collection
subqueries in the FROM clause of the outer query:
The following logically equivalent query returns the same results as the query
above by using ANSI/ISO-compliant syntax to join two derived tables in the
FROM clause of the outer query:
Collection-derived tables
A collection-derived table enables you to handle the elements of a collection
expression as rows in a virtual table. Use the TABLE keyword in the FROM clause
of a SELECT statement to create a collection-derived table. The database server
supports collection-derived tables in SELECT, INSERT, UPDATE, and DELETE
statements.
(expression)
$47.22
$53.22
The following query uses a collection-derived table to access elements from the
sales collection column where the rep_num column equals 102. With a
collection-derived table, you can specify aliases for the table and columns. If no
table name is specified for a collection-derived table, the database server creates
one automatically. This example specifies the derived column list s_month and
s_amount for the collection-derived table c_table.
1998-03 $53.22
1998-04 $18.22
The following query creates a collection-derived table but does not specify a
derived table or derived column names. The query returns the same result as
Figure 5-70 on page 5-30 except the derived columns assume the default field
names of the sales column in the sales_rep table.
month amount
1998-03 $53.22
1998-04 $18.22
In the FROM clause of the SELECT statement, however, and only in that context,
you can instead use syntax that complies with the ANSI/ISO standard for SQL by
specifying a subquery, without the TABLE keyword or the nested parentheses, to
define a collection-derived table.
The following example is logically equivalent to Figure 5-70 on page 5-30, and
specifies the derived column list s_month and s_amount for the collection-derived
table c_table.
1998-03 $53.22
1998-04 $18.22
As in the IBM Informix extension syntax, declaring names for the derived table or
for its columns is optional, rather than required. The following query uses
ANSI/ISO-compliant syntax for a derived table in the FROM clause of the outer
query, and produces the same results as Figure 5-72 on page 5-31:
month amount
1998-03 $53.22
1998-04 $18.22
Set operations
The standard set operations union, intersection, and difference let you manipulate
database information. These three operations let you use SELECT statements to
check the integrity of your database after you perform an update, insert, or delete.
They can be useful when you transfer data to a history table, for example, and
want to verify that the correct data is in the history table before you delete the
data from the original table.
Union
A union operation uses the UNION operator to combine two queries into a single
compound query. You can use the UNION operator between two or more SELECT
statements to produce a temporary table that contains rows that exist in any or all
of the original tables. You can also use the UNION operator in the definition of a
view.
You cannot use the UNION operator inside a subquery in the following contexts
v in the Projection clause of the SELECT statement
v in the WHERE clause of the SELECT, INSERT, DELETE, or UPDATE statement.
The UNION operator is valid, however, in a subquery in the FROM clause of the
SELECT statement, as in the following example:
SELECT * FROM (SELECT col1 FROM tab1 WHERE col1 = 100) AS vtab1(c1),
(SELECT col1 FROM tab2 WHERE col1 = 10
UNION ALL
SELECT col1 FROM tab1 WHERE col1 < 50 ) AS vtab2(vc1);
IBM Informix does not support ordering on ROW types. Because a UNION
operation requires a sort to remove duplicate values, you cannot use a UNION
UNION quantity
SELECT stock_num, manu_code greater less than or
FROM items unit_price < 25.00
WHERE quantity > 3 than 3 equal to 3
less than
qualifies qualifies
25.00
unit_price quantity > 3
greater than or qualifies
equal to 25.00
The UNION keyword selects all rows from the two queries, removes duplicates,
and returns what is left. Because the results of the queries are combined into a
single result, the projection list in each query must have the same number of
columns. Also, the corresponding columns that are selected from each table must
contain compatible data types (CHARACTER data type columns must be the same
length), and these corresponding columns must all allow or all disallow NULL
values.
For the complete syntax of the SELECT statement and the UNION operator, see
the IBM Informix Guide to SQL: Syntax. For information specific to the IBM Informix
ESQL/C product and any limitations that involve the INTO clause and compound
queries, see the IBM Informix ESQL/C Programmer's Manual.
The following query performs a union on the stock_num and manu_code columns
in the stock and items tables.
The query selects those items that have a unit price of less than $25.00 or that have
been ordered in quantities greater than three and lists their stock_num and
manu_code, as the result shows.
5 ANZ
5 NRG
5 SMT
9 ANZ
103 PRC
106 PRC
201 NKL
301 KAR
302 HRO
302 KAR
The compound query above selects the same rows as Figure 5-79 on page 5-33 but
displays them in order of the manufacturer code, as the result shows.
stock_num manu_code
5 ANZ
9 ANZ
302 HRO
301 KAR
302 KAR
201 NKL
5 NRG
103 PRC
106 PRC
5 SMT
The query uses the UNION ALL keywords to unite two SELECT statements and
adds an INTO TEMP clause after the final SELECT to put the results into a
temporary table. It returns the same rows as Figure 5-81 on page 5-34 but also
includes duplicate values.
stock_num manu_code
9 ANZ
5 ANZ
9 ANZ
5 ANZ
9 ANZ
.
.
.
5 NRG
5 NRG
103 PRC
106 PRC
5 SMT
5 SMT
The following query selects the state column from the customer table and the
corresponding code column from the state table.
The query returns state code abbreviations for customer numbers 120 through 125
and for states whose sname ends in a.
AK
AL
AZ
CA
DE
.
.
.
SD
VA
WV
In compound queries, the column names or display labels in the first SELECT
statement are the ones that appear in the results. Thus, in the query, the column
name state from the first SELECT statement is used instead of the column name
code from the second.
The query selects items where the unit_price in the stock table is greater than
$600, the catalog_num in the catalog table is 10025, or the quantity in the items
table is 10; and the query orders the data by manu_code. The result shows the
return values.
stock_num manu_code
5 ANZ
9 ANZ
8 ANZ
4 HSK
1 HSK
203 NKL
5 NRG
106 PRC
113 SHM
The query creates a list in which the customers from California appear first.
sortkey 1
lname Baxter
fname Dick
company Blue Ribbon Sports
city Oakland
state CA
phone 415-655-0011
sortkey 1
lname Beatty
fname Lana
company Sportstown
city Menlo Park
state CA
phone
. 415-356-9982
.
.
sortkey 2
lname Wallack
fname Jason
company City Sports
city Wilmington
state DE
phone 302-366-7511
A FIRST clause
You can use the FIRST clause to select the first rows that result from a union query.
The following query uses a FIRST clause to return the first five rows of a union
between the stock and items tables.
5 NRG
5 ANZ
6 SMT
6 ANZ
9 ANZ
Intersection
The intersection of two sets of rows produces a table that contains rows that exist in
both the original tables. Use the keyword EXISTS or IN to introduce subqueries
that show the intersection of two sets. The following figure illustrates the
intersection set operation.
The following query is an example of a nested SELECT statement that shows the
intersection of the stock and items tables. The result contains all the elements that
appear in both sets and returns the following rows.
1 HRO $250.00
1 HSK $800.00
1 SMT $450.00
2 HRO $126.00
3 HSK $240.00
3 SHM $280.00
.
.
.
306 SHM $190.00
307 PRC $250.00
309 HRO $40.00
309 SHM $40.00
Difference
The difference between two sets of rows produces a table that contains rows in the
first set that are not also in the second set. Use the keywords NOT EXISTS or NOT
IN to introduce subqueries that show the difference between two sets. The
following figure illustrates the difference set operation.
The following query is an example of a nested SELECT statement that shows the
difference between the stock and items tables.
The result contains all the elements from only the first set, which returns 17 rows.
Summary
This chapter builds on concepts introduced in Chapter 2, “Compose SELECT
statements,” on page 2-1. It provides sample syntax and results for more advanced
kinds of SELECT statements, which are used to query a relational database. This
chapter presents the following material:
v Introduces the GROUP BY and HAVING clauses, which you can use with
aggregates to return groups of rows and apply conditions to those groups
v Shows how to join a table to itself with a self-join to compare values in a column
with other values in the same column and to identify duplicates
v Explains how an outer join treats two or more tables asymmetrically, and
provides examples of the four kinds of outer join using both the IBM Informix
extension and ANSI join syntax.
v Describes how to nest a SELECT statement in the WHERE clause of another
SELECT statement to create correlated and uncorrelated subqueries and shows
how to use aggregate functions in subqueries
v Describes how to nest SELECT statements in the FROM clause of another
SELECT statement to specify uncorrelated subqueries whose results are a data
source for the outer SELECT statement
v Demonstrates how to use the keywords ALL, ANY, EXISTS, IN, and SOME to
create subqueries, and the effect of adding the keyword NOT or a relational
operator
v Describes how to use collection subqueries to convert relational data to a
collection of type MULTISET and how to use collection-derived tables to access
elements within a collection
v Discusses the union, intersection, and difference set operations
v Shows how to use the UNION and UNION ALL keywords to create compound
queries that consist of two or more SELECT statements
Although these SQL statements are relatively simple when compared with the
more advanced SELECT statements, use them carefully because they change the
contents of the database.
Think about what happens if the system hardware or software fails during a query.
Even if the effect on the application is severe, the database itself is unharmed.
However, if the system fails while a modification is under way, the state of the
database is in doubt. Obviously, a database in an uncertain state has far-reaching
implications. Before you delete, insert, or update rows in a database, ask yourself
the following questions:
v Is user access to the database and its tables secure; that is, are specific users
given limited database and table-level privileges?
v Does the modified data preserve the existing integrity of the database?
v Are systems in place that make the database relatively immune to external
events that might cause system or hardware failures?
If you cannot answer yes to each of these questions, do not panic. Solutions to all
these problems are built into the IBM Informix database servers. After a
description of the statements that modify data, this section discusses these
solutions. The IBM Informix Database Design and Implementation Guide covers these
topics in greater detail.
Delete rows
The DELETE statement removes any row or combination of rows from a table. You
cannot recover a deleted row after the transaction is committed. (Transactions are
discussed under “Interrupted modifications” on page 6-33. For now, think of a
transaction and a statement as the same thing.)
When you delete a row, you must also be careful to delete any rows of other tables
whose values depend on the deleted row. If your database enforces referential
constraints, you can use the ON DELETE CASCADE option of the CREATE TABLE
or ALTER TABLE statements to allow deletes to cascade from one table in a
relationship to another. For more information on referential constraints and the ON
DELETE CASCADE option, refer to “Referential integrity” on page 6-24.
You can write DELETE statements with or without the FROM keyword.
DELETE customer;
Because these DELETE statements do not contain a WHERE clause, all rows from
the customer table are deleted. If you attempt an unconditional delete using the
DB-Access menu options, the program warns you and asks for confirmation.
However, an unconditional DELETE from within a program can occur without
warning.
If you want to delete rows from a table named from, you must first set the
DELIMIDENT environment variable, or qualify the name of the table with the name
of its owner:
DELETE legree.from;
Removing rows with the TRUNCATE statement is faster than removing them with
the DELETE statement. It is not necessary to run the UPDATE STATISTICS
statement immediately after the TRUNCATE statement. After TRUNCATE executes
successfully, IBM Informix automatically updates the statistics and distributions for
the table and for its indexes in the system catalog to show no rows in the table or
in its dbspace partitions.
If the table that the TRUNCATE statement specifies is a typed table, a successful
TRUNCATE operation removes all the rows and B-tree structures from that table
and from all its subtables within the table hierarchy. TRUNCATE has no equivalent
to the ONLY keyword of the DELETE statement to restricts the operation to a
single table within the typed table hierarchy.
IBM Informix always logs the TRUNCATE operation, even for a non-logging table.
In databases that support transaction logging, only the COMMIT WORK or
In this example, because the customer_num column has a unique constraint, you
can ensure that no more than one row is deleted.
Because the column that is tested does not have a unique constraint, this statement
might delete more than one row. (Druid Cyclery might have two stores, both with
the same name but different customer numbers.)
To find out how many rows a DELETE statement affects, select the count of
qualifying rows from the customer table for Druid Cyclery.
SELECT COUNT(*) FROM customer WHERE company = ’Druid Cyclery’;
You can also select the rows and display them to ensure that they are the ones you
want to delete.
Although it is not likely that other users would do these things in that brief
interval, the possibility does exist. This same problem affects the UPDATE
statement. Ways of addressing this problem are discussed under “Concurrency and
locks” on page 6-36, and in greater detail in Chapter 10, “Programming for a
multiuser environment,” on page 10-1.
Another problem you might encounter is a hardware or software failure before the
statement finishes. In this case, the database might have deleted no rows, some
rows, or all specified rows. The state of the database is unknown, which is
undesirable. To prevent this situation, use transaction logging, as “Interrupted
modifications” on page 6-33 discusses.
In the preceding statement, the address column might be a named ROW type or
an unnamed ROW type. The syntax you use to specify field values of a ROW type
is the same.
To limit a delete to rows of the supertable only, you must use the ONLY keyword
in the DELETE statement. For example, the following statement deletes rows of the
person table only:
DELETE FROM ONLY(person)
WHERE name =’Walker’;
Important: Use caution when you delete rows from a supertable because the scope
of a delete on a supertable includes the supertable and all its subtables.
Suppose you discover that some rows of the stock table contain incorrect
manufacturer codes. Rather than update them, you want to delete them so that
they can be re-entered. You know that these rows, unlike the correct ones, have no
matching rows in the manufact table. The fact that these incorrect rows have no
matching rows in the manufact table allows you to write a DELETE statement
such as the one in the following example:
The subquery counts the number of rows of manufact that match; the count is 1
for a correct row of stock and 0 for an incorrect one. The latter rows are chosen for
deletion.
The WHERE clause of a DELETE statement cannot use a subquery that tests the
same table. That is, when you delete from stock, you cannot use a subquery in the
WHERE clause that also selects from stock.
The key to this rule is in the FROM clause. If a table is named in the FROM clause
of a DELETE statement, it cannot also appear in the FROM clause of a subquery of
the DELETE statement.
As in the previous example, suppose you discover that some rows of the stock
table contain incorrect manufacturer codes. Rather than update them, you want to
delete them so that they can be re-entered. You can use the MERGE statement that
specifies stock as the target table, manufact as the source table, a join condition in
the ON clause, and with the Delete clause for the stock rows with incorrect
manufacturer codes, as in the following example:
MERGE INTO stock USING manufact
ON stock.manu_code != manufact.manu_code
WHEN MATCHED THEN DELETE;
In this example, all the rows of the stock table for which the join condition in the
ON clause is satisfied will be deleted. Here the inequality predicate in the join
condition (stock.manu_code != manufact.manu_code) evaluates to true for the rows
of stock in which the manu_code column value is not equal to any manu_code
value in the manufact table.
The source table that is being joined to the target table must be listed in the USING
clause.
The MERGE statement can also update rows of the target table, or insert data from
the source table into the target table, according to whether or not the row satisfies
the condition that the ON clause specifies for joining the target and source tables.
A single MERGE statement can also combine both DELETE and INSERT
operations, or can combine both UPDATE and INSERT operations without deleting
any rows. The source table is unchanged by the MERGE statement. For more
Insert rows
The INSERT statement adds a new row, or rows, to a table. The statement has two
basic functions. It can create a single new row using column values you supply, or
it can create a group of new rows using data selected from other tables.
Single rows
In its simplest form, the INSERT statement creates one new row from a list of
column values and puts that row in the table. The following statement shows how
to add a row to the stock table:
INSERT INTO stock
VALUES (115, ’PRC’, ’tire pump’, 108, ’box’, ’6/box’);
The values that are listed in the VALUES clause in the preceding example have a
one-to-one correspondence with the columns of the stock table. To write a VALUES
clause, you must know the columns of the tables as well as their sequence from
first to last.
Restriction: Do not specify the currency symbols for columns that contain money
values. Just specify the numeric value of the amount.
The database server can convert between numeric and character data types. You
can give a string of numeric characters (for example, ’-0075.6') as the value of a
numeric column. The database server converts the numeric string to a number. An
error occurs only if the string does not represent a number.
You can specify a number or a date as the value for a character column. The
database server converts that value to a character string. For example, if you
specify TODAY as the value for a character column, a character string that
represents the current date is used. (The DBDATE environment variable specifies the
format that is used.)
When you insert values, specify the value zero for the serial column. The database
server generates the next actual value in sequence. Serial columns do not allow
NULL values.
You can specify a nonzero value for a serial column (as long as it does not
duplicate any existing value in that column), and the database server uses the
value. That nonzero value might set a new starting point for values that the
database server generates. (The next value the database server generates for you is
one greater than the maximum value in the column.)
You can list the columns in any order, as long as the values for those columns are
listed in the same order. For information about how to designate null or default
values for a column, see the IBM Informix Database Design and Implementation Guide.
After the INSERT statement in the preceding example is executed, the following
new row is inserted into the stock table:
stock_num manu_code description unit_price unit unit_descr
Both unit and unit_descr are blank, which indicates that NULL values exist in
those two columns. Because the unit column permits NULL values, the number of
tire pumps that can be purchased for $114 is not known. Of course, if a default
value of box were specified for this column, then box would be the unit of
measure. In any case, when you insert values into specific columns of a table, pay
attention to what data is needed for that row.
When a typed table contains a row-type column (the named ROW type that
defines the typed table contains a nested ROW type), you insert into the row-type
column in the same way you insert into a row-type column for a table not based
on a ROW type. The following section, “Syntax rules for inserts on columns” on
page 6-9, describes how to perform inserts into row-type columns.
This section uses row types zip_t, address_t, and employee_t and typed table
employee for examples. The following figure shows the SQL syntax that creates
the row types and table.
Figure 6-1. SQL syntax that creates the row types and table.
Because the address column of the employee table is a named ROW type, you
must use a cast operator and the name of the ROW type (address_t) to insert a
value of type address_t.
The following statement shows you how to add a row to the student table. To
insert into the unnamed row-type column s_address, use the ROW constructor but
do not cast the row-type value.
INSERT INTO student
VALUES (’Keene, Terry’,
ROW(’53 Terra Villa’, ’Wheeling’, ’IL’, ’45052’),
3.75);
The following statement specifies a NULL value at the column level to insert
NULL values for all fields of the s_address column. When you insert a NULL
value at the column level, do not include the ROW constructor.
INSERT INTO student VALUES (’Brauer, Howie’, NULL, 3.75);
When you insert a NULL value for particular fields of a ROW type, you must
include the ROW constructor. The following INSERT statement shows how you
might insert NULL values into particular fields of the address column of the
employee table. (The address column is defined as a named ROW type.)
INSERT INTO employee
VALUES (
’Singer, John’,
ROW(NULL, ’Davis’, ’CA’,
ROW(97000, 2000))::address_t, 67000
);
When you specify a NULL value for the field of a ROW type, you do not need to
explicitly cast the NULL value when the ROW type occurs in an INSERT
statement, an UPDATE statement, or a program variable assignment.
The following INSERT statement shows how you insert NULL values for the street
and zip fields of the s_address column for the student table:
INSERT INTO student
VALUES(
’Henry, John’,
ROW(NULL, ’Seattle’, ’WA’, NULL), 3.82
);
The examples that this section provides are based on the manager table in the
following figure. The manager table contains both simple and nested collection
types.
In general, NULL values are not allowed in a collection. However, if the element
type of the collection is a ROW type, you can insert NULL values into individual
fields of the row type.
You can also specify an empty collection. An empty collection is a collection that
contains no elements. To specify an empty collection, use the braces ({}). For
example, the following statement inserts data into a row in the manager table but
specifies that the direct_reports and projects columns are empty collections:
INSERT INTO manager
VALUES (’Sayles’, ’marketing’, "SET{}",
"LIST{ROW(NULL, SET{})}"
);
The following syntax rules apply for performing inserts and updates on collection
types:
v Use braces ({}) to demarcate the elements that each collection contains.
v If the collection is a nested collection, use braces ({}) to demarcate the elements
of both the inner and outer collections.
The following INSERT statement uses the filetoblob() and filetoclob() functions to
insert a row of the inmate table. (Figure 4-53 on page 4-14 defines the inmate
table.)
INSERT INTO inmate
VALUES (437, FILETOBLOB(’datafile’, ’client’),
FILETOCLOB(’tmp/text’, ’server’));
In the preceding example, the first argument for the FILETOBLOB() and
FILETOCLOB() functions specifies the path of the source file to be copied into the
BLOB and CLOB columns of the inmate table, respectively. The second argument
for each function specifies whether the source file is located on the client computer
('client') or server computer ('server'). To specify the path of a file name in the
function argument, apply the following rules:
v If the source file resides on the server computer, you must specify the full path
name to the file (not the path name relative to the current working directory).
For example, suppose a follow-up call is required for every order that has been
paid for but not shipped. The INSERT statement in the following example finds
those orders and inserts a row in cust_calls for each order:
INSERT INTO cust_calls (customer_num, call_descr)
SELECT customer_num, order_num FROM orders
WHERE paid_date IS NOT NULL
AND ship_date IS NULL;
This SELECT statement returns two columns. The data from these columns (in
each selected row) is inserted into the named columns of the cust_calls table. Then
an order number (from order_num, a SERIAL column) is inserted into the call
description, which is a character column. Remember that the database server
allows you to insert integer values into a character column. It automatically
converts the serial number to a character string of decimal digits.
The INTO, INTO TEMP, and ORDER BY clause restrictions are minor. The INTO
clause is not useful in this context. (For more information, see Chapter 8, “SQL
programming,” on page 8-1.) To work around the INTO TEMP clause restriction,
first select the data you want to insert into a temporary table and then insert the
data from the temporary table with the INSERT statement. Likewise, the lack of an
ORDER BY clause is not important. If you need to ensure that the new rows are
physically ordered in the table, you can first select them into a temporary table
and order it, and then insert from the temporary table. You can also apply a
physical order to the table using a clustered index after all insertions are done.
Important: The last restriction is more serious because it prevents you from
naming the same table in both the INTO clause of the INSERT statement and the
FROM clause of the SELECT statement. Naming the same table in both the INTO
clause of the INSERT statement and the FROM clause of the SELECT statement
causes the database server to enter an endless loop in which each inserted row is
reselected and reinserted.
To get around this restriction, select the data you want to insert into a temporary
table. Then select from that temporary table in the INSERT statement, as the
following example shows:
SELECT stock_num, ’NIK’ temp_manu, description, unit_price/2
half_price, unit, unit_descr FROM stock
WHERE manu_code = ’ANZ’
AND stock_num < 110
INTO TEMP anzrows;
This SELECT statement takes existing rows from stock and substitutes a literal
value for the manufacturer code and a computed value for the unit price. These
rows are then saved in a temporary table, anzrows, which is immediately inserted
into the stock table.
When you insert multiple rows, a risk exists that one of the rows contains invalid
data that might cause the database server to report an error. When such an error
occurs, the statement terminates early. Even if no error occurs, a small risk exists
that a hardware or software failure might occur while the statement is executing
(for example, the disk might fill up).
In either event, you cannot easily tell how many new rows were inserted. If you
repeat the statement in its entirety, you might create duplicate rows, or you might
not. Because the database is in an unknown state, you cannot know what to do.
The solution lies in using transactions, as “Interrupted modifications” on page 6-33
discusses.
Update rows
Use the UPDATE statement to change the contents of one or more existing rows of
a table, according to the specifications of the SET clause. This statement takes two
fundamentally different forms. One lets you assign specific values to columns by
name; the other lets you assign a list of values (that might be returned by a
SELECT statement) to a list of columns. In either case, if you are updating rows,
and some of the columns have data integrity constraints, the data that you change
must conform to the constraints placed on those columns. For more information,
refer to “Data integrity” on page 6-23.
The WHERE clause selects the row you want to update. In the demonstration
database, the customer.customer_num column is the primary key for that table, so
this statement can update no more than one row.
You can also use subqueries in the WHERE clause. Suppose that the Anza
Corporation issues a safety recall of their tennis balls. As a result, any unshipped
orders that include stock number 6 from manufacturer ANZ must be put on back
order, as the following example shows:
UPDATE orders
SET backlog = ’y’
WHERE ship_date IS NULL
AND order_num IN
(SELECT DISTINCT items.order_num FROM items
WHERE items.stock_num = 6
AND items.manu_code = ’ANZ’);
This subquery returns a column of order numbers (zero or more). The UPDATE
operation then tests each row of orders against the list and performs the update if
that row matches.
You can also use a subquery as part of the assigned value. When a subquery is
used as an element of an expression, it must return exactly one value (one column
and one row). Perhaps you decide that for any stock number, you must charge a
The first SELECT statement returns a single value: the highest price in the stock
table for a particular product. The first SELECT statement is a correlated subquery
because, when a value from items appears in the WHERE clause for the first
SELECT statement, you must execute the query for every row that you update.
The second SELECT statement produces a list of the order numbers of unshipped
orders. It is an uncorrelated subquery that is executed once.
Restrictions on updates
Restrictions exist on the use of subqueries when you modify data. In particular,
you cannot query the table that is being modified. You can refer to the present
value of a column in an expression, as in the example that increments the
unit_price column by 5 percent. You can also refer to a value of a column in a
WHERE clause in a subquery, as in the example that updated the stock table, in
which the items table is updated and items.stock_num is used in a join
expression.
The need to update and query a table at the same time does not occur often in a
well-designed database. (For more information about database design, see the IBM
Informix Database Design and Implementation Guide.) However, you might want to
update and query at the same time when a database is first being developed,
before its design has been carefully thought through. A typical problem arises
when a table inadvertently and incorrectly contains a few rows with duplicate
values in a column that should be unique. You might want to delete the duplicate
rows or update only the duplicate rows. Either way, a test for duplicate rows
inevitably requires a subquery on the same table that you want to modify, which is
not allowed in an UPDATE statement or DELETE statement. Chapter 9, “Modify
data through SQL programs,” on page 9-1 discusses how to use an update cursor to
perform this kind of modification.
No advantage exists to writing the statement this way. In fact, it is harder to read
because it is not obvious which values are assigned to which columns.
A single SELECT statement produces the values for multiple columns. If you
rewrite this example in the other form, with an assignment for each updated
column, you must write five SELECT statements, one for each column to be
updated. Not only is such a statement harder to write, but it also takes much
longer to execute.
Tip: In SQL API programs, you can use record or host variables to update values.
For more information, refer to Chapter 8, “SQL programming,” on page 8-1.
In this example, the values of the state and zip fields are read from and then
immediately reinserted into the row. Only the street and city fields of the address
column are updated.
When you update the fields of a column that are defined on a named ROW type,
you must use a ROW constructor and cast the row value to the appropriate named
ROW type.
To update the fields of a column that are defined on an unnamed ROW type,
always specify the ROW constructor before the field values to be inserted.
The following UPDATE statement shows how you might specify NULL values for
particular fields of a named row-type column:
UPDATE employee
SET address = ROW(NULL::VARCHAR(20), ’Davis’, ’CA’,
ROW(NULL::CHAR(5), NULL::CHAR(4)))::address_t)
WHERE name = ’henry, john’;
The following UPDATE statement shows how you specify NULL values for the
street and zip fields of the address column for the student table.
UPDATE student
SET address = ROW(NULL::VARCHAR(20), address.city,
address.state, NULL::VARCHAR(9))
WHERE s_name = ’henry, john’;
Important: You cannot specify NULL values for a row-type column. You can only
specify NULL values for the individual fields of the row type.
The first occurrence of the SET keyword in the preceding statement is part of the
UPDATE statement syntax.
Important: Do not confuse the SET keyword of an UPDATE statement with the
SET constructor that indicates that a collection is a SET data type.
Although you can use the IN keyword to locate specific elements of a simple
collection, you cannot update individual elements of a collection column from
DB-Access. However, you can create Informix ESQL/C programs and SPL routines
to update elements within a collection. For information about how to create an
Informix ESQL/C program to update a collection, see the IBM Informix ESQL/C
When you construct an UPDATE statement on a supertable, you can update all
columns in the supertable and columns of subtables that are inherited from the
supertable. For example, the following statement updates rows from the employee
and sales_rep tables, which are subtables of the supertable person:
UPDATE person
SET salary=65000
WHERE address.state = ’CA’;
However, an update on a supertable does not allow you to update columns from
subtables that are not in the supertable. For example, in the previous update
statement, you cannot update the region_num column of the sales_rep table
because the region_num column does not occur in the employee table.
When you perform updates on supertables, be aware of the scope of the update.
For example, an UPDATE statement on the person table that does not include a
WHERE clause to restrict which rows to update, modifies all rows of the person,
employee, and sales_rep table.
To limit an update to rows of the supertable only, you must use the ONLY
keyword in the UPDATE statement. For example, the following statement updates
rows of the person table only:
UPDATE ONLY(person)
SET address = ROW(’14 Jackson St’, ’Berkeley’,
address.state, address.zip)
WHERE name = ’Sallie, A.’;
Important: Use caution when you update rows of a supertable because the scope
of an update on a supertable includes the supertable and all its subtables.
The following UPDATE statement uses the LOCOPY() function to copy BLOB data
from the mugshot column of the fbi_list table into the picture column of the
inmate table. (Figure 4-53 on page 4-14 defines the inmate and fbi_list tables.)
UPDATE inmate (picture)
SET picture = (SELECT LOCOPY(mugshot, ’inmate’, ’picture’)
FROM fbi_list WHERE fbi_list.id = 669)
WHERE inmate.id_num = 437;
The first argument for LOCOPY() specifies the column (mugshot) from which the
object is exported. The second and third arguments specify the name of the table
(inmate) and column (picture) whose storage characteristics the newly created
object will use. After execution of the UPDATE statement, the picture column
contains data from the mugshot column.
When you specify the path of a file name in the function argument, apply the
following rules:
v If the source file resides on the server computer, you must specify the full path
name to the file (not the path name relative to the current working directory).
v If the source file resides on the client computer, you can specify either the full or
relative path name to the file.
The following example illustrates how you can use the Update clause of the
MERGE statement to update a target table:
MERGE INTO t_target AS t USING t_source AS s ON t.col_a = s.col_a
WHEN MATCHED THEN UPDATE
SET t.col_b = t.col_b + s.col_b ;
In the preceding example, the name of the target table is t_target and the name of
the source table is t_source. For rows of the join result where col_a has the same
value in both the source and the target tables, the MERGE statement updates the
t_target table by adding the value of column col_b in the source table to the
current value of the col_b column in the t_target table.
An UPDATE operation of the MERGE statement does not modify the source table,
and cannot update any row in the target table more than once.
This section briefly describes database- and table-level privileges. For more
information about database privileges, see the IBM Informix Database Design and
Implementation Guide. For a list of privileges and a description of the GRANT and
REVOKE statements, see the IBM Informix Guide to SQL: Syntax.
Database-level privileges
When you create a database, you are the only one who can access it until you, as
the owner or database administrator (DBA) of the database, grant database-level
privileges to others. The following table shows database-level privileges.
Privilege Effect
Connect Allows you to open a database, issue queries,
and create and place indexes on temporary
tables.
Resource Allows you to create permanent tables.
DBA Allows you to perform several additional
functions as the DBA.
Table-level privileges
When you create a table in a database that is not ANSI compliant, all users have
access privileges to the table until you, as the owner of the table, revoke table-level
privileges from specific users. The following table introduces the four privileges
that govern how users can access a table.
Privilege Purpose
Select Granted on a table-by-table basis and allows
you to select rows from a table. (This
privilege can be limited to specific columns
in a table.)
Delete Allows you to delete rows.
Insert Allows you to insert rows.
The people who create databases and tables often grant the Connect and Select
privileges to public so that all users have them. If you can query a table, you have
at least the Connect and Select privileges for that database and table.
You need the other table-level privileges to modify data. The owners of tables
often withhold these privileges or grant them only to specific users. As a result,
you might not be able to modify some tables that you can query freely.
Because these privileges are granted on a table-by-table basis, you can have only
Insert privileges on one table and only Update privileges on another, for example.
The Update privileges can be restricted even further to specific columns in a table.
For more information on these and other table-level privileges, see the IBM
Informix Database Design and Implementation Guide.
The grantor is the user who grants the privilege. The grantor is usually the owner
of the table but the owner can be another user that the grantor empowered. The
grantee is the user to whom the privilege is granted, and the grantee public means
any user with Connect privilege. If your user name does not appear, you have only
those privileges granted to public.
The tabauth column specifies the privileges granted. The letters in each row of this
column are the initial letters of the privilege names, except that i means Insert and
x means Index. In this example, public has Select, Insert, and Index privileges.
Only the user mutator has Update privileges, and only the user procrustes has
Delete privileges.
Before the database server performs any action for you (for example, execution of a
DELETE statement), it performs a query similar to the preceding one. If you are
Default roles automatically apply upon connection to the database for particular
users and groups, without requiring the user to issue a SET ROLE statement. For
example:
GRANT DEFAULT ROLE manager TO larry;
For more information on roles and default roles, see “Control database use” on
page 1-5 or see the IBM Informix Administrator's Guide.
For more information on granting and revoking privileges, see “Grant and revoke
privileges in applications” on page 8-21. Also see IBM Informix Database Design and
Implementation Guide.
Data integrity
The INSERT, UPDATE, and DELETE statements modify data in an existing
database. Whenever you modify existing data, the integrity of the data can be
affected. For example, an order for a nonexistent product could be entered into the
orders table, a customer with outstanding orders could be deleted from the
customer table, or the order number could be updated in the orders table and not
in the items table. In each of these cases, the integrity of the stored data is lost.
Entity integrity
An entity is any person, place, or thing to be recorded in a database. Each table
represents an entity, and each row of a table represents an instance of that entity.
For example, if order is an entity, the orders table represents the idea of an order
and each row in the table represents a specific order.
For example, the orders table primary key is order_num. The order_num column
holds a unique system-generated order number for each row in the table. To access
a row of data in the orders table, use the following SELECT statement:
SELECT * FROM orders WHERE order_num = 1001;
Using the order number in the WHERE clause of this statement enables you to
access a row easily because the order number uniquely identifies that row. If the
table allowed duplicate order numbers, it would be almost impossible to access
one single row because all other columns of this table allow duplicate values.
For more information on primary keys and entity integrity, see the IBM Informix
Database Design and Implementation Guide.
Semantic integrity
Semantic integrity ensures that data entered into a row reflects an allowable value
for that row. The value must be within the domain, or allowable set of values, for
that column. For example, the quantity column of the items table permits only
numbers. If a value outside the domain can be entered into a column, the semantic
integrity of the data is violated.
Referential integrity
Referential integrity refers to the relationship between tables. Because each table in
a database must have a primary key, this primary key can appear in other tables
because of its relationship to data within those tables. When a primary key from
one table appears in another table, it is called a foreign key.
Foreign keys join tables and establish dependencies between tables. tables can form
a hierarchy of dependencies in such a way that if you change or delete a row in
one table, you destroy the meaning of rows in other tables. For example, the
following figure shows that the customer_num column of the customer table is a
primary key for that table and a foreign key in the orders and cust_call tables.
When you delete a row that contains a primary key or update it with a different
primary key, you destroy the meaning of any rows that contain that value as a
foreign key. Referential integrity is the logical dependency of a foreign key on a
primary key. The integrity of a row that contains a foreign key depends on the
integrity of the row that it references—the row that contains the matching primary
key.
By default, the database server does not allow you to violate referential integrity
and gives you an error message if you attempt to delete rows from the parent table
before you delete rows from the child table. You can, however, use the ON
DELETE CASCADE option to cause deletes from a parent table to trip deletes on
child tables. See “The ON DELETE CASCADE option.”
To define primary and foreign keys, and the relationship between them, use the
CREATE TABLE and ALTER TABLE statements. For more information on these
statements, see the IBM Informix Guide to SQL: Syntax. For information about how
to build a data model with primary and foreign keys, see the IBM Informix Database
Design and Implementation Guide.
During deletes, locks are held on all qualifying rows of the parent and child tables.
When you specify a delete, the delete that is requested from the parent table occurs
before any referential actions are performed.
If you have a parent table with two child constraints, one child with cascading
deletes specified and one child without cascading deletes, and you attempt to
delete a row from the parent table that applies to both child tables, the DELETE
statement fails, and no rows are deleted from either the parent or child tables.
You must turn on logging in your current database for cascading deletes to work.
Logging and cascading deletes are discussed in “Transaction logging” on page
6-34.
The primary key of the accounts table, the acc_num column, uses a SERIAL data
type, and the foreign key of the sub_accounts table, the ref_num column, uses an
INTEGER data type. Combining the SERIAL data type on the primary key and the
INTEGER data type on the foreign key is allowed. Only in this condition can you
mix and match data types. The SERIAL data type is an INTEGER, and the
database automatically generates the values for the column. All other primary and
foreign key combinations must match explicitly. For example, a primary key that is
defined as CHAR must match a foreign key that is defined as CHAR.
The definition of the foreign key of the sub_accounts table, the ref_num column,
includes the ON DELETE CASCADE option. This option specifies that a delete of
any row in the parent table accounts will automatically cause the corresponding
rows of the child table sub_accounts to be deleted.
To delete a row from the accounts table that will cascade a delete to the
sub_accounts table, you must turn on logging. After logging is turned on, you can
delete the account number 2 from both tables, as the following example shows:
DELETE FROM accounts WHERE acc_num = 2;
Database objects, within the context of a discussion of the object modes feature, are
constraints, indexes, and triggers, and each of them have different modes. Do not
confuse database objects that are relevant to the object modes feature with generic
database objects. Generic database objects are things like tables and synonyms.
When a database object is enabled, the database server recognizes the existence of
the database object and takes the database object into consideration while it
executes an INSERT, DELETE, or UPDATE statement. Thus, an enabled constraint
is enforced, an enabled index updated, and an enabled trigger is executed when
the trigger event takes place.
When you enable constraints and unique indexes, if a violating row exists, the data
manipulation statement fails (that is no rows change) and the database server
returns an error message.
You can identify the reason for the failure when you analyze the information in the
violations and diagnostic tables. You can then take corrective action or roll back the
operation.
Disabled mode: When a database object is disabled, the database server does not
take it into consideration during the execution of an INSERT, DELETE, or UPDATE
statement. A disabled constraint is not enforced, a disabled index is not updated,
and a disabled trigger is not executed when the trigger event takes place. When
you disable constraints and unique indexes, any data manipulation statement that
violates the restriction of the constraint or unique index succeed, (that is, the target
row is changed), and the database server does not return an error message.
Assume that user joe defined the lname column as not null but has not assigned a
name to the not null constraint, so the database server has implicitly assigned the
name n104_7 to this constraint. Finally, assume that user joe created a unique
index named unq_ssn on the ssn column.
Now user linda who has the Insert privilege on the cust_subset table enters the
following INSERT statement on this table:
INSERT INTO cust_subset (ssn, fname, city)
VALUES (973824499, "jane", "los altos");
If the NOT NULL constraint on the cust_subset table is enabled, the INSERT
statement fails to insert the new row in this table. Instead user linda receives the
following error message when she enters the INSERT statement:
-292 An implied insert column lname does not accept NULLs.
If the NOT NULL constraint on the cust_subset table is disabled, the INSERT
statement that user linda issues successfully inserts the new row in this table. The
new row of the cust_subset table has the following column values.
If the NOT NULL constraint on the cust_subset table is set to the filtering mode,
the INSERT statement that user linda issues fails to insert the new row in this
table. Instead the new row is inserted into the violations table, and a diagnostic
row that describes the integrity violation is added to the diagnostics table.
Assume that user joe has started a violations and diagnostics table for the
cust_subset table. The violations table is named cust_subset_vio, and the
diagnostics table is named cust_subset_dia. The new row added to the
cust_subset_vio violations table when user linda issues the INSERT statement on
the cust_subset target table has the following column values.
The INSERT statement that user linda issued on the cust_subset target table also
causes a diagnostic row to be added to the cust_subset_dia diagnostics table. The
new diagnostic row added to the diagnostics table has the following column
values.
This new diagnostic row in the cust_subset_dia diagnostics table has the following
characteristics:
v This row of the diagnostics table is linked to the corresponding row of the
violations table by means of the informix_tupleid column that appears in both
tables. The value 1 appears in this column in both tables.
v The value C in the objtype column identifies the type of integrity violation that
the corresponding row in the violations table caused. Specifically, the value C
stands for a constraint violation.
v The value joe in the objowner column identifies the owner of the constraint for
which an integrity violation was detected.
v The value n104_7 in the objname column gives the name of the constraint for
which an integrity violation was detected.
By joining the violations and diagnostics tables, user joe (who owns the
cust_subset target table and its associated special tables) or the DBA can find out
that the row in the violations table whose informix_tupleid value is 1 was created
after an INSERT statement and that this row is violating a constraint. The table
owner or DBA can query the sysconstraints system catalog table to determine that
this constraint is a NOT NULL constraint. Now that the reason for the failure of
the INSERT statement is known, user joe or the DBA can take corrective action.
In the preceding example, only one row in the diagnostics table corresponds to the
new row in the violations table. However, more than one diagnostic row can be
added to the diagnostics table when a single new row is added to the violations
table. For example, if the ssn value (973824499) that user linda entered in the
INSERT statement had been the same as an existing value in the ssn column of the
cust_subset target table, only one new row would appear in the violations table,
but the following two diagnostic rows would be present in the cust_subset_dia
diagnostics table.
Both rows in the diagnostics table correspond to the same row of the violations
table because both of these rows have the value 1 in the informix_tupleid column.
However, the first diagnostic row identifies the constraint violation caused by the
INSERT statement that user linda issued, while the second diagnostic row
identifies the unique-index violation caused by the same INSERT statement. In this
second diagnostic row, the value I in the objtype column stands for a
unique-index violation, and the value unq_ssn in the objname column gives the
name of the index for which the integrity violation was detected.
For more information about how to set database object modes, see the SET
Database object mode statement in the IBM Informix Guide to SQL: Syntax.
After you create a violations table for a target table, you cannot alter the columns
or the fragmentation of the base table or the violations table. If you alter the
constraints on a target table after you have started the violations table,
nonconforming rows will be filtered to the violations table.
For information about how to start and stop the violations tables, see the START
VIOLATIONS TABLE and STOP VIOLATIONS TABLE statements in the IBM
Informix Guide to SQL: Syntax.
Relationship of violations tables and database object modes: If you set the
constraints or unique indexes defined on a table to the filtering mode, but you do
not create the violations and diagnostics tables for this target table, any rows that
violate a constraint or unique-index requirement during an insert, update, or delete
operation are not filtered to a violations table. Instead, you receive an error
message that indicates that you must start a violations table for the target table.
Similarly, if you set a disabled constraint or disabled unique index to the enabled
or filtering mode and you want the ability to identify existing rows that do not
satisfy the constraint or unique-index requirement, you must create the violations
tables before you issue the SET DATABASE OBJECT MODE statement.
The following examples show different ways to execute the START VIOLATIONS
TABLE statement.
Because your START VIOLATIONS TABLE statement does not include a USING
clause, the violations table is named customer_vio by default, and the diagnostics
table is named customer_dia by default. The customer_vio table includes the
following columns:
customer_num
fname
lname
company
address1
address2
city
state
zipcode
phone
informix_tupleid
informix_optype
informix_recowner
The customer_vio table has the same table definition as the customer table except
that the customer_vio table has three additional columns that contain information
about the operation that caused the bad row.
This list of columns shows an important difference between the diagnostics table
and violations table for a target table. Whereas the violations table has a matching
column for every column in the target table, the columns of the diagnostics table
do not match any columns in the target table. The diagnostics table created by any
START VIOLATIONS TABLE statement always has the same columns with the
same column names and data types.
The following statement starts a violations and diagnostics table for the target table
named items. The USING clause assigns explicit names to the violations and
diagnostics tables. The violations table is to be named exceptions, and the
diagnostics table is to be named reasons.
START VIOLATIONS TABLE FOR items
USING exceptions, reasons;
The following statement starts violations and diagnostics tables for the target table
named orders. The MAX ROWS clause specifies the maximum number of rows
that can be inserted into the diagnostics table when a single statement, such as an
INSERT or SET DATABASE OBJECT MODE statement, is executed on the target
table.
START VIOLATIONS TABLE FOR orders MAX ROWS 50000;
The following example illustrates how the initial set of privileges on a violations
table is derived from the current set of privileges on the target table.
For example, assume that we created a table named cust_subset and that this table
consists of the following columns: ssn (customer's social security number), fname
(customer's first name), lname (customer's last name), and city (city in which the
customer lives).
Now user alvin starts a violations table named cust_subset_viols and a diagnostics
table named cust_subset_diags for the cust_subset table, as follows:
START VIOLATIONS TABLE FOR cust_subset
USING cust_subset_viols, cust_subset_diags;
The database server grants the following set of initial privileges on the
cust_subset_viols violations table:
v User alvin is the owner of the violations table, so he has all table-level privileges
on the table.
v User barbara has the Insert, Delete, and Index privileges on the violations table.
She also has the Select privilege on the following columns of the violations table:
the ssn column, the lname column, the informix_tupleid column, the
informix_optype column, and the informix_recowner column.
v User carrie has the Insert and Delete privileges on the violations table. She has
the Update privilege on the following columns of the violations table: the city
column, the informix_tupleid column, the informix_optype column, and the
informix_recowner column. She has the Select privilege on the following
columns of the violations table: the ssn column, the informix_tupleid column,
the informix_optype column, and the informix_recowner column.
v User danny has no privileges on the violations table.
The following example illustrates how the initial set of privileges on a diagnostics
table is derived from the current set of privileges on the target table.
For example, assume that a table called cust_subset consists of the following
columns: ssn (customer's social security number), fname (customer's first name),
lname (customer's last name), and city (city in which the customer lives).
Now user alvin starts a violations table named cust_subset_viols and a diagnostics
table named cust_subset_diags for the cust_subset table, as follows:
START VIOLATIONS TABLE FOR cust_subset
USING cust_subset_viols, cust_subset_diags;
The database server grants the following set of initial privileges on the
cust_subset_diags diagnostics table:
v User alvin is the owner of the diagnostics table, so he has all table-level
privileges on the table.
v User barbara has the Insert, Delete, Select, and Index privileges on the
diagnostics table.
v User carrie has the Insert, Delete, Select, and Update privileges on the
diagnostics table.
v User danny has no privileges on the diagnostics table.
Interrupted modifications
Even if all the software is error-free and all the hardware is utterly reliable, the
world outside the computer can interfere. Lightning might strike the building,
interrupting the electrical supply and stopping the computer in the middle of your
UPDATE statement. A more likely scenario occurs when a disk fills up or a user
supplies incorrect data, causing your multirow insert to stop early with an error. In
any case, whenever you modify data, you must assume that some unforeseen
event can interrupt the modification.
When an external cause interrupts a modification, you cannot be sure how much
of the operation was completed. Even in a single-row operation, you cannot know
whether the data reached the disk or the indexes were properly updated.
The following list describes the correct way to build an order-entry application:
v Accept all the data interactively.
v Validate the data, and expand it (look up codes in stock and manufact, for
example).
Even with these steps, an unforeseen circumstance can halt the program after it
inserts the order but before it finishes inserting the items. If that happens, the
database is in an unpredictable condition: its data integrity is compromised.
Transactions
The solution to all these potential problems is called the transaction. A transaction
is a sequence of modifications that must be accomplished either completely or not
at all. The database server guarantees that operations performed within the bounds
of a transaction are either completely and perfectly committed to disk, or the
database is restored to the same state as before the transaction started.
The transaction is not merely protection against unforeseen failures; it also offers a
program a way to escape when the program detects a logical error.
Transaction logging
The database server can keep a record of each change that it makes to the database
during a transaction. If something happens to cancel the transaction, the database
server automatically uses the records to reverse the changes. Many things can
make a transaction fail. For example, the program that issues the SQL statements
can fail or be terminated. As soon as the database server discovers that the
transaction failed, which might be only after the computer and the database server
are restarted, it uses the records from the transaction to return the database to the
same state as before.
IBM Informix supports nonlogging tables for fast loads of very large tables. It is
recommended that you do not use nonlogging tables within a transaction. To avoid
concurrency problems, use the ALTER TABLE statement to make the table
standard (that is, logging) before you use the table in a transaction.
For more information about nonlogging tables for IBM Informix, see the IBM
Informix Administrator's Guide. For the performance advantages of nonlogging
tables, see the IBM Informix Performance Guide. For information about the ALTER
TABLE statement, see the IBM Informix Guide to SQL: Syntax.
IBM Informix allows you to turn on logging with the WITH LOG clause in the
CREATE DATABASE statement.
Specify transactions
You can use two methods to specify the boundaries of transactions with SQL
statements. In the most common method, you specify the start of a multistatement
transaction by executing the BEGIN WORK statement. In databases that are created
with the MODE ANSI option, no need exists to mark the beginning of a
transaction. One is always in effect; you indicate only the end of each transaction.
If any external failure prevents the transaction from being completed, the partial
transaction rolls back when the system restarts. In all cases, the database is in a
predictable state. Either the new order is completely entered, or it is not entered at
all.
The database server contains elaborate features to support backups and logging.
Your database server archive and backup guide describes these features.
The database server has stringent requirements for performance and reliability (for
example, it supports making backup copies while databases are in use).
The database server manages its own disk space, which is devoted to logging.
The database server performs logging concurrently for all databases using a
limited set of log files. The log files can be copied to another medium (backed up)
while transactions are active.
Database users never have to be concerned with these facilities because the DBA
usually manages them from a central location.
IBM Informix supports the onload and onunload utilities. Use the onunload utility
to make a personal backup copy of a single database or table. This program copies
a table or a database to tape. Its output consists of binary images of the disk pages
as they were stored in the database server. As a result, the copy can be made
quickly, and the corresponding onload program can restore the file quickly.
However, the data format is not meaningful to any other programs. For
information about how to use the onload and onunload utilities, see the IBM
Informix Migration Guide.
If your DBA uses ON-Bar to create backups and back up logical logs, you might
also be able to create your own backup copies using ON-Bar. For more
information, see your IBM Informix Backup and Restore Guide.
To prevent errors of this kind, the database server imposes a system of locks. A lock
is a claim, or reservation, that a program can place on a piece of data. The
database server guarantees that, as long as the data is locked, no other program
can modify it. When another program requests the data, the database server either
makes the program wait or turns it back with an error.
To control the effect that locks have on your data access, use a combination of SQL
statements: SET LOCK MODE and either SET ISOLATION or SET
These advantages do not come without a cost. Data replication obviously requires
more storage for replicated data than for unreplicated data, and updating
replicated data can take more processing time than updating a single object.
Within the broad framework of data replication, an IBM Informix database server
implements nearly transparent data replication of entire database servers. All the
data that one database server manages is replicated and dynamically updated on
another database server, usually at a remote site. Data replication of an IBM
Informix database server is sometimes called hot-site backup, because it provides a
means of maintaining a backup copy of the entire database server that can be used
quickly in the event of a catastrophic failure.
If data integrity constraints are imposed on the database, your ability to modify
data is restricted by those constraints. Your database- and table-level privileges and
any data constraints control how and when you can modify data. In addition, the
object modes and violation detection features of the database affect how you can
modify data and help to preserve the integrity of your data.
You can delete one or more rows from a table with the DELETE statement. Its
WHERE clause selects the rows; use a SELECT statement with the same clause to
preview the deletes.
Rows are added to a table with the INSERT statement. You can insert a single row
that contains specified column values, or you can insert a block of rows that a
SELECT statement generates.
Use the UPDATE statement to modify the contents of existing rows. You specify
the new contents with expressions that can include subqueries, so that you can use
data that is based on other tables or the updated table itself. The statement has two
forms. In the first form, you specify new values column by column. In the second
form, a SELECT statement or a record variable generates a set of new values.
Use the REFERENCES clause of the CREATE TABLE and ALTER TABLE
statements to create relationships between tables. The ON DELETE CASCADE
option of the REFERENCES clause allows you to delete rows from parent and
associated child tables with one DELETE statement.
When the external database is on the same database server as the current database,
you must qualify the object name with the database name and a colon. For
example, to refer to a table in a database other than the local database, the
following SELECT statement accesses information from an external database:
SELECT name, number FROM salesdb:contacts
In this example, the query returns data from the table, contacts, that is in the
database, salesdb.
A remote database server is any database server that is not the current database
server. When the external database is on a remote database server, you must
qualify the name of the database object with the database server name and the
database name, as the following example illustrates:
SELECT name, number FROM salesdb@distantserver:contacts
In this example, the query returns data from the table, contacts, that is in the
database, salesdb on the remote database server, distantserver.
For the syntax and rules on how to specify database object names in an external
database, see the IBM Informix Guide to SQL: Syntax.
Tip: You can always over-qualify an object name. That is, you can specify the full
object name, database@servername:ownername.objectname, even in situations that
do not require the full object name.
For more information about ANSI-compliant databases, refer to the IBM Informix
Database Design and Implementation Guide.
To run each statement successfully across databases or database servers, the local
and external databases must have the same logging mode.
Distributed operations that use SQL statements or UDRs to access other databases
of the local IBM Informix instance, however, can also return the opaque built-in
data types BLOB, BOOLEAN, CLOB, and LVARCHAR. They can also access
DISTINCT types based on built-in types, as well as UDTs that can be cast to
built-in types, provided that the DISTINCT or UDT values are explicitly cast to
built-in types, and that all the DISTINCT types, UDTs, and casts are defined in all
of the participating databases.
Distributed operations that access databases of other Informix instances can access
or return values of the following data types:
Of course, that is not the case. Many layers of software stand between you and the
database server. The database server retains data in a binary form that must be
formatted before it can be displayed. It does not return a mass of data at once; it
returns one row at a time, as a program requests it.
You can access information in your database through interactive access with
DB-Access, through application programs written with an SQL API such as
Informix ESQL/C, or through an application language such as SPL.
Almost any program can contain SQL statements, execute them, and retrieve data
from a database server. This chapter explains how these activities are performed
and indicates how you can write programs that perform them.
This chapter introduces concepts that are common to SQL programming in any
language. Before you can write a successful program in a particular programming
language, you must first become fluent in that language. Then, because the details
of the process are different in every language, you must become familiar with the
publication for the IBM Informix SQL API specific to that language.
SQL in programs
You can write a program in any of several languages and mix SQL statements
among the other statements of the program, just as if they were ordinary
statements of that programming language. These SQL statements are embedded in
the program, and the program contains embedded SQL, which is often abbreviated
as ESQL.
The following figure shows how an SQL API product works. You write a source
program in which you treat SQL statements as executable code. Your source
program is processed by an embedded SQL preprocessor, a program that locates the
embedded SQL statements and converts them into a series of procedure calls and
special data structures.
The converted source program then passes through the programming language
compiler. The compiler output becomes an executable program after it is linked
with a static or dynamic library of SQL API procedures. When the program runs,
the SQL API library procedures are called; they set up communication with the
database server to carry out the SQL operations.
If you link your executable program to a threading library package, you can
develop Informix ESQL/C multithreaded applications. A multithreaded application
can have many threads of control. It separates a process into multiple execution
threads, each of which runs independently. The major advantage of a
multithreaded Informix ESQL/C application is that each thread can have many
active connections to a database server simultaneously. While a nonthreaded
Informix ESQL/C application can establish many connections to one or more
databases, it can have only one connection active at a time. A multithreaded
Informix ESQL/C application can have one active connection per thread and many
threads per application.
For more information on multithreaded applications, see the IBM Informix ESQL/C
Programmer's Manual.
Static embedding
You can introduce SQL statements into a program through static embedding or
dynamic statements. The simpler and more common way is by static embedding,
which means that the SQL statements are written as part of the code. The
statements are static because they are a fixed part of the source text. For more
information on static embedding, see “Retrieve single rows” on page 8-8 and
“Retrieve multiple rows” on page 8-11.
Dynamic statements
Some applications require the ability to compose SQL statements dynamically, in
response to user input. For example, a program might have to select different
columns or apply different criteria to rows, depending on what the user wants.
CREATE
. PROCEDURE delete_item (drop_number INT)
.
.
DELETE
. FROM items WHERE order_num = drop_number
.
.
In applications that use embedded SQL statements, the SQL statements can refer to
the contents of program variables. A program variable that is named in an
embedded SQL statement is called a host variable because the SQL statement is
thought of as a guest in the program.
The statement contains one new feature. It compares the order_num column to an
item written as :onum, which is the name of a host variable.
An SQL API product provides a way to delimit the names of host variables when
they appear in the context of an SQL statement. In Informix ESQL/C, a host
variable can be introduced with either a dollar sign ($) or a colon (:). The colon is
the ANSI-compatible format. The example statement asks the database server to
delete rows in which the order number equals the current contents of the host
variable named :onum. This numeric variable was declared and assigned a value
earlier in the program.
The differences of syntax as illustrated in the preceding examples are trivial; the
essential point is that the SQL API and SPL languages let you perform the
following tasks:
v Embed SQL statements in a source program as if they were executable
statements of the host language.
v Use program variables in SQL expressions the way literal values are used.
If you have programming experience, you can immediately see the possibilities. In
the example, the order number to be deleted is passed in the variable onum. That
value comes from any source that a program can use. It can be read from a file, the
program can prompt a user to enter it, or it can be read from the database. The
DELETE statement itself can be part of a subroutine (in which case onum can be a
parameter of the subroutine); the subroutine can be called once or repetitively.
Some of this communication is done through host variables. You can think of the
host variables named in an SQL statement as the parameters of the procedure call
to the database server. In the preceding example, a host variable acts as a
parameter of the WHERE clause. Host variables receive data that the database
server returns, as “Retrieve multiple rows” on page 8-11 describes.
The principal fields of the SQLCA are listed in Table 8-1 through Table 8-3 on page
8-6. The syntax that you use to describe a data structure such as the SQLCA, as
well as the syntax that you use to refer to a field in it, differs among programming
languages. For details, see your SQL API publication.
In particular, the subscript by which you name one element of the SQLERRD and
SQLWARN arrays differs. Array elements are numbered starting with zero in IBM
Informix ESQL/C, but starting with one in other languages. In this discussion, the
fields are named with specific words such as third, and you must translate these
words into the syntax of your programming language.
You can also use the SQLSTATE variable of the GET DIAGNOSTICS statement to
detect, handle, and diagnose errors. See “SQLSTATE value” on page 8-7.
SQLCODE field
The SQLCODE field is the primary return code of the database server. After every
SQL statement, SQLCODE is set to an integer value as the following table shows.
When that value is zero, the statement is performed without error. In particular,
when a statement is supposed to return data into a host variable, a code of zero
means that the data has been returned and can be used. Any nonzero code means
the opposite. No useful data was returned to host variables.
Table 8-1. Values of SQLCODE
Return value Interpretation
value < 0 Specifies an error code.
value = 0 Indicates success.
0 < value < 100 After a DESCRIBE statement, an integer value that represents the type
of SQL statement that is described.
End of data
The database server sets SQLCODE to 100 when the statement is performed
correctly but no rows are found. This condition can occur in two situations.
The first situation involves a query that uses a cursor. (“Retrieve multiple rows” on
page 8-11 describes queries that use cursors.) In these queries, the FETCH
statement retrieves each value from the active set into memory. After the last row
is retrieved, a subsequent FETCH statement cannot return any data. When this
condition occurs, the database server sets SQLCODE to 100, which indicates end of
data, no rows found.
The second situation involves a query that does not use a cursor. In this case, the
database server sets SQLCODE to 100 when no rows satisfy the query condition. In
databases that are not ANSI compliant, only a SELECT statement that returns no
rows causes SQLCODE to be set to 100.
Negative Codes
When something unexpected goes wrong during a statement, the database server
returns a negative number in SQLCODE to explain the problem. The meanings of
these codes are documented in the online error message file.
SQLERRD array
Some error codes that can be reported in SQLCODE reflect general problems. The
database server can set a more detailed code in the second field of SQLERRD that
reveals the error that the database server I/O routines or the operating system
encountered.
The integers in the SQLERRD array are set to different values following different
statements. The first and fourth elements of the array are used only in IBM
Informix ESQL/C. The following table shows how the fields are used.
Table 8-2. Fields of SQLERRD
Field Interpretation
First After a successful PREPARE statement for a SELECT, UPDATE, INSERT, or
DELETE statement, or after a Select cursor is opened, this field contains the
estimated number of rows affected.
Second When SQLCODE contains an error code, this field contains either zero or an
additional error code, called the ISAM error code, that explains the cause of
the main error. After a successful insert operation of a single row, this field
contains the value of any SERIAL, BIGSERIAL, or SERIAL8 value generated
for that row. (This field is not updated, however, when a serial column is
directly inserted as a triggered action by a trigger on a table, or by an
INSTEAD OF trigger on a view.)
These additional details can be useful. For example, you can use the value in the
third field to report how many rows were deleted or updated. When your program
prepares an SQL statement that the user enters and an error is found, the value in
the fifth field enables you to display the exact point of error to the user.
(DB-Access uses this feature to position the cursor when you ask to modify a
statement after an error.)
SQLWARN array
The eight character fields in the SQLWARN array are set to either a blank or to W
to indicate a variety of special conditions. Their meanings depend on the statement
just executed.
A set of warning flags appears when a database opens, that is, following a
CONNECT, DATABASE, or CREATE DATABASE statement. These flags tell you
some characteristics of the database as a whole.
A second set of flags appears following any other statement. These flags reflect
unusual events that occur during the statement, which are usually not serious
enough to be reflected by SQLCODE.
Tip: If an error string is longer than 72 bytes, the overflow is silently discarded. In
some contexts, this can result in the loss of information about runtime errors.
SQLSTATE value
Certain IBM Informix products, such as IBM Informix ESQL/C, support the
SQLSTATE value in compliance with X/Open and ANSI SQL standards. The GET
DIAGNOSTICS statement reads the SQLSTATE value to diagnose errors after you
run an SQL statement. The database server returns a result code in a five-character
Tip: If your IBM Informix product supports GET DIAGNOSTICS and SQLSTATE,
it is recommended that you use them as the primary structure to detect, handle,
and diagnose errors. Using SQLSTATE allows you to detect multiple errors, and it
is ANSI compliant.
The INTO clause is the only detail that distinguishes this statement from any
example in Chapter 2, “Compose SELECT statements,” on page 2-1 or Chapter 5,
“Compose advanced SELECT statements,” on page 5-1. This clause specifies the
host variables that are to receive the data that is produced.
When the program executes an embedded SELECT statement, the database server
performs the query. The example statement selects an aggregate value so that it
produces exactly one row of data. The row has only a single column, and its value
is deposited in the host variable named avg_price. Subsequent lines of the
program can use that variable.
You can use statements of this kind to retrieve single rows of data into host
variables. The single row can have as many columns as desired. If a query
produces more than one row of data, the database server cannot return any data. It
returns an error code instead.
You should list as many host variables in the INTO clause as there are items in the
select list. If, by accident, these lists are of different lengths, the database server
returns as many values as it can and sets the warning flag in the fourth field of
SQLWARN.
8-8 IBM Informix Guide to SQL: Tutorial
Data type conversion
The following Informix ESQL/C example retrieves the average of a DECIMAL
column, which is itself a DECIMAL value. However, the host variable into which
the average of the DECIMAL column is placed is not required to have that data
type.
EXEC SQL SELECT avg (total_price) into :avg_price
FROM items;
The data type of each host variable that is used in a statement is noted and passed
to the database server with the statement. The database server does its best to
convert column data into the form that the receiving variables use. Almost any
conversion is allowed, although some conversions cause a precision loss. The
results of the preceding example differ, depending on the data type of the
receiving host variable, as the following table shows.
Following execution of the SELECT statement, the program tests the indicator
variable for a negative value. A negative number (usually -1) means that the value
retrieved into the main variable is NULL. If the variable is NULL, this program
uses an Informix ESQL/C library function to assign a default value to the host
variable. (The function rstrdate is part of the IBM Informix ESQL/C product.)
The syntax that you use to associate an indicator variable with a host variable
differs with the language you are using, but the principle is the same in all
languages.
End of data
One common event is that no rows satisfy a query. This event is signalled by an
SQLSTATE code of 02000 and by a code of 100 in SQLCODE after a SELECT
statement. This code indicates an error or a normal event, depending entirely on
your application. If you are sure a row or rows should satisfy the query (for
example, if you are reading a row using a key value that you just read from a row
of another table), then the end-of-data code represents a serious failure in the logic
of the program. On the other hand, if you select a row based on a key that a user
supplies or some other source supplies that is less reliable than a program, a lack
of data can be a normal event.
Serious errors
Errors that set SQLCODE to a negative value or SQLSTATE to a value that begins
with anything other than 00, 01, or 02 are usually serious. Programs that you have
developed and that are in production should rarely report these errors.
Nevertheless, it is difficult to anticipate every problematic situation, so your
program must be able to deal with these errors.
For example, a query can return error -206, which means that a table specified in
the query is not in the database. This condition occurs if someone dropped the
table after the program was written, or if the program opened the wrong database
through some error of logic or mistake in input.
However, an aggregate value is also null if it is based on one or more rows that all
contain null values. If you must be able to detect the difference between an
aggregate value that is based on no rows and one that is based on some rows that
are all null, you must include a COUNT function in the statement and an indicator
variable on the aggregate value. You can then work out the following cases.
Default values
You can handle these inevitable errors in many ways. In some applications, more
lines of code are used to handle errors than to execute functionality. In the
examples in this section, however, one of the simplest solutions, the default value,
should work, as the following example shows:
avg_price = 0; /* set default for errors */
EXEC SQL SELECT avg (total_price)
INTO :avg_price:null_flag
FROM items;
if (null_flag < 0) /* probably no rows */
avg_price = 0; /* set default for 0 rows */
These operations are performed using a special data object called a cursor. A cursor
is a data structure that represents the current state of a query. The following list
shows the general sequence of program operations:
1. The program declares the cursor and its associated SELECT statement, which
merely allocates storage to hold the cursor.
These operations are performed with SQL statements named DECLARE, OPEN,
FETCH, CLOSE, and FREE.
Declare a cursor
You use the DECLARE statement to declare a cursor. This statement gives the
cursor a name, specifies its use, and associates it with a statement. The following
example is written in IBM Informix ESQL/C:
EXEC SQL DECLARE the_item CURSOR FOR
SELECT order_num, item_num, stock_num
INTO :o_num, :i_num, :s_num
FROM items
FOR READ ONLY;
The declaration gives the cursor a name (the_item in this case) and associates it
with a SELECT statement. (Chapter 9, “Modify data through SQL programs,” on
page 9-1 discusses how a cursor can also be associated with an INSERT statement.)
The SELECT statement in this example contains an INTO clause. The INTO clause
specifies which variables receive data. You can also use the FETCH statement to
specify which variables receive data, as “Locate the INTO clause” on page 8-13
discusses.
Open a cursor
The program opens the cursor when it is ready to use it. The OPEN statement
activates the cursor. It passes the associated SELECT statement to the database
server, which begins the search for matching rows. The database server processes
the query to the point of locating or constructing the first row of output. It does
not actually return that row of data, but it does set a return code in SQLSTATE
and in SQLCODE for SQL APIs. The following example shows the OPEN
statement in Informix ESQL/C:
EXEC SQL OPEN the_item;
Because the database server is seeing the query for the first time, it might detect a
number of errors. After the program opens the cursor, it should test SQLSTATE or
SQLCODE. If the SQLSTATE value is greater than 02000 or the SQLCODE contains
Fetch rows
The program uses the FETCH statement to retrieve each row of output. This
statement names a cursor and can also name the host variables that receive the
data. The following example shows the completed IBM Informix ESQL/C code:
EXEC SQL DECLARE the_item CURSOR FOR
SELECT order_num, item_num, stock_num
INTO :o_num, :i_num, :s_num
FROM items;
EXEC SQL OPEN the_item;
while(SQLCODE == 0)
{
EXEC SQL FETCH the_item;
if(SQLCODE == 0)
printf("%d, %d, %d", o_num, i_num, s_num);
}
In this version, the case of no returned rows is handled early, so no second test of
SQLCODE exists within the loop. These versions have no measurable difference in
performance because the time cost of a test of SQLCODE is a tiny fraction of the
cost of a fetch.
This form lets you fetch different rows into different locations. For example, you
could use this form to fetch successive rows into successive elements of an array.
After the cursor is opened, it can be used only with a sequential fetch that
retrieves the next row of data, as the following example shows:
EXEC SQL FETCH p_curs into:cnum, :clname, :ccity;
A scroll cursor is declared with the keywords SCROLL CURSOR, as the following
example from IBM Informix ESQL/C shows:
EXEC SQL DECLARE s_curs SCROLL CURSOR FOR
SELECT order_num, order_date FROM orders
WHERE customer_num > 104
Use the scroll cursor with a variety of fetch options. For example, the ABSOLUTE
option specifies the absolute row position of the row to fetch.
EXEC SQL FETCH ABSOLUTE :numrow s_curs
INTO :nordr, :nodat
This statement fetches the row whose position is given in the host variable
numrow. You can also fetch the current row again, or you can fetch the first row
and then scan through all the rows again. However, these features can cause the
application to run more slowly, as the next section describes. For additional options
that apply to scroll cursors, see the FETCH statement in the IBM Informix Guide to
SQL: Syntax.
Because this cursor queries only a single table in a simple way, the database server
quickly determines whether any rows satisfy the query and identifies the first one.
The first row is the only row the cursor finds at this time. The rest of the rows in
the active set remain unknown. As a contrast, consider the following declaration of
a cursor:
EXEC SQL DECLARE hard SCROLL CURSOR FOR
SELECT C.customer_num, O.order_num, sum (items.total_price)
FROM customer C, orders O, items I
WHERE C.customer_num = O.customer_num
AND O.order_num = I.order_num
AND O.paid_date is null
GROUP BY C.customer_num, O.order_num
The active set of this cursor is generated by joining three tables and grouping the
output rows. The optimizer might be able to use indexes to produce the rows in
the correct order, but generally the use of ORDER BY or GROUP BY clauses
requires the database server to generate all the rows, copy them to a temporary
table, and sort the table, before it can determine which row to present first.
In cases where the active set is entirely generated and saved in a temporary table,
the database server can take quite some time to open the cursor. Afterwards, the
database server could tell the program exactly how many rows the active set
contains. However, this information is not made available. One reason is that you
can never be sure which method the optimizer uses. If the optimizer can avoid
sorts and temporary tables, it does so; but small changes in the query, in the sizes
of the tables, or in the available indexes can change the methods of the optimizer.
Most frequently, the database server implements the active set of a scroll cursor as
a temporary table. The database server might not fill this table immediately,
however (unless it created a temporary table to process the query). Usually it
creates the temporary table when the cursor is opened. Then, the first time a row is
fetched, the database server copies it into the temporary table and returns it to the
program. When a row is fetched for a second time, it can be taken from the
When other programs can update the tables while your cursor is open, the idea of
the active set becomes less useful. Your program can see only one row of data at a
time, but all other rows in the table can be changing.
In the case of a simple query, when the database server holds only one row of the
active set, any other row can change. The instant after your program fetches a row,
another program can delete the same row, or update it so that if it is examined
again, it is no longer part of the active set.
When the active set, or part of it, is saved in a temporary table, stale data can
present a problem. That is, the rows in the actual tables from which the active-set
rows are derived can change. If they do, some of the active-set rows no longer
reflect the current table contents.
These ideas seem unsettling at first, but as long as your program only reads the
data, stale data does not exist, or rather, all data is equally stale. The active set is a
snapshot of the data as it is at one moment. A row is different the next day; it does
not matter if it is also different in the next millisecond. To put it another way, no
practical difference exists between changes that occur while the program is running
and changes that are saved and applied the instant that the program terminates.
The only time that stale data can cause a problem is when the program intends to
use the input data to modify the same database; for example, when a banking
application must read an account balance, change it, and write it back. Chapter 9,
“Modify data through SQL programs,” on page 9-1 discusses programs that
modify data.
Parts-explosion problem
When you use a cursor supplemented by program logic, you can solve problems
that plain SQL cannot solve. One of these problems is the parts-explosion problem,
sometimes called bill-of-materials processing. At the heart of this problem is a
recursive relationship among objects; one object contains other objects, which
contain yet others.
PARENT CHILD
FKNN FKNN
123400 432100
432100 765899
Here is the parts-explosion problem: given a part number, produce a list of all
parts that are components of that part. The following example is a sketch of one
solution, as implemented in IBM Informix ESQL/C:
int part_list[200];
boom(top_part)
int top_part;
{
long this_part, child_part;
int next_to_do = 0, next_free = 1;
part_list[next_to_do] = top_part;
Technically speaking, each row of the contains table is the head node of a directed
acyclic graph, or tree. The function performs a breadth-first search of the tree
whose root is the part number passed as its parameter. The function uses a cursor
named part_scan to return all the rows with a particular value in the parent
column. The innermost while loop opens the part_scan cursor, fetches each row in
the selection set, and closes the cursor when the part number of each component
has been retrieved.
This function addresses the heart of the parts-explosion problem, but the function
is not a complete solution. For example, it does not allow for components that
appear at more than one level in the tree. Furthermore, a practical contains table
would also have a column count, giving the count of child parts used in each
parent. A program that returns a total count of each component part is much more
complicated.
If up to four generations of parts can be contained within one top-level part, the
following SELECT statement returns all of them:
SELECT a.parent, a.child, b.child, c.child, d.child
FROM contains a
OUTER (contains b,
OUTER (contains c, outer contains d) )
WHERE a.parent = top_part_number
AND a.child = b.parent
AND b.child = c.parent
AND c.child = d.parent
This SELECT statement returns one row for each line of descent rooted in the part
given as top_part_number. Null values are returned for levels that do not exist.
(Use indicator variables to detect them.) To extend this solution to more levels,
select additional nested outer joins of the contains table. You can also revise this
solution to return counts of the number of parts at each level.
Dynamic SQL
Although static SQL is useful, it requires that you know the exact content of every
SQL statement at the time you write the program. For example, you must state
exactly which columns are tested in any WHERE clause and exactly which
columns are named in any select list.
No problem exists when you write a program to perform a well-defined task. But
the database tasks of some programs cannot be perfectly defined in advance. In
particular, a program that must respond to an interactive user might need to
compose SQL statements in response to what the user enters.
In this way, a program can construct and then use any SQL statement, based on
user input of any kind. For example, it can read a file of SQL statements and
prepare and execute each one.
DB-Access, a utility that you can use to explore SQL interactively, is an IBM
Informix ESQL/C program that constructs, prepares, and executes SQL statements
dynamically. For example, DB-Access lets you use simple, interactive menus to
specify the columns of a table. When you are finished, DB-Access builds the
necessary CREATE TABLE or ALTER TABLE statement dynamically and prepares
and executes it.
You can prepare a statement in this form for execution with the PREPARE
statement. The following example is written in IBM Informix ESQL/C:
EXEC SQL prepare query_2 from
’SELECT * from orders
WHERE customer_num = ? and order_date > ?’;
The two question marks in this example indicate that when the statement is
executed, the values of host variables are used at those two points.
You can prepare almost any SQL statement dynamically. The only statements that
you cannot prepare are the ones directly concerned with dynamic SQL and cursor
management, such as the PREPARE and OPEN statements. After you prepare an
UPDATE or DELETE statement, it is a good idea to test the fifth field of
SQLWARN to see if you used a WHERE clause (see “SQLWARN array” on page
8-6).
The PREPARE statement does not limit the character string to one statement. It can
contain multiple SQL statements, separated by semicolons. The following example
shows a fairly complex transaction in IBM Informix ESQL/C:
strcpy(big_query, "UPDATE account SET balance = balance + ?
WHERE customer_id = ?; \ UPDATE teller SET balance =
balance + ? WHERE teller_id = ?;");
EXEC SQL PREPARE big1 FROM :big_query;
When this list of statements is executed, host variables must provide values for six
place-holding question marks. Although it is more complicated to set up a
multistatement list, performance is often better because fewer exchanges take place
between the program and the database server.
The following IBM Informix ESQL/C code prepares and executes a multistatement
update of a bank account:
The USING clause of the EXECUTE statement supplies a list of host variables
whose values are to take the place of the question marks in the prepared
statement. If a SELECT (or EXECUTE FUNCTION) returns only one row, you can
use the INTO clause of EXECUTE to specify the host variables that receive the
values.
The key to this ability is the DESCRIBE statement. It takes the name of a prepared
SQL statement and returns information about the statement and its contents. It sets
SQLCODE to specify the type of statement; that is, the verb with which it begins.
If the prepared statement is a SELECT statement, the DESCRIBE statement also
returns information about the selected output data. If the prepared statement is an
INSERT statement, the DESCRIBE statement returns information about the input
parameters. The data structure to which a DESCRIBE statement returns
information is a predefined data structure that is allocated for this purpose and is
known as a system-descriptor area. If you are using IBM Informix ESQL/C, you
can use a system-descriptor area or, as an alternative, an sqlda structure.
The data structure that a DESCRIBE statement returns or references for a SELECT
statement includes an array of structures. Each structure describes the data that is
returned for one item in the select list. The program can examine the array and
discover that a row of data includes a decimal value, a character value of a certain
length, and an integer.
With this information, the program can allocate memory to hold the retrieved
values and put the necessary pointers in the data structure for the database server
to use.
You can use the FREE statement to release this space. The FREE statement takes
either the name of a statement or the name of a cursor that was declared for a
statement name, and releases the space allocated to the prepared statement. If more
than one cursor is defined on the statement, freeing the statement does not free the
cursor.
Quick execution
For simple statements that do not require a cursor or host variables, you can
combine the actions of the PREPARE, EXECUTE, and FREE statements into a
single operation. The following example shows how the EXECUTE IMMEDIATE
statement takes a character string, prepares it, executes it, and frees the storage in
one operation:
EXEC SQL execute immediate ’drop index my_temp_index’;
This capability makes it easy to write simple SQL operations. However, because no
USING clause is allowed, the EXECUTE IMMEDIATE statement cannot be used for
SELECT statements.
The creation of a database and its tables is generally done interactively, using
DB-Access. These tools can also be run from a file of statements, so that the
creation of a database can be done with one operating-system command. The
data-definition statements are documented in the IBM Informix Guide to SQL:
Syntax and the IBM Informix Database Design and Implementation Guide.
The GRANT and REVOKE statements are especially good candidates for dynamic
SQL. Each statement takes the following parameters:
v A list of one or more privileges
v A table name
v The name of a user
You probably need to supply at least some of these values based on program input
(from the user, command-line parameters, or a file) but none can be supplied in
the form of a host variable. The syntax of these statements does not allow host
variables at any point.
The following IBM Informix ESQL/C function assembles a GRANT statement from
parameters, and then prepares and executes it:
char priv_to_grant[100];
char table_name[20];
char user_id[20];
The opening statement of the function that the following example shows specifies
its name and its three parameters. The three parameters specify the privileges to
grant, the name of the table on which to grant privileges, and the ID of the user to
receive them.
table_grant(priv_to_grant, table_name, user_id)
char *priv_to_grant;
char *table_name;
char *user_id;
The function uses the statements in the following example to define a local
variable, grant_stmt, which is used to assemble and hold the GRANT statement:
EXEC SQL BEGIN DECLARE SECTION;
char grant_stmt[200];
EXEC SQL END DECLARE SECTION;
If the database server returns an error code in SQLCODE following the PREPARE
statement, the function displays an error message. If the database server approves
the form of the statement, it sets a zero return code. This action does not guarantee
that the statement is executed properly; it means only that the statement has
correct syntax. It might refer to a nonexistent table or contain many other kinds of
errors that can be detected only during execution. The following portion of the
example checks that the_grant was prepared successfully before executing it:
if(SQLCODE == 0)
EXEC SQL EXECUTE the_grant;
else
printf("Sorry, got error # %d attempting %s", SQLCODE, grant_stmt);
If the preparation is successful, SQLCODE = = 0, the next step executes the prepared
statement.
Assign roles
Alternatively, the DBA can define a role with the CREATE ROLE statement, and
use the GRANT and REVOKE statements to cancel or assign roles to users, and to
grant and revoke privileges of roles. For example:
GRANT engineer TO nmartin;
The SET ROLE statement is needed to activate a non-default role. For more
information on roles and privileges, see “Access-management strategies” on page
1-5 and “Privileges on a database and on its objects” on page 6-21. For more
information on the GRANT and REVOKE statements, see the IBM Informix
Database Design and Implementation Guide. For more information about the syntax of
these statements, see IBM Informix Guide to SQL: Syntax.
Summary
SQL statements can be written into programs as if they were normal statements of
the programming language. Program variables can be used in WHERE clauses, and
data from the database can be fetched into them. A preprocessor translates the SQL
code into procedure calls and data structures.
Statements that do not return data, or queries that return only one row of data, are
written like ordinary imperative statements of the language. Queries that can
return more than one row are associated with a cursor that represents the current
row of data. Through the cursor, the program can fetch each row of data as it is
needed.
Static SQL statements are written into the text of the program. However, the
program can form new SQL statements dynamically, as it runs, and execute them
also. In the most advanced cases, the program can obtain information about the
number and types of columns that a query returns and dynamically allocate the
memory space to hold them.
This chapter discusses the issues that arise when a program needs to delete, insert,
or update rows to modify the database. As in Chapter 8, “SQL programming,” on
page 8-1, this chapter prepares you for reading your IBM Informix embedded
language publication.
The general use of the INSERT, UPDATE, and DELETE statements is discussed in
Chapter 6, “Modify data,” on page 6-1. This chapter examines their use from
within a program. You can easily embed the statements in a program, but it can be
difficult to handle errors and to deal with concurrent modifications from multiple
programs.
Whenever you delete rows, you must consider whether rows in other tables
depend on the deleted rows. This problem of coordinated deletions is covered in
Chapter 6, “Modify data,” on page 6-1. The problem is the same when deletions
are made from within a program.
Direct deletions
You can embed a DELETE statement in a program. The following example uses
IBM Informix ESQL/C:
EXEC SQL delete from items
WHERE order_num = :onum;
You can also prepare and execute a statement of the same form dynamically. In
either case, the statement works directly on the database to affect one or more
rows.
The WHERE clause in the example uses the value of a host variable named onum.
Following the operation, results are posted in SQLSTATE and in the sqlca
structure, as usual. The third element of the SQLERRD array contains the count of
rows deleted even if an error occurs. The value in SQLCODE shows the overall
success of the operation. If the value is not negative, no errors occurred and the
third element of SQLERRD is the count of all rows that satisfied the WHERE
clause and were deleted.
However, certain errors can be discovered after the operation begins and some
rows are processed. The most common of these errors is a lock conflict. The
database server must obtain an exclusive lock on a row before it can delete that
row. Other programs might be using the rows from the table, preventing the
database server from locking a row. Because the issue of locking affects all types of
modifications, Chapter 10, “Programming for a multiuser environment,” on page
10-1, discusses it.
Other, rarer types of errors can strike after deletions begin. For example, hardware
errors that occur while the database is being updated.
Transaction logging
The best way to prepare for any kind of error during a modification is to use
transaction logging. In the event of an error, you can tell the database server to put
the database back the way it was. The following example is based on the example
in the section “Direct deletions” on page 9-1, which is extended to use transactions:
EXEC SQL begin work; /* start the transaction*/
EXEC SQL delete from items
where order_num = :onum;
del_result = sqlca.sqlcode; /* save two error */
del_isamno = sqlca.sqlerrd[1]; /* code numbers */
del_rowcnt = sqlca.sqlerrd[2]; /* and count of rows */
if (del_result < 0) /* problem found: */
EXEC SQL rollback work; /* put everything back */
else /* everything worked OK:*/
EXEC SQL commit work; /* finish transaction */
A key point in this example is that the program saves the important return values
in the sqlca structure before it ends the transaction. Both the ROLLBACK WORK
and COMMIT WORK statements, like other SQL statements, set return codes in the
sqlca structure. However, if you want to report the codes that the error generated,
you must save them before executing ROLLBACK WORK. The ROLLBACK
WORK statement removes all of the pending transaction, including its error codes.
In a database with logging, if a user does not start an explicit transaction, the
database server initiates an internal transaction prior to execution of the statement
and terminates the transaction after execution completes or fails. If the statement
execution succeeds, the internal transaction is committed. If the statement fails, the
internal transaction is rolled back.
Coordinated deletions
The usefulness of transaction logging is particularly clear when you must modify
more than one table. For example, consider the problem of deleting an order from
the demonstration database. In the simplest form of the problem, you must delete
rows from two tables, orders and items, as the following example of IBM Informix
ESQL/C shows:
The logic of this program is much the same whether or not transactions are used.
If they are not used, the person who sees the error message has a much more
difficult set of decisions to make. Depending on when the error occurred, one of
the following situations applies:
v No deletions were performed; all rows with this order number remain in the
database.
v Some, but not all, item rows were deleted; an order record with only some items
remains.
v All item rows were deleted, but the order row remains.
v All rows were deleted.
In the second and third cases, the database is corrupted to some extent; it contains
partial information that can cause some queries to produce wrong answers. You
must take careful action to restore consistency to the information. When
transactions are used, all these uncertainties are prevented.
Warning: The design of the Informix ESQL/C function in this example is unsafe. It
depends on the current isolation level for correct operation. Isolation levels are
discussed later in the chapter. For more information on isolation levels, see
Chapter 10, “Programming for a multiuser environment,” on page 10-1. Even when
the function works as intended, its effects depend on the physical order of rows in
the table, which is not generally a good idea.
int delDupOrder()
{
int ord_num;
int dup_cnt, ret_code;
The purpose of the function is to delete rows that contain duplicate order numbers.
In fact, in the demonstration database, the orders.order_num column has a unique
index, so duplicate rows cannot occur in it. However, a similar function can be
written for another database; this one uses familiar column names.
The function declares scan_ord, a cursor to scan all rows in the orders table. It is
declared with the FOR UPDATE clause, which states that the cursor can modify
data. If the cursor opens properly, the function begins a transaction and then loops
over rows of the table. For each row, it uses an embedded SELECT statement to
determine how many rows of the table have the order number of the current row.
(This step fails without the correct isolation level, as Chapter 10, “Programming for
a multiuser environment,” on page 10-1 describes.)
In the demonstration database, with its unique index on this table, the count
returned to dup_cnt is always one. However, if it is greater, the function deletes
the current row of the table, reducing the count of duplicates by one.
Cleanup functions of this sort are sometimes needed, but they generally need more
sophisticated design. This function deletes all duplicate rows except the last one
that the database server returns. That order has nothing to do with the content of
the rows or their meanings. You can improve the function in the previous example
by adding, perhaps, an ORDER BY clause to the cursor declaration. However, you
cannot use ORDER BY and FOR UPDATE together. “An insert example” on page
9-7 presents a better approach.
When you open an insert cursor, a buffer is created in memory to hold a block of
rows. The buffer receives rows of data as the program produces them; then they
are passed to the database server in a block when the buffer is full. The buffer
reduces the amount of communication between the program and the database
server, and it lets the database server insert the rows with less difficulty. As a
result, the insertions go faster.
The buffer is always made large enough to hold at least two rows of inserted
values. It is large enough to hold more than two rows when the rows are shorter
than the minimum buffer size.
The code in this example calls next_cust repeatedly. When it returns non-null data,
the PUT statement sends the returned data to the row buffer. When the buffer fills,
the rows it contains are automatically sent to the database server. The loop
Re-examine the information about the INSERT statement. See “The INSERT
statement” on page 9-4. The statement by itself, not part of a cursor definition,
inserts a single row into the customer table. In fact, the whole apparatus of the
insert cursor can be dropped from the example code, and the INSERT statement
can be written into the code where the PUT statement now stands. The difference
is that an insert cursor causes a program to run somewhat faster.
The values returned into the SQL Communications Area (SQLCA) give the
program the information it needs to sort out each case. SQLCODE and SQLSTATE
are set to zero after every PUT statement if no error occurs and to a negative error
code if an error occurs.
The database server sets the third element of SQLERRD to the number of rows
actually inserted into the table, as follows
v Zero, if the new row is merely moved to the buffer
v The number of rows that are in the buffer, if the buffer load is inserted without
error
v The number of rows inserted before an error occurs, if one did occur
Read the code once again to see how SQLCODE is used (see the previous
example). First, if the OPEN statement yields an error, the loop is not executed
because the WHILE condition fails, the FLUSH operation is not performed, and the
transaction rolls back. Second, if the PUT statement returns an error, the loop ends
because of the WHILE condition, the FLUSH operation is not performed, and the
transaction rolls back. This condition can occur only if the loop generates enough
rows to fill the buffer at least once; otherwise, the PUT statement cannot generate
an error.
The program might end the loop with rows still in the buffer, possibly without
inserting any rows. At this point, the SQL status is zero, and the FLUSH operation
occurs. If the FLUSH operation produces an error code, the transaction rolls back.
Only when all inserts are successfully performed is the transaction committed.
Rows of constants
The insert cursor mechanism supports one special case where high performance is
easy to obtain. In this case, all the values listed in the INSERT statement are
constants: no expressions and no host variables are listed, just literal numbers and
strings of characters. No matter how many times such an INSERT operation
occurs, the rows it produces are identical. When the rows are identical, copying,
buffering, and transmitting each identical row is pointless.
You do not usually insert a quantity of identical rows. You can insert identical
rows when you first establish a database to populate a large table with null data.
An insert example
“Delete with a cursor” on page 9-3 contains an example of the DELETE statement
whose purpose is to look for and delete duplicate rows of a table. A better way to
perform this task is to select the desired rows instead of deleting the undesired
ones. The code in the following IBM Informix ESQL/C example shows one way to
do this task:
EXEC SQL BEGIN DECLARE SECTION;
long last_ord = 1;
struct {
long int o_num;
date o_date;
long c_num;
char o_shipinst[40];
char o_backlog;
char o_po[10];
date o_shipdate;
decimal o_shipwt;
decimal o_shipchg;
date o_paiddate;
} ord_row;
EXEC SQL END DECLARE SECTION;
This example begins with an ordinary INSERT statement, which finds all the
nonduplicated rows of the table and inserts them into another table, presumably
created before the program started. That action leaves only the duplicate rows. (In
the demonstration database, the orders table has a unique index and cannot have
duplicate rows. Assume that this example deals with some other database.)
The code in the previous example then declares two cursors. The first, called
dup_row, returns the duplicate rows in the table. Because dup_row is for input
only, it can use the ORDER BY clause to impose some order on the duplicates
other than the physical record order used in the example on page “Delete with a
cursor” on page 9-3. In this example, the duplicate rows are ordered by their dates
(the oldest one remains), but you can use any other order based on the data.
The second cursor, ins_row, is an insert cursor. This cursor takes advantage of the
ability to use a C structure, ord_row, to supply values for all columns in the row.
The remainder of the code examines the rows that are returned through dup_row.
It inserts the first one from each group of duplicates into the new table and
disregards the rest.
For the sake of brevity, the preceding example uses the simplest kind of error
handling. If an error occurs before all rows have been processed, the sample code
rolls back the active transaction.
In the same way, the INSERT statement does not set the end-of-data return code
even when the source of the inserted rows is a SELECT statement, and the SELECT
statement selected no rows. The INSERT statement is a success because it inserted
as many rows as it was asked to (that is, zero).
To find out how many rows are inserted, updated, or deleted, a program can test
the third element of SQLERRD. The count of rows is there, regardless of the value
(zero or negative) in SQLCODE.
An update cursor
An update cursor permits you to delete or update the current row; that is, the most
recently fetched row. The following example in IBM Informix ESQL/C shows the
declaration of an update cursor:
EXEC SQL
DECLARE names CURSOR FOR
SELECT fname, lname, company
FROM customer
FOR UPDATE;
The program that uses this cursor can fetch rows in the usual way.
EXEC SQL
FETCH names INTO :FNAME, :LNAME, :COMPANY;
If the program then decides that the row needs to be changed, it can do so.
if (strcmp(COMPANY, "SONY") ==0)
{
EXEC SQL
UPDATE customer
SET fname = ’Midori’, lname = ’Tokugawa’
WHERE CURRENT OF names;
}
The words CURRENT OF names take the place of the usual test expressions in the
WHERE clause. In other respects, the UPDATE statement is the same as usual,
even including the specification of the table name, which is implicit in the cursor
name but still required.
If the program attempts such an update, an error code is returned and no update
occurs. An attempt to delete with WHERE CURRENT OF is also rejected, because
deletion affects all columns.
Cleanup a table
A final, hypothetical example of how to use an update cursor presents a problem
that should never arise with an established database but could arise in the initial
design phases of an application.
In the example, a large table named target is created and populated. A character
column, dactyl, inadvertently acquires some null values. These rows should be
deleted. Furthermore, a new column, serials, is added to the table with the ALTER
TABLE statement. This column is to have unique integer values installed. The
following example shows the IBM Informix ESQL/C code you use to accomplish
these tasks:
EXEC SQL BEGIN DECLARE SECTION;
char dcol[80];
short dcolint;
int sequence;
EXEC SQL END DECLARE SECTION;
In all these activities, you must make sure that the program detects errors and
returns the database to a known state when an error occurs. The most important
tool for doing this is transaction logging. Without transaction logging, it is more
difficult to write programs that can recover from errors.
If your database is contained in a single-user workstation and does not access data
from another computer, your programs can modify data freely. In all other cases,
you must allow for the possibility that, while your program is modifying data,
another program is reading or modifying the same data. This situation is described
as concurrency: two or more independent uses of the same data at the same time.
This section addresses concurrency, locking, and isolation levels.
This section also describes the statement cache feature, which can reduce
per-session memory allocation and speed up query processing. The statement
cache stores statements that can then be shared among different user sessions that
use identical SQL statements.
To prevent errors of this kind, the database server imposes a system of locks. A lock
is a claim, or reservation, that a program can place on a piece of data. The
database server guarantees that, as long as the data is locked, no other program
can modify it. When another program requests the data, the database server either
makes the program wait or turns it back with an error.
The following sections discuss how you can achieve the following goals with your
program:
v Place all the locks necessary to ensure data integrity.
v Lock the fewest, smallest pieces of data possible consistent with the preceding
goal.
The transfer of each row from the database server to the program takes time.
During and between transfers, other programs can perform other database
operations. At about the same time that your program fetches the rows produced
by that query, another user's program might execute the following update:
EXEC SQL UPDATE stock
SET unit_price = 1.15 * unit_price
WHERE manu_code = ’ANZ’;
In other words, both programs are reading through the same table, one fetching
certain rows and the other changing the same rows. The following scenarios are
possible:
1. The other program finishes its update before your program fetches its first row.
Your program shows you only updated rows.
2. Your program fetches every row before the other program has a chance to
update it.
Your program shows you only original rows.
3. After your program fetches some original rows, the other program catches up
and goes on to update some rows that your program has yet to read; then it
executes the COMMIT WORK statement.
Your program might return a mixture of original rows and updated rows.
4. Same as number 3, except that after updating the table, the other program
issues a ROLLBACK WORK statement.
Your program can show you a mixture of original rows and updated rows that
no longer exist in the database.
The first two possibilities are harmless. In possibility number 1, the update is
complete before your query begins. It makes no difference whether the update
finished a microsecond ago or a week ago.
In possibility number 2, your query is, in effect, complete before the update begins.
The other program might have been working just one row behind yours, or it
might not start until tomorrow night; it does not matter.
The last two possibilities, however, can be important to the design of some
applications. In possibility number 3, the query returns a mix of updated and
original data. That result can be detrimental in some applications. In others, such
as one that is taking an average of all prices, it might not matter at all.
Another concern arises when your program uses a cursor to update or delete the
last-fetched row. Erroneous results occur with the following sequence of events:
v Your program fetches the row.
To control concurrent events such as these, use the locking and isolation level
features of the database server.
Kinds of locks
The following table shows the types of locks that IBM Informix database servers
support for different situations.
Lock scope
You can apply locks to entire databases, entire tables, disk pages, single rows, or
index-key values. The size of the object that is being locked is referred to as the
scope of the lock (also called the lock granularity). In general, the larger the scope of
a lock, the more concurrency is reduced, but the simpler programming becomes.
The following statement shows how you might lock an entire database exclusively:
DATABASE database_one EXCLUSIVE
This statement succeeds if no other program has opened that database. After the
lock is placed, no other program can open the database, even for reading, because
its attempt to place a shared lock on the database name fails.
A database lock is released only when the database closes. That action can be
performed explicitly with the DISCONNECT or CLOSE DATABASE statements or
implicitly by executing another DATABASE statement.
Table locks
You can lock entire tables. In some cases, the database server performs this action
automatically. You can also use the LOCK TABLE statement to lock an entire table
explicitly.
The LOCK TABLE statement or the database server can place the following types
of table locks:
Shared lock
No users can write to the table. In shared mode, the database server places
one shared lock on the table, which informs other users that no updates
can be performed. In addition, the database server adds locks for every
row updated, deleted, or inserted.
Exclusive lock
No other users can read from or write to the table. In exclusive mode, the
database server places only one exclusive lock on the table, no matter how
many rows it updates. An exclusive table lock prevents any concurrent use
of the table and, therefore, can have a serious effect on performance if
many other programs are contending for the use of the table. However,
when you need to update most of the rows in a table, place an exclusive
lock on the table.
Lock a table with the LOCK TABLE statement: A transaction tells the database
server to use table-level locking for a table with the LOCK TABLE statement. The
following example shows how to place an exclusive lock on a table:
LOCK TABLE tab1 IN EXCLUSIVE MODE
Tip: You can set the isolation level for your database server to achieve the same
degree of protection as the shared table lock while providing greater concurrency.
Completion of the statement (or end of the transaction) releases the lock. An entire
table can also be locked automatically during certain queries.
You can avoid table locking when you CREATE or DROP an index using the
ONLINE keyword. While the index is being created or dropped online, no DDL
operations on the table are supported, but operations that were concurrent when
the CREATE INDEX or DROP INDEX statement was issued can be completed. The
specified index is not created or dropped until no other processes are concurrently
accessing the table. Then locks are held briefly to write the system catalog data
associated with the index. This increases the availability of the system, since the
table is still readable by ongoing and new sessions. The following statement shows
how to use the ONLINE keyword to avoid automatic table locking with a CREATE
INDEX statement:
CREATE INDEX idx_1 ON customer (lname) ONLINE;
Row and key locking are not the default behaviors. You must specify row-level
locking when you create the table. The following example creates a table with
row-level locking:
CREATE TABLE tab1
(
col1...
) LOCK MODE ROW;
If you specify a LOCK MODE clause when you create a table, you can later change
the lock mode with the ALTER TABLE statement. The following statement changes
the lock mode on the reservations table to page-level locking:
ALTER TABLE tab1 LOCK MODE PAGE
In certain cases, the database server has to lock a row that does not exist. To do
this, the database server places a lock on an index-key value. Key locks are used
identically to row locks. When the table uses row locking, key locks are
implemented as locks on imaginary rows. When the table uses page locking, a key
lock is placed on the index page that contains the key or that would contain the
key if it existed.
Row and key locks generally provide the best performance overall when you
update a relatively small number of rows because they increase concurrency.
However, the database server incurs some overhead in obtaining a lock.
When one or more rows in a table are locked by an exclusive lock, the effect on
other users partly depends on their transaction isolation level. Other users whose
isolation levels is not Dirty Read might encounter transactions that fail because the
exclusive lock was not released within a specified time limit.
For Committed Read or Dirty Read isolation level operations that attempt to access
tables on which a concurrent session has set exclusive row-level locks, the risk of
locking conflicts can be reduced by enabling transactions to read the most recently
committed version of the data in the locked rows, rather than waiting for the
transaction that set the lock to be committed or rolled back. Enabling access to the
last committed version of exclusively locked rows can be accomplished in several
ways:
v For an individual session, issue this SQL statement
SET ISOLATION TO COMMITTED READ LAST COMMITTED;
v For all sessions using the Committed Read or Read Committed isolation level,
the DBA can set the USELASTCOMMITTED configuration parameter to ’ALL’ or
to ’COMMITTED READ’.
v For an individual session using the Committed Read or Read Committed
isolation level, any user can issue the SET ENVIRONMENT
USELASTCOMMITTED statement with ’ALL’ or ’COMMITTED READ’ as the value
of this session environment option.
v For all sessions using Dirty Read or Read Uncommitted isolation levels, the DBA
can set the USELASTCOMMITTED configuration parameter to ’ALL’ or to
’DIRTY READ’.
v For an individual session using the Dirty Read or Read Uncommitted isolation
levels, any user can issue the SET ENVIRONMENT USELASTCOMMITTED
statement with ’ALL’ or ’DIRTY READ’ as the value of this session environment
option.
This LAST COMMITTED feature is useful only when row-level locking is in effect,
rather than when another session holds an exclusive lock on the entire table. This
feature is disabled for any table on which the LOCK TABLE statement applies a
table-level lock. See the description of the SET ENVIRONMENT statement in the
IBM Informix Guide to SQL: Syntax and the description of the
USELASTCOMMITTED configuration parameter inIBM Informix Administrator's
Reference for more information about this feature for concurrent access to tables in
which some rows are locked by exclusive locks, and for restrictions on the kinds of
tables that can support this feature.
Page locks
The database server stores data in units called disk pages. A disk page contains one
or more rows. In some cases, it is better to lock a disk page than to lock individual
rows on it. For example, with operations that require changing a large number of
rows, you might choose page-level locking because row-level locking (one lock per
row) might not be cost effective.
Set the row or page lock mode for all CREATE TABLE statements: IBM Informix
allows you to set the lock mode to page-level locking or row-level locking for all
newly created tables for a single user (per session) or for multiple users (per
server). You no longer need to specify the lock mode every time that you create a
new table with the CREATE TABLE statement.
If you want every new table created within your session to be created with a
particular lock mode, you have to set the IFX_DEF_TABLE_LOCKMODE environment
variable. For example, for every new table created within your session to be
created with lock mode row, set IFX_DEF_TABLE_LOCKMODE to ROW. To override this
behavior, use the CREATE TABLE or ALTER TABLE statements and redefine the
LOCK MODE clause.
Single-user lock mode: Set the single-user lock mode if all of the new tables that
you create in your session require the same lock mode. Set the single-user lock
mode with the IFX_DEF_TABLE_LOCKMODE environment variable. For example, for
every new table created within your session to be created with row-level locking,
set IFX_DEF_TABLE_LOCKMODE to ROW. To override this behavior, use the CREATE
TABLE or ALTER TABLE statements and redefine the LOCK MODE clause. For
more information on setting environment variables, see the IBM Informix Guide to
SQL: Reference.
Multiple-user lock mode: Database administrators can use the multiple-user lock
mode to create greater concurrency by designating the lock mode for all users on
the same server. All tables that any user creates on that server will then have the
same lock mode. To enable multiple-user lock mode, set the
IFX_DEF_TABLE_LOCKMODE environment variable before starting the database server
or set the DEF_TABLES_LOCKMODE configuration parameter.
Rules of precedence:
Locking mode for CREATE TABLE or ALTER TABLE has the following rules of
precedence, listed in order of highest precedence to lowest:
1. CREATE TABLE or ALTER TABLE SQL statements that use the LOCK MODE
clause
2. Single-user environment variable setting
3. Multi-user environment variable setting in the server environment
4. Configuration parameters in the configuration file
5. Default behavior (page-level locking)
Use the coarse lock mode when you know the index is not going to change; that is,
when read-only operations are performed on the index.
When the database server executes the command to change the lock mode to
coarse, it acquires an exclusive lock on the table for the duration of the command.
Any transactions that are currently using a lock of finer granularity must complete
before the database server switches to the coarse lock mode.
Smart-large-object locks
Locks on a CLOB or BLOB column are separate from the lock on the row. Smart
large objects are locked only when they are accessed. When you lock a table that
contains a CLOB or BLOB column, no smart large objects are locked. If accessed
for writing, the smart large object is locked in update mode, and the lock is
promoted to exclusive when the actual write occurs. If accessed for reading, the
smart large object is locked in shared mode. The database server recognizes the
transaction isolation mode, so if Repeatable Read isolation level is set, the database
server does not release smart-large-object read locks before end of transaction.
When the database server retrieves a row and updates a smart large object that the
row points to, only the smart large object is exclusively locked during the time it is
being updated.
Byte-range locks:
You can lock a range of bytes for a smart large object. Byte-range locks allow a
transaction to selectively lock only those bytes that are accessed so that writers and
readers simultaneously can access different byte ranges in the same smart large
object.
For information about how to use byte-range locks, see your IBM Informix
Performance Guide.
Duration of a lock
The program controls the duration of a database lock. A database lock is released
when the database closes.
Depending on whether the database uses transactions, table lock durations vary. If
the database does not use transactions (that is, if no transaction log exists and you
do not use a COMMIT WORK statement), a table lock remains until it is removed
by the execution of the UNLOCK TABLE statement.
The duration of table, row, and index locks depends on what SQL statements you
use and on whether transactions are in use.
When you use transactions, the end of a transaction releases all table, row, page,
and index locks. When a transaction ends, all locks are released.
The duration of an exclusive row lock depends on whether transactions are in use.
If they are not in use, the lock is released as soon as the modified row is written to
disk. When transactions are in use, all such locks are held until the end of the
transaction. This action prevents other programs from using rows that might be
rolled back to their original state.
When transactions are in use, a key lock is used whenever a row is deleted. Using
a key lock prevents the following error from occurring:
v Program A deletes a row.
v Program B inserts a row that has the same key.
v Program A rolls back its transaction, forcing the database server to restore its
deleted row.
What is to be done with the row inserted by Program B?
By locking the index, the database server prevents a second program from
inserting a row until the first program commits its transaction.
The locks placed while the database server reads various rows are controlled by
the current isolation level, as discussed in the next section.
This section describes the different isolation levels and update cursors.
To set the isolation level, use either the SET ISOLATION or SET TRANSACTION
statement. The SET TRANSACTION statement also lets you set access modes. For
more information about access modes, see “Control data modification with access
modes” on page 10-15.
The following table shows the relationships between the isolation levels that you
set with the SET TRANSACTION and SET ISOLATION statements.
The major difference between the SET TRANSACTION and SET ISOLATION
statements is the behavior of the isolation levels within transactions. The SET
TRANSACTION statement can be issued only once for a transaction. Any cursors
opened during that transaction are guaranteed to have that isolation level (or
access mode if you are defining an access mode). With the SET ISOLATION
statement, after a transaction is started, you can change the isolation level more
than once within the transaction. The following examples illustrate the difference
between the use of SET ISOLATION and the use of SET TRANSACTION.
SET ISOLATION:
EXEC SQL BEGIN WORK;
EXEC SQL SET ISOLATION TO DIRTY READ;
EXEC SQL SELECT ... ;
EXEC SQL SET ISOLATION TO REPEATABLE READ;
EXEC SQL INSERT ... ;
EXEC SQL COMMIT WORK;
-- Executes without error
SET TRANSACTION:
EXEC SQL BEGIN WORK;
EXEC SQL SET TRANSACTION ISOLATION LEVEL TO SERIALIZABLE;
EXEC SQL SELECT ... ;
EXEC SQL SET TRANSACTION ISOLATION LEVEL TO READ COMMITTED;
Error -876: Cannot issue SET TRANSACTION once a transaction has started.
A program always receives complete rows of data. Even under ANSI Read
Uncommitted or IBM Informix Dirty Read isolation, a program never sees a row in
which some columns are updated and some are not. However, a program that uses
ANSI Read Uncommitted or IBM Informix Dirty Read isolation sometimes reads
updated rows before the updating program ends its transaction. If the updating
program later rolls back its transaction, the reading program processes data that
never really existed (possibility number 4 on page 10-2 in the list of concurrency
issues).
ANSI Read Uncommitted or IBM Informix Dirty Read is the most efficient
isolation level. The reading program never waits and never makes another
program wait. It is the preferred level in any of the following cases:
v All tables are static; that is, concurrent programs only read and never modify
data.
v The table is held in an exclusive lock.
v Only one program is using the table.
ANSI Read Committed or IBM Informix Committed Read does not actually place a
lock on the fetched row, so this isolation level is almost as efficient as ANSI Read
Uncommitted or IBM Informix Dirty Read. This isolation level is appropriate to
use when each row of data is processed as an independent unit, without reference
to other rows in the same or other tables.
Locking conflicts can occur in ANSI Read Committed or IBM Informix Committed
Read sessions, however, if the attempt to place the test lock is not successful
because a concurrent session holds a shared lock on the row. To avoid waiting for
concurrent transactions to release shared locks (by committing or rolling back),
Informix supports the Last Committed option to the Committed Read isolation
level. When this Last Committed option is in effect, a shared lock by another
session causes the query to return the most recently committed version of the row.
When Cursor Stability is in effect, IBM Informix places a lock on the latest row
fetched. It places a shared lock for an ordinary cursor or a promotable lock for an
update cursor. Only one row is locked at a time; that is, each time a row is fetched,
the lock on the previous row is released (unless that row is updated, in which case
the lock holds until the end of the transaction). Because Cursor Stability locks only
one row at a time, it restricts concurrency less than a table lock or database lock.
Cursor Stability ensures that a row does not change while the program examines
it. Such row stability is important when the program updates some other table
based on the data it reads from the row. Because of Cursor Stability, the program is
assured that the update is based on current information. It prevents the use of stale
data.
If Program A used a lesser level of isolation, the following sequence could occur:
1. Program A reads the HRO row of the manufact table to learn the manufacturer
code. No lock is placed.
2. Program B issues a DELETE statement for that row. It succeeds.
3. Program B deletes all rows of stock that use manufacturer code HRO.
4. Program B ends.
5. Program A, not aware that its copy of the HRO row is now invalid, inserts a
new row of stock using the manufacturer code HRO.
6. Program A ends.
At the end, a row occurs in stock that has no matching manufacturer code in
manufact. Furthermore, Program B apparently has a bug; it did not delete the rows
that it was supposed to delete. Use of the Cursor Stability isolation level prevents
these effects.
The preceding scenario could be rearranged to fail even with Cursor Stability. All
that is required is for Program B to operate on tables in the reverse sequence to
Program A. If Program B deletes from stock before it removes the row of
manufact, no degree of isolation can prevent an error. Whenever this kind of error
is possible, all programs that are involved must use the same sequence of access.
The Repeatable Read isolation level asks the database server to put a lock on every
row the program examines and fetches. The locks that are placed are shareable for
an ordinary cursor and promotable for an update cursor. The locks are placed
individually as each row is examined. They are not released until the cursor closes
or a transaction ends.
Repeatable Read isolation places the largest number of locks and holds them the
longest. Therefore, it is the level that reduces concurrency the most. If your
program uses this level of isolation, think carefully about how many locks it
places, how long they are held, and what the effect can be on other programs.
Update cursors
An update cursor is a special kind of cursor that applications can use when the
row might potentially be updated. To use an update cursor, execute SELECT FOR
UPDATE in your application. Update cursors use promotable locks; that is, the
database server places an update lock (meaning other users can still view the row)
when the application fetches the row, but the lock is changed to an exclusive lock
when the application updates the row using an update cursor and
UPDATE...WHERE CURRENT OF.
The advantage of using an update cursor is that you can view the row with the
confidence that other users cannot change it or view it with an update cursor
while you are viewing it and before you update it.
The pseudocode in the following figure shows when the database server places
and releases locks with a cursor.
This feature lets you avoid the overhead of Repeatable Read isolation level or
workarounds such as dummy updates on a row. When the RETAIN UPDATE
LOCKS feature is turned on and an update lock is implicitly placed on a row
during a fetch of a SELECT...FOR UPDATE statement, the update lock is not
released until the end of the transaction. With the RETAIN UPDATE LOCKS
feature, only update locks are held until end of transaction, whereas the Repeatable
Read isolation level holds both update locks and shared locks until end of
transaction.
The following example shows how to use the RETAIN UPDATE LOCKS clause
when you set the isolation level to Committed Read.
SET ISOLATION TO COMMITTED READ RETAIN UPDATE LOCKS
To turn off the RETAIN UPDATE LOCKS feature, set the isolation level without
the RETAIN UPDATE LOCKS clause. When you turn off the feature, update locks
are not released directly. However, from this point on, a subsequent fetch releases
the update lock of the immediately preceding fetch but not of earlier fetch
operations. A close cursor releases the update lock on the current row.
For more information about how to use the RETAIN UPDATE LOCKS feature
when you specify an isolation level, see the IBM Informix Guide to SQL: Syntax.
When the database server removes the exclusive lock depends on whether the
database supports transaction logging.
For more information about these exclusive locks, see Locks placed with INSERT,
UPDATE, and DELETE statements.
In addition, the lock table might store intent locks. An intent lock can be an intent
shared (IS), intent exclusive (IX), or intent shared exclusive (SIX). An intent lock is
the lock the database server (lock manager) places on a higher granularity object
when a lower granularity object needs to be locked. For example, when a user
locks a row or page in Shared lock mode, the database server places an IS (intent
shared) lock on the table to provide an instant check that no other user holds an X
lock on the table. In this case, intent locks are placed on the table only and not on
the row or page. Intent locks can be placed at the level of a row, page, or table
only.
The user does not have direct control over intent locks; the lock manager internally
manages all intent locks.
The following table shows what locks a user (or the database server) can place if
another user (or the database server) holds a certain type of lock. For example, if
one user holds an exclusive lock on an item, another user requesting any kind of
lock (exclusive, update or shared) receives an error. In addition, the database
server is unable to place any intent locks on an item if a user holds an exclusive
lock on the item.
For information about how locking affects performance, see your IBM Informix
Performance Guide.
You can execute stored routines in a read-only transaction as long as the routine
does not try to perform any restricted operations.
For information about how to use the SET TRANSACTION statement to specify an
access mode, see the IBM Informix Guide to SQL: Syntax.
You choose among these results with the SET LOCK MODE statement.
When this lock mode is set, your program usually ignores the existence of other
concurrent programs. When your program needs to access a row that another
program has locked, it waits until the lock is removed, then proceeds. In most
cases, the delays are imperceptible.
You can also wait for a specific number of seconds, as in the following example:
SET LOCK MODE TO WAIT 20
When the program requests a locked row, it immediately receives an error code
(for example, error -107 Record is locked), and the current SQL statement
terminates. The program must roll back its current transaction and try again.
This statement places an upper limit of 17 seconds on the length of any wait. If a
lock is not removed in that time, the error code is returned.
Handle a deadlock
A deadlock is a situation in which a pair of programs blocks the progress of each
other. Each program has a lock on some object that the other program wants to
access. A deadlock arises only when all programs concerned set their lock modes
to wait for locks.
An IBM Informix database server detects deadlocks immediately when they only
involve data at a single network server. It prevents the deadlock from occurring by
returning an error code (error -143 ISAM error: deadlock detected) to the second
program to request a lock. The error code is the one the program receives if it sets
its lock mode to not wait for locks. If your program receives an error code related
to locks even after it sets lock mode to wait, you know the cause is an impending
deadlock.
In other words, when external databases are involved, every program runs with a
maximum lock-waiting time. The DBA can set or modify the maximum for the
database server.
Simple concurrency
If you are not sure which choice to make concerning locking and concurrency, you
can use the following guideline: If your application accesses non-static tables, and
there is no risk of deadlock, have your program execute the following statements
when it starts up (immediately after the first CONNECT or DATABASE statement):
SET LOCK MODE TO WAIT
SET ISOLATION TO REPEATABLE READ
Ignore the return codes from both statements. Proceed as if no other programs
exist. If no performance problems arise, you do not need to read this section again.
The rules that are used to handle transactions reliably are normal with most
database systems that support transactions, and they do not cause any trouble for
most applications. However, circumstances exist in which using standard
transactions with cursors is not possible. For example, the following code works
fine without transactions. However, when transactions are added, closing the
cursor conflicts with using two cursors simultaneously.
EXEC SQL DECLARE master CURSOR FOR
EXEC SQL DECLARE detail CURSOR FOR FOR UPDATE
EXEC SQL OPEN master;
while(SQLCODE == 0)
{
EXEC SQL FETCH master INTO
if(SQLCODE == 0)
{
EXEC SQL BEGIN WORK;
EXEC SQL OPEN detail USING
EXEC SQL FETCH detail
EXEC SQL UPDATE WHERE CURRENT OF detail
EXEC SQL COMMIT WORK;
}
}
EXEC SQL CLOSE master;
In this design, one cursor is used to scan a table. Selected records are used as the
basis for updating a different table. The problem is that when each update is
treated as a separate transaction (as the pseudocode in the previous example
shows), the COMMIT WORK statement following the UPDATE closes all cursors,
including the master cursor.
The simplest alternative is to move the COMMIT WORK and BEGIN WORK
statements to be the last and first statements, respectively, so that the entire scan
over the master table is one large transaction. Treating the scan of the master table
as one large transaction is sometimes possible, but it can become impractical if
many rows need to be updated. The number of locks can be too large, and they are
held for the duration of the program.
A solution that IBM Informix database servers support is to add the keywords
WITH HOLD to the declaration of the master cursor. Such a cursor is referred to as
a hold cursor and is not closed at the end of a transaction. The database server still
closes all other cursors, and it still releases all locks, but the hold cursor remains
open until it is explicitly closed.
Before you attempt to use a hold cursor, you must be sure that you understand the
locking mechanism described here, and you must also understand the programs
that are running concurrently. Whenever COMMIT WORK is executed, all locks are
released, including any locks placed on rows fetched through the hold cursor.
The removal of locks has little importance if the cursor is used as intended, for a
single forward scan over a table. However, you can specify WITH HOLD for any
Use SQL to turn on or turn off statement caching for an individual database
session when statement caching is enabled for the database server. The following
statement shows how to use SQL to turn on caching for the current database
session:
SET STATEMENT CACHE ON
The following statement shows how to use SQL to turn off caching for the current
database session:
SET STATEMENT CACHE OFF
If you attempt to turn on or turn off statement caching when caching is disabled,
the database server returns an error.
For information about syntax for the SET STATEMENT CACHE statement, see the
IBM Informix Guide to SQL: Syntax. For information about the STMT_CACHE and
STMT_CACHE_SIZE configuration parameters, see the IBM Informix Administrator's
Reference and your IBM Informix Performance Guide. For information about the
STMT_CACHE environment variable, see the IBM Informix Guide to SQL: Reference.
Summary
Whenever multiple programs have access to a database concurrently (and when at
least one of them can modify data), all programs must allow for the possibility that
another program can change the data even as they read it. The database server
provides a mechanism of locks and isolation levels that usually allow programs to
run as if they were alone with the data.
The SET STATEMENT CACHE statement allows you to store in a buffer identical
SQL statements that are used repeatedly. When statement caching is turned on, the
database server stores the identical statements so they can be reused among
different user sessions without the need for per-session memory allocation.
Routines written in SQL are parsed, optimized as far as possible, and then stored
in the system catalog tables in executable format. An SPL routine might be a good
choice for SQL-intensive tasks. SPL routines can execute routines written in C or
other external languages, and external routines can execute SPL routines.
You can use SPL routines to perform any task that you can perform in SQL and to
expand what you can accomplish with SQL alone. Because SPL is a language
native to the database, and because SPL routines are parsed and optimized when
they are created rather than at runtime, SPL routines can improve performance for
some tasks. SPL routines can also reduce traffic between a client application and
the database server and reduce program complexity.
The syntax for each SPL statement is described in the IBM Informix Guide to SQL:
Syntax. Examples accompany the syntax for each statement.
You use SQL and SPL statements to write an SPL routine. SPL statements can be
used only inside the CREATE PROCEDURE, CREATE PROCEDURE FROM,
CREATE FUNCTION, and CREATE FUNCTION FROM statements. All these
statements are available with SQL APIs such as IBM Informix ESQL/C. The
CREATE PROCEDURE and CREATE FUNCTION statements are available with
DB-Access.
To list all SPL routines in a database, run this command, which creates and
displays the schema for a database:
dbschema -d database_name -f all
Because an SPL routine is stored in an executable format, you can use it to execute
frequently repeated tasks to improve performance. When you execute an SPL
routine rather than straight SQL code, you can bypass repeated parsing, validity
checking, and query optimization.
You can also issue SQL statements in an SPL routine to hide those SQL statements
from a database user. Rather than having all users learn how to use SQL, one
experienced SQL user can write an SPL routine to encapsulate an SQL activity and
let others know that the routine is stored in the database so that they can execute
it.
You can write an SPL routine to be run with the DBA privilege by a user who does
not have the DBA privilege. This feature allows you to limit and control access to
data in the database. Alternatively, an SPL routine can monitor the users who
access certain tables or data. For more information about how to use SPL routines
to control access to data, see the IBM Informix Database Design and Implementation
Guide.
The maximum size of an SPL routine is 64 kilobytes. The maximum size includes
any SPL global variables in the database and the routine itself.
To create an SPL function that returns one or more values, start with the CREATE
FUNCTION statement and end with the END FUNCTION keyword. The following
figure shows how to begin and end an SPL function.
The entire text of an SPL routine, including spaces and tabs, must not exceed 64
kilobytes. In SPL routines, the END PROCEDURE or END FUNCTION keywords
are required.
Important: For compatibility with earlier IBM Informix products, you can use
CREATE PROCEDURE with a RETURNING clause to create a user-defined routine
that returns a value. Your code will be easier to read and to maintain, however, it
you use CREATE PROCEDURE for SPL routines that do not return values (SPL
procedures) and CREATE FUNCTION for SPL routines that return one or more
values (SPL functions).
IBM Informix allows you to create more than one SPL routine with the same name
but with different parameters. This feature is known as routine overloading. For
example, you might create each of the following SPL routines in your database:
CREATE PROCEDURE multiply (a INT, b FLOAT)
CREATE PROCEDURE multiply (a INT, b SMALLINT)
CREATE PROCEDURE multiply (a REAL, b REAL)
If you call a routine with the name multiply(), the database server evaluates the
name of the routine and its arguments to determine which routine to execute.
Routine resolution is the process in which the database server searches for a routine
signature that it can use, given the name of the routine and a list of arguments.
Every routine has a signature that uniquely identifies the routine based on the
following information:
v The type of routine (procedure or function)
v The routine name
v The number of parameters
v The data types of the parameters
A specific name can be up to 128 bytes long. The following figure shows how to
define the specific name calc1 in a CREATE FUNCTION statement that creates the
calculate() function.
Because the owner bsmith has given the SPL function the specific name calc1, no
other user can define a routine—SPL or external—with the specific name calc1.
Now you can refer to the routine as bsmith.calculate or with the SPECIFIC
keyword calc1 in any statement that requires the SPECIFIC keyword.
A parameter to an SPL routine must have a name and can be defined with a
default value. The following are the categories of data types that a parameter can
specify:
v Built-in data types
v Opaque data types
v Distinct data types
v Row types
The parameter list cannot specify any of the following data types directly:
v SERIAL
v SERIAL8
v BIGSERIAL
v TEXT
v BYTE
For the serial data types, however, a routine can return numerically equivalent
values that are cast to a corresponding integer type (INT, INT8, or BIGINT).
Similarly, for a routine to support the simple large object data types, the parameter
list can include the REFERENCES keyword to return a descriptor that points to the
storage location of the TEXT or BYTE object.
If you define a parameter with a default value, the user can execute the SPL
routine with or without the corresponding argument. If the user executes the SPL
routine without the argument, the database server assigns the parameter the
default value as an argument.
When you invoke an SPL routine, you can give an argument a NULL value. SPL
routines handle NULL values by default. However, you cannot give an argument a
NULL value if the argument is a collection element.
Although you cannot define a parameter with a simple large object (a large object
that contains TEXT or BYTE data types), you can use the REFERENCES keyword
to define a parameter that points to a simple large object, as the following figure
shows.
The REFERENCES keyword means that the SPL routine is passed a descriptor that
contains a pointer to the simple large object, not the object itself.
Undefined arguments:
When you invoke an SPL routine, you can specify all, some, or none of the defined
arguments. If you do not specify an argument, and if its corresponding parameter
does not have a default value, the argument, which is used as a variable within the
SPL routine, is given a status of undefined.
Undefined is a special status used for SPL variables that have no value. The SPL
routine executes without error, as long as you do not attempt to use the variable
that has the status undefined in the body of the routine.
The undefined status is not the same as a NULL value. (The NULL value means
that the value is not known, or does not exist, or is not applicable.)
Tip: If you use the CREATE PROCEDURE statement to create an SPL routine, you
have the option of specifying a return clause. Your code will be easier to read and
to maintain, however, it you instead use the CREATE FUNCTION statement to
create a routine that returns values.
To specify a return clause, use the RETURNING or RETURNS keyword with a list
of data types the routine will return. The data types can be any SQL data types
except SERIAL, SERIAL8, TEXT, or BYTE.
The return clause in the following figure specifies that the SPL routine will return
an INT value and a REAL value.
After you specify a return clause, you must also specify a RETURN statement in
the body of the routine that explicitly returns the values to the calling routine. For
more information on writing the RETURN statement, see “Return values from an
SPL function” on page 11-31.
To specify that the function should return a simple large object (a TEXT or BYTE
value), you must use the REFERENCES clause, as in the following figure, because
In addition, although using CREATE FUNCTION for routines that return values is
recommended, you can use CREATE PROCEDURE to create a routine that returns
values and specifies display labels for the values returned.
If you choose to specify a display label for one return value, you must specify a
display label for every return value. In addition, each return value must have a
unique display label.
To add display labels, you must specify a return clause, use the RETURNING
keyword. The return clause in the following figure specifies that the routine will
return an INT value with a serial_num display label, a CHAR value with a name
display label, and an INT value with a points display label. You could use either
CREATE FUNCTION or CREATE PROCEDURE in the following figure.
The returned values and their display labels are shown in the following figure.
Tip: Because you can specify display labels for return values directly in a SELECT
statement, when a SPL routine is used in a SELECT statement, the labels will
display as expression. For more information on specifying display labels for return
values in a SELECT statement, see Chapter 2, “Compose SELECT statements,” on
page 2-1.
Add a modifier
When you write SPL functions, you can use the WITH clause to add a modifier to
the CREATE FUNCTION statement. In the WITH clause, you can specify the
COMMUTATOR or NEGATOR functions. The other modifiers are for external
routines.
Restriction: You can use the COMMUTATOR or NEGATOR modifiers with SPL
functions only. You cannot use any modifiers with SPL procedures.
The COMMUTATOR modifier allows you to specify an SPL function that is the
commutator function of the SPL function you are creating. A commutator function
accepts the same arguments as the SPL function you are creating, but in opposite
order, and returns the same value. The commutator function might be more cost
effective for the SQL optimizer to execute.
For example, the functions lessthan(a,b), which returns TRUE if a is less than b,
and greaterthan(b,a), which returns TRUE if b is greater than or equal to a, are
commutator functions. The following figure uses the WITH clause to define a
commutator function.
For a detailed description of granting privileges, see the description of the GRANT
statement in the IBM Informix Guide to SQL: Syntax.
The NEGATOR modifier is available for Boolean functions. Two Boolean functions
are negator functions if they take the same arguments, in the same order, and return
complementary Boolean values.
For example, the functions equal(a,b), which returns TRUE if a is equal to b, and
notequal(a,b), which returns FALSE if a is equal to b, are negator functions. The
optimizer might choose to execute the negator function you specify if it is less
expensive than the original function.
Tip: By default, any SPL routine can handle NULL values that are passed to it in
the argument list. In other words, the HANDLESNULLS modifier is set to YES for
SPL routines, and you cannot change its value.
For more information on the COMMUTATOR and NEGATOR modifiers, see the
Routine Modifier segment in the IBM Informix Guide to SQL: Syntax.
The DOCUMENT clause lets you add comments to your SPL routine that another
routine can select from the system catalog tables, if needed. The DOCUMENT
clause in the following figure contains a usage statement that shows a user how to
run the SPL routine.
Figure 11-14. Usage statement that shows a user how to run the SPL routine.
Remember to place single or double quotation marks around the literal clause. If
the literal clause extends past one line, place quotation marks around each line.
Always remember to place single or double quotation marks around the file name
or path name.
Add comments
You can add a comment to any line of an SPL routine, even a blank line.
If you use braces or C-style comment indicators to delimit the text of a comment,
the opening indicator must be in the same style as the closing indicator.
The following example shows how the previous SQL statement looks in an
Informix ESQL/C program:
/* This program creates whatever routine is in *
* the file ’read_add_source’.
*/
#include <stdio.h>
EXEC SQL include sqlca;
EXEC SQL include sqlda;
EXEC SQL include datetime;
/* Program to create a routine from the pwd */
In general, use DROP PROCEDURE with an SPL procedure name and DROP
FUNCTION with an SPL function name, as the following figure shows.
Tip: You can also use DROP PROCEDURE with a function name to drop an SPL
function. However, it is recommended that you use DROP PROCEDURE only with
procedure names and DROP FUNCTION only with function names.
However, if the database has other routines of the same name (overloaded
routines), you cannot drop the SPL routine by its routine name alone. To drop a
routine that has been overloaded, you must specify either its signature or its
specific name. The following figure shows two ways that you might drop a routine
that is overloaded.
If you do not know the type of a routine (function or procedure), you can use the
DROP ROUTINE statement to drop it. DROP ROUTINE works with either
functions or procedures. DROP ROUTINE also has a SPECIFIC keyword, as the
following figure shows.
Before you drop an SPL routine stored on a remote database server, be aware of
the following restriction. You can drop an SPL routine with a fully qualified
routine name in the form database@dbservername:owner.routinename only if the
routine name alone, without its arguments, is enough to identify the routine. SPL
The value of a variable is held in memory; the variable is not a database object.
Therefore, rolling back a transaction does not restore the values of SPL variables.
To define a variable in an SPL routine, use the DEFINE statement. DEFINE is not
an executable statement. DEFINE must appear after the CREATE PROCEDURE
statement and before any other statements. The examples in the following figure
are all legal variable definitions.
DEFINE a INT;
DEFINE person person_t;
DEFINE GLOBAL gl_out INT DEFAULT 13;
For more information on DEFINE, see the description in the IBM Informix Guide to
SQL: Syntax.
An SPL variable has a name and a data type. The variable name must be a valid
identifier, as described in the Identifier segment in the IBM Informix Guide to SQL:
Syntax.
You can define a local variable on any of the following data types:
v Built-in data types (except SERIAL, SERIAL8, BIGSERIAL, TEXT, or BYTE)
The scope of a local variable is the statement block in which it is declared. You can
use the same variable name outside the statement block with a different definition.
For more information on defining global variables, see “Declare global variables”
on page 11-20.
In the beginning of the SPL procedure in the following figure, the integer variables
x, y, and z are defined and initialized.
The BEGIN and END statements mark a nested statement block in which the
integer variables x and q are defined as well as the CHAR variable z. Within the
nested block, the redefined variable x masks the original variable x. After the END
statement, which marks the end of the nested block, the original value of x is
accessible again.
DEFINE x INT;
DEFINE y INT8;
DEFINE name CHAR(15);
DEFINE this_day DATETIME YEAR TO DAY;
Figure 11-25. Use the REFERENCES keyword before the data type.
You must always define the elements of a collection variable as NOT NULL. In this
example, the variable a is defined to hold a SET of non-NULL integers; the
variable b holds a MULTISET of non-NULL row types; and the variable c holds a
LIST of non-NULL sets of non-NULL decimal values.
If you define a variable with the name of a named row type, the variable can only
hold data of that row type. In the following figure, the person variable can only
hold data of employee_t type.
To define a variable that holds data stored in an unnamed row type, use the ROW
keyword followed by the fields of the row type, as the following figure shows.
Figure 11-29. Use the ROW keyword followed by the fields of the row type.
Important: Before you can use a row type variable, you must initialize the row
variable with a LET statement or SELECTINTO statement.
If you define an opaque data type named point and a distinct data type named
centerpoint, you can define SPL variables to hold data from the two types, as the
following figure shows.
DEFINE a point;
DEFINE b centerpoint;
Figure 11-31. Defining SPL variables to hold opaque and distinct data types.
The variable a can only hold data of type point, and b can only hold data of type
centerpoint.
If the column contains a collection, row type, or nested complex type, the variable
has the complex or nested complex type defined in the column.
In the following figure, the variable loc1 defines the data type for the locations
column in the image table.
Figure 11-32. Define the loc1 data type for the locations column in the image table.
For example, the statement in the following figure defines length as an SPL
procedure or SPL function, not as the built-in LENGTH function.
This definition disables the built-in LENGTH function within the scope of the
statement block. You would use such a definition if you had already created an
SPL or external routine with the name LENGTH.
Because IBM Informix supports routine overloading, you can define more than one
SPL routine or external routine with the same name. If you call any routine from
an SPL routine, Informix determines which routine to use, based on the arguments
specified and the routine determination rules. For information about routine
overloading and routine determination, see IBM Informix User-Defined Routines and
Data Types Developer's Guide.
Tip: If you create an SPL routine with the same name as an aggregate function
(SUM, MAX, MIN, AVG, COUNT) or with the name extend, you must qualify the
routine name with an owner name.
Subscripts must always be constants. You cannot use variables as subscripts. The
following figure illustrates how to use a subscript with a CHAR(15) variable.
In general, avoid using an ANSI-reserved word for the name of the variable. For
example, you cannot define a variable with the name count or max because they
are the names of aggregate functions. For a list of the reserved keywords that you
should avoid using as variable names, see the Identifier segment in the IBM
Informix Guide to SQL: Syntax.
For information about ambiguities between SPL routine names and SQL function
names, see the IBM Informix Guide to SQL: Syntax.
If you use the same identifier for an SPL variable that you use for a column name,
the database server assumes that each instance of the identifier is a variable.
Qualify the column name with the table name, using dot notation, in order to use
the identifier as a column name.
If you use the same identifier for an SPL variable as for an SQL function, the
database server assumes that an instance of the identifier is a variable and
disallows the use of the SQL function. You cannot use the SQL function within the
block of code in which the variable is defined. The example in the following figure
shows a block within an SPL procedure in which the variable called user is
defined. This definition disallows the use of the USER function in the BEGIN END
BEGIN
DEFINE user CHAR(15); -- disables user function
LET user = ’Miller’;
LET name = user; -- assigns ’Miller’ to variable name
END
. . .
LET name2 = user; -- SQL function again
Figure 11-36. A procedure that disallows the use of the USER function in the BEGIN END
block.
The following figure shows two SPL functions that share a global variable.
Although you must define a global variable with a default value, the variable is
only set to the default the first time you use it. If you execute the two functions in
the following figure in the order given, the value of gvar would be 4.
But if you execute the functions in the opposite order, as the following figure
shows, the value of gvar would be 7.
You can assign a value to a routine variable in any of the following ways:
v Use a LET statement.
v Use a SELECT INTO statement.
v Use a CALL statement with a procedure that has a RETURNING clause.
v Use an EXECUTE PROCEDURE INTO or EXECUTE FUNCTION INTO
statement.
LET a = 5;
LET b = 6; LET c = 10;
LET a,b = 10,c+d;
LET a,b = (SELECT cola,colb
FROM tab1 WHERE cola=10);
LET d = func1(x,y);
DEFINE a address_t;
LET a = ROW (’A Street’, ’Nowhere’, ’AA’,
ROW(NULL, NULL))::address_t
After you define and initialize the row-type variable, you can write the LET
statements that the following figure shows.
Suppose you define an opaque-type point that contains two values that define a
two-dimensional point, and the text representation of the values is '(x,y)'. You
might also have a function circum() that calculates the circumference of a circle,
given the point '(x,y)' and a radius r.
If you define an opaque-type center that defines a point as the center of a circle,
and a function circum() that calculates the circumference of a circle, based on a
point and the radius, you can write variable declarations for each. In the following
figure, c is an opaque type variable and d holds the value that the external
function circum() returns.
DEFINE c point;
DEFINE r REAL;
DEFINE d REAL;
LET c = ’(29.9,1.0)’ ;
-- Assign a value to an opaque type variable
LET d = circum( c, r );
-- Assign a value returned from circum()
The IBM Informix Guide to SQL: Syntax describes in detail the syntax of the LET
statement.
Figure 11-44. Fetch a value from the database and assign it directly to a variable.
CALL read_address(’Smith’)
RETURNING p_fname, p_lname, p_add, p_city,
p_state, p_zip;
Figure 11-45. Return the full name and address from the SPL function.
BEGIN
DEFINE distance INT;
LET distance = 2;
END
The explicit statement block allows you to define variables or processing that are
valid only within the statement block. For example, you can define or redefine
variables, or handle exceptions differently, for just the scope of the explicit
statement block.
The SPL function in the following figure has an explicit statement block that
redefines a variable defined in the implicit block.
END FUNCTION;
Figure 11-47. An explicit statement block that redefines a variable defined in the implicit
block.
In this example, the implicit statement block defines the variable distance and
gives it a value of 37. The explicit statement block defines a different variable
named distance and gives it a value of 2. However, the RETURN statement returns
the value stored in the first distance variable, or 37.
The FOREACH loop declares and opens a cursor, fetches rows from the database,
works on each item in the group, and then closes the cursor. You must declare a
cursor if a SELECT, EXECUTE PROCEDURE, or EXECUTE FUNCTION statement
might return more than one row. After you declare the cursor, you place the
SELECT, EXECUTE PROCEDURE, or EXECUTE FUNCTION statement within it.
An SPL routine that returns a group of rows is called a cursor routine because you
must use a cursor to access the data it returns. An SPL routine that returns no
value, a single value, or any other value that does not require a cursor is called a
noncursor routine. The FOREACH loop declares and opens a cursor, fetches rows or
a collection from the database, works on each item in the group, and then closes
the cursor. You must declare a cursor if a SELECT, EXECUTE PROCEDURE, or
EXECUTE FUNCTION statement might return more than one row or a collection.
FOREACH
EXECUTE FUNCTION name() INTO variable;
END FOREACH;
The following figure creates a routine that uses a FOREACH loop to operate on the
employee table.
END PROCEDURE;
The routine in preceding figure performs these tasks within the FOREACH loop:
v Declares a cursor
v Selects one salary value at a time from employee
v Increases the salary by a percentage
v Updates employee with the new salary
v Fetches the next salary value
The SELECT statement is placed within a cursor because it returns all the salaries
in the table greater than 35000.
The WHERE CURRENT OF clause in the UPDATE statement updates only the row
on which the cursor is currently positioned, and sets an update cursor on the
BEGIN WORK;
FOREACH sal_cursor FOR
SELECT salary INTO s FROM employee WHERE salary > 35000;
LET s = s + s * ( pct/100 );
UPDATE employee SET salary = s WHERE CURRENT OF sal_cursor
END FOREACH;
COMMIT WORK;
For each iteration of the FOREACH loop in the preceding figure, a new lock is
acquired (if you use row level locking). The COMMIT WORK statement releases all
of the locks (and commits all of the updated rows as a single transaction) after the
last iteration of the FOREACH loop.
To commit an updated row after each iteration of the loop, you must open the
cursor WITH HOLD, and include the BEGIN WORK and COMMIT WORK
statements within the FOREACH loop, as in the following SPL routine.
Figure 11-51. Committing an updated row after each iteration of the loop.
Suppose you define a table named manager with the columns that the following
figure shows.
The following SPL routine uses an IF - ELIF - ELSE structure to check the number
of elements in the SET in the direct_reports column and call various external
routines based on the results.
END FUNCTION;
Figure 11-54. An IF - ELIF - ELSE structure to check the number of elements in the SET.
The cardinality() function counts the number of elements that a collection contains.
For more information, see “Cardinality function” on page 4-13.
An IF - ELIF - ELSE structure in an SPL routine has up to the following four parts:
v An IF THEN condition
If the condition following the IF statement is TRUE, the routine executes the
statements in the IF block. If the condition is false, the routine evaluates the
ELIF condition.
The expression in an IF statement can be any valid condition, as the Condition
segment of the IBM Informix Guide to SQL: Syntax describes. For the complete
syntax and a detailed discussion of the IF statement, see the IBM Informix Guide
to SQL: Syntax.
v One or more ELIF conditions (optional)
The routine evaluates the ELIF condition only if the IF condition is false. If the
ELIF condition is true, the routine executes the statements in the ELIF block. If
the ELIF condition is false, the routine either evaluates the next ELIF block or
executes the ELSE statement.
v An ELSE condition (optional)
The routine executes the statements in the ELSE block if the IF condition and all
of the ELIF conditions are false.
v An END IF statement
The END IF statement ends the statement block.
The following figure shows a valid WHILE condition. The routine executes the
WHILE loop as long as the condition specified in the WHILE statement is true.
DEFINE i INTEGER;
LET i = 1;
END PROCEDURE;
Figure 11-55. Routine to execute the WHILE loop as long as the condition specified in the
WHILE statement is true.
The SPL routine in the previous figure accepts an integer as an argument and then
inserts an integer value into the numbers column of table1 each time it executes
the WHILE loop. The values inserted start at 1 and increase to num - 1.
Be careful that you do not create an endless loop, as the following figure shows.
DEFINE i INTEGER;
LET i = 1;
WHILE ( 1 = 1 ) -- don’t do this!
LET i = i + 1;
INSERT INTO table1 VALUES (i);
END WHILE;
END PROCEDURE;
Figure 11-56. Routine to accept an integer as an argument and then insert an integer value
into the numbers column.
A FOR loop extends from a FOR statement to an END FOR statement and executes
for a specified number of iterations, which are defined in the FOR statement. The
following figure shows several ways to define the iterations in the FOR loop.
For each iteration of the FOR loop, the iteration variable (declared as i in the
examples that follow) is reset, and the statements within the loop are executed
with the new value of the variable.
FOR i = 1 TO 10 STEP 2
. . .
END FOR;
FOR i IN (2,4,8,14,22,32)
. . .
END FOR;
In the first example, the SPL procedure executes the FOR loop as long as i is
between 1 and 10, inclusive. In the second example, i steps from 1 to 3, 5, 7, and so
on, but never exceeds 10. The third example checks whether i is within a defined
set of values. In the fourth example, the SPL procedure executes the loop when i is
1, 6, 11, 16, 20, 15, 10, 5, 1, 2, 3, 4, or 5—in other words, 13 times.
Tip: The main difference between a WHILE loop and a FOR loop is that a FOR
loop is guaranteed to finish, but a WHILE loop is not. The FOR statement specifies
the exact number of times the loop executes, unless a statement causes the routine
to exit the loop. With WHILE, it is possible to create an endless loop.
Exit a loop
In a FOR, FOREACH, LOOP, or WHILE loop that has no label, you can use the
CONTINUE or EXIT statement to control the execution of the loop.
v CONTINUE causes the routine to skip the statements in the rest of the loop and
move to the next iteration of the FOR, LOOP, or WHILE statement.
v EXIT ends the loop and causes the routine to continue executing with the first
statement following the END FOR, the END LOOP, or the END WHILE
keywords.
For more information about loops in SPL routines, including labelled loops, see
IBM Informix Guide to SQL: Syntax.
The following figure shows examples of CONTINUE and EXIT within a FOR loop.
END FOR;
Tip: You can use CONTINUE and EXIT to improve the performance of SPL
routines so that loops do not execute unnecessarily.
Tip: You can define a routine with the CREATE PROCEDURE statement that
returns values, but in that case, the routine is actually a function. It is
recommended that you use the CREATE FUNCTION statement when the routine
returns values.
After you define a return clause (with a RETURNING statement), the SPL function
can return values that match those specified in number and data type, or no values
at all. If you specify a return clause, and the SPL routine returns no actual values,
it is still considered a function. In that case, the routine returns a NULL value for
each value defined in the return clause.
An SPL function can return variables, expressions, or the result of another function
call. If the SPL function returns a variable, the function must first assign the
variable a value by one of the following methods:
v A LET statement
v A default value
v A SELECT statement
v Another function that passes a value into the variable
Important: The return value for an SPL function must be a specific data type. You
cannot specify a generic row or generic collection data type as a return type.
RETURN result;
END FUNCTION;
DEFINE n VARCHAR(30);
DEFINE b DATE;
END FUNCTION;
Figure 11-60. SPL function that returns two column values from a single row of a table.
The function returns two values (a name and birthdate) to the calling routine from
one row of the emp_tab table. In this case, the calling routine must be prepared to
handle the VARCHAR and DATE values returned.
The following figure shows an SPL function that returns more than one value from
more than one row.
Figure 11-61. SPL function that returns more than one value from more than one row.
In preceding figure, the SELECT statement fetches two values from the set of rows
whose employee number is higher than the number the user enters. The set of
rows that satisfy the condition could contain one row, many rows, or zero rows.
Because the SELECT statement can return many rows, it is placed within a cursor.
Tip: When a statement within an SPL routine returns no rows, the corresponding
SPL variables are assigned NULL values.
The RETURN statement uses the WITH RESUME keywords. When RETURN
WITH RESUME is executed, control is returned to the calling routine. But the next
time the SPL function is called (by a FETCH or the next iteration of a cursor in the
calling routine), all the variables in the SPL function keep their same values, and
execution continues at the statement immediately following the RETURN WITH
RESUME statement.
If your SPL routine returns multiple values, the calling routine must be able to
handle the multiple values through a cursor or loop, as follows:
v If the calling routine is an SPL routine, it needs a FOREACH loop.
v If the calling routine is an Informix ESQL/C program, it needs a cursor declared
with the DECLARE statement.
v If the calling routine is an external routine, it needs a cursor or loop appropriate
to the language in which the routine is written.
Important: The values returned by a UDR from external databases of a local server
must be built-in data types or UDTs explicitly cast to built-in types or DISTINCT
types based on built-in types and explicitly cast to built-in types. In addition, you
must define the UDR and all the casts in the participating databases.
database db2;
create table rtab1(r1col1 boolean, r1col2 blob, r1col3 integer)
put r1col2 in (sbsp);
create table rtab2(r2col1 lvarchar, r2col2 clob) put r2col2 in (sbsp);
create table rtab3(r3col1 integer, r3col2 boolean,
r3col3 lvarchar, r3col4 circle);
The following figure defines a row type salary_t and an emp_info table, which are
the examples that this section uses.
The emp_info table has columns for the employee name and salary information.
The SELECT statement selects a row from the salary column of emp_info table
into the ROW variable row_var.
The emp_raise procedure uses SPL dot notation to directly access the base field of
the variable row_var. In this case, the dot notation means variable.field. The
emp_raise procedure recalculates the value of row_var.base as (row_var.base *
pct). The procedure then updates the salary column of the emp_info table with
the new row_var value.
Important: A row-type variable must be initialized as a row before its fields can be
set or referenced. You can initialize a row-type variable with a SELECT INTO
statement or LET statement.
Handle collections
A collection is a group of elements of the same data type, such as a SET,
MULTISET, or LIST.
The basics of handling collections in SPL programs are illustrated with the
numbers table, as the following figure shows.
The primes and evens columns hold simple collections. The twin_primes column
holds a nested collection, a LIST of SETs. (Twin prime numbers are pairs of
consecutive prime numbers whose difference is 2, such as 5 and 7, or 11 and 13.
The twin_primes column is designed to allow you to enter such pairs.
Some examples in this chapter use the polygons table in the following figure to
illustrate how to manipulate collections. The polygons table contains a collection to
represent two-dimensional graphical data. For example, suppose that you define an
opaque data type named point that has two double-precision values that represent
the x and y coordinates of a two-dimensional point whose coordinates might be
represented as ’1.0, 3.0’. Using the point data type, you can create a table that
contains a set of points that define a polygon.
The definition column in the polygons table contains a simple collection, a SET of
point values.
After you take these initial steps, you can insert elements into the collection or
select and handle elements that are already in the collection.
Each of these steps is explained in the following sections, using the numbers table
as an example.
The DEFINE statement declares a collection variable p_coll, whose type matches
the data type of the collection stored in the primes column.
For example, to hold an element of the SET in the primes column, use an element
variable declaration such as the one that the following figure shows.
DEFINE p INTEGER;
The variable s holds a SET of integers. Each SET is an element of the LIST stored
in twin_primes.
For example, to select the collection stored in one row of the primes column of
numbers, add a SELECT statement, such as the one that the following figure
shows, to your SPL routine.
Figure 11-69. Add a SELECT statement to select the collection stored in one row.
The WHERE clause in the SELECT statement specifies that you want to select the
collection stored in just one row of numbers. The statement places the collection
into the collection variable p_coll, which Figure 11-66 on page 11-37 declares.
The variable p_coll now holds a collection from the primes column, which could
contain the value SET {5,7,31,19,13}.
5
7
31
19
13
After the insert, p_coll might look like the virtual table that the following figure
shows.
Because the collection is a SET, the new value is added to the collection, but the
position of the new element is undefined. The same principle is true for a
MULTISET.
Tip: You can only insert one value at a time into a simple collection.
The following figure shows the statements you need to define a collection variable
and select a LIST from the numbers table into the collection variable.
At this point, the value of e_coll might be LIST {2,4,6,8,10}. Because e_coll holds
a LIST, each element has a numbered position in the list. To add an element at a
specific point in a LIST, add an AT position clause to the INSERT statement, as the
following figure shows.
Now the LIST in e_coll has the elements {2,4,12,6,8,10}, in that order.
The value you enter for the position in the AT clause can be a number or a variable,
but it must have an INTEGER or SMALLINT data type. You cannot use a letter,
floating-point number, decimal value, or expression.
Suppose that in the numbers table, the evens column of the row whose id column
is 99 still contains the collection LIST {2,4,6,8,10}. This time, you want to add the
element 12 at the end of the LIST. You can do so with the SPL procedure
end_of_list, as the following figure shows.
DEFINE n SMALLINT;
DEFINE list_var LIST(INTEGER NOT NULL);
LET n = n + 1;
END PROCEDURE;
In end_of_list, the variable n holds the value that cardinality() returns, that is, the
count of the items in the LIST. The LET statement increments n, so that the
INSERT statement can insert a value at the last position of the LIST. The SELECT
statement selects the collection from one row of the table into the collection
variable list_var. The INSERT statement inserts the element 12 at the end of the
list.
To move through the elements of a collection, you first need to declare a cursor
using a FOREACH statement, just as you would declare a cursor to move through
a set of rows. The following figure shows the FOREACH and END FOREACH
The FOREACH statement is described in “The FOREACH loop” on page 11-24 and
the IBM Informix Guide to SQL: Syntax.
The next topic, “The collection query,” describes the statements that are omitted
between the FOREACH and END FOREACH statements.
The examples in the following sections are based on the polygons table of
Figure 11-65 on page 11-36.
A collection query is a SELECT statement that uses the FROM TABLE keywords
followed by the name of a collection variable. The following figure shows this
structure, which is known as a collection-derived table.
’(3.0,1.0)’
’(8.0,1.0)’
’(3.0,4.0)’
’(8.0,4.0)’
After the first iteration of the FOREACH statement in the previous figure, the
collection query selects the first element in vertexes and stores it in pnt, so that pnt
contains the value '(3.0,1.0)'.
Figure 11-79. Cursor defined with FOREACH and the collection query.
The statements shown above form the framework of an SPL routine that handles
the elements of a collection variable. To decompose a collection into its elements,
use a collection-derived table. After the collection is decomposed into its elements,
the routine can access elements individually as rows of the collection-derived table.
Now that you have selected one element in pnt, you can update or delete that
element, as “Update a collection element” on page 11-45 and “Delete a collection
element” on page 11-43 describe.
For the complete syntax of the collection query, see the SELECT statement in the
IBM Informix Guide to SQL: Syntax. For the syntax of a collection-derived table, see
the Collection-Derived Table segment in the IBM Informix Guide to SQL: Syntax.
Tip: If you are selecting from a collection that contains no elements or zero
elements, you can use a collection query without declaring a cursor. However, if
the collection contains more than one element and you do not use a cursor, you
will receive an error message.
Attention: In the program fragment above, the database server would have
issued a syntax error if the query (
SELECT * INTO pnt FROM TABLE(vertexes)
The following figure shows a routine that deletes one of the four points in
vertexes, so that the polygon becomes a triangle instead of a rectangle.
The IF THEN ELSE structure tests the value currently in pnt to see if it is the point
’(3,4)’. Note that the expression pnt = ’(3,4)’ calls the instance of the equal()
function defined on the point data type. If the current value in pnt is ’(3,4)’, the
DELETE statement deletes it, and the EXIT FOREACH statement exits the cursor.
Tip: Deleting an element from a collection stored in a collection variable does not
delete it from the collection stored in the database. After you delete the element
from a collection variable, you must update the collection stored in the database
The syntax for the DELETE statement is described in the IBM Informix Guide to
SQL: Syntax.
To update a collection in the database, add an UPDATE statement that sets the
collection column in the table to the contents of the updated collection variable.
For example, the UPDATE statement in the following figure shows how to update
the polygons table to set the definition column to the new collection stored in the
collection variable vertexes.
END PROCEDURE;
Now the shapes() routine is complete. After you run shapes(), the collection stored
in the row whose ID column is 207 is updated so that it contains three values
instead of four.
You can use the shapes() routine as a framework for writing other SPL routines
that manipulate collections.
The elements of the collection now stored in the definition column of row 207 of
the polygons table are listed as follows:
’(3,1)’
’(8,1)’
’(8,4)’
The following figure shows the statements that you might use in an SPL routine to
delete an entire collection.
This form of the DELETE statement deletes the entire collection in the collection
variable vertexes. You cannot use a WHERE clause in a DELETE statement that
uses a collection-derived table.
After the UPDATE statement, the polygons table contains an empty collection
where the id column is equal to 207.
The syntax for the DELETE statement is described in the IBM Informix Guide to
SQL: Syntax.
If you want to update the collection SET{100, 200, 300, 500} to change the value
500 to 400, retrieve the SET from the database into a collection variable and then
declare a cursor to move through the elements in the SET, as the following figure
shows.
Think of the collection-derived table as having one row and looking something like
the following example:
100 200 300 500
In this example, x is a fictitious column name for the “column” that contains the
value 500. You only specify a derived column if you are updating a collection of
built-in, opaque, distinct, or collection type elements. If you are updating a
collection of row types, use a field name instead of a derived column, as “Update a
collection of row types” on page 11-47 describes.
The SPL procedure in the following figure uses statements that are similar to the
ones that Figure 11-83 shows, except that this procedure updates the SET in the
direct_reports column of the manager table with a variable, rather than with a
literal value. Figure 11-53 on page 11-27 defines the manager table.
END PROCEDURE;
The UPDATE statement nested in the FOREACH loop uses the collection- derived
table s and the derived column x. If the current value of n is the same as old, the
UPDATE statement changes it to the value of new. The second UPDATE statement
stores the new collection in the manager table.
The first UPDATE statement in this example uses a derived column named x with
the collection-derived table s and gives all the elements in the collection the value
0. The second UPDATE statement stores the new collection in the database.
The manager table in Figure 11-53 on page 11-27 has a column named projects that
contains a LIST of ROW types with the definition that the following figure shows.
To access the ROW types in the LIST, declare a cursor and select the LIST into a
collection variable. After you retrieve each ROW type value in the projects column,
however, you cannot update the pro_name or pro_members fields individually.
Instead, for each ROW value that needs to be updated in the collection, you must
replace the entire ROW with values from a collection variable that include the new
field values, as the following figure shows.
END PROCEDURE;
Before you can use a row-type variable in an SPL program, you must initialize the
row variable with a LET statement or a SELECT INTO statement. The UPDATE
statement nested in the FOREACH loop of the previous figure sets the pro_name
field of the row type to the value supplied in the variable pro.
For example, suppose that the manager table has an additional column, scores,
which contains a LIST whose element type is a MULTISET of integers, as the
following figure shows.
To update a value in the MULTISET, declare a cursor that moves through each
value in the LIST and a nested cursor that moves through each value in the
MULTISET, as the following figure shows.
END FUNCTION
WITH LISTING IN ’/tmp/nested.out’;
The SPL function selects each MULTISET in the scores column into l, and then
each value in the MULTISET into m. If a value in m is 0, the function deletes it
from the MULTISET. After the values of 0 are deleted, the function counts the
remaining elements in each MULTISET and returns an integer.
In the manager table, the direct_reports column contains collections of SET type,
and the projects column contains a LIST. To add a name to the SET in the
direct_reports column, use an INSERT statement with a collection-derived table, as
the following figure shows.
END PROCEDURE;
This SPL procedure takes an employee name and a manager name as arguments.
The procedure then selects the collection in the direct_reports column for the
manager the user has entered, adds the employee name the user has entered, and
updates the manager table with the new collection.
The INSERT statement in the previous figure inserts the new employee name that
the user supplies into the SET contained in the collection variable r. The UPDATE
statement then stores the new collection in the manager table.
Notice the syntax of the VALUES clause. The syntax rules for inserting literal data
and variables into collection variables are as follows:
v Use parentheses after the VALUES keyword to enclose the complete list of
values.
v If the collection is SET, MULTISET, or LIST, use the type constructor followed by
brackets to enclose the list of values to be inserted. In addition, the collection
value must be enclosed in quotes.
VALUES( "SET{ 1,4,8,9 }" )
v If the collection contains a row type, use ROW followed by parentheses to
enclose the list of values to be inserted:
VALUES( ROW( ’Waters’, ’voyager_project’ ) )
v If the collection is a nested collection, nest keywords, parentheses, and brackets
according to how the data type is defined:
For more information on inserting values into collections, see Chapter 6, “Modify
data,” on page 6-1.
With the twin_primes column, you might want to insert a SET into the LIST or an
element into the inner SET. The following sections describe each of these tasks.
Inserting a SET into the LIST is similar to inserting a single value into a simple
collection.
To insert a SET into the LIST, declare a collection variable to hold the LIST and
select the entire collection into it. When you use the collection variable as a
collection-derived table, each SET in the LIST becomes a row in the table. You can
then insert another SET at the end of the LIST or at a specified point.
For example, the twin_primes column of one row of numbers might contain the
following LIST, as the following figure shows.
If you think of the LIST as a collection-derived table, it might look similar to the
following.
{3,5}
{5,7}
{11,13}
You might want to insert the value "SET{17,19}" as a second item in the LIST. The
statements in the following figure show how to do this.
END PROCEDURE;
In the INSERT statement, the VALUES clause inserts the value SET {17,19} at the
second position of the LIST. Now the LIST looks like the following figure.
{3,5}
{17,19}
{5,7}
{11,13}
You can perform the same insert by passing a SET to an SPL routine as an
argument, as the following figure shows.
LET n = n + 1;
END PROCEDURE;
In add_set(), the user supplies a SET to add to the LIST and an INTEGER value
that is the id of the row in which the SET will be inserted.
As an example, you can use this process on the twin_primes column of numbers.
For example, suppose that twin_primes contains the values that the following
figure shows, and you want to insert the value 18 into the last SET in the LIST.
The following figure shows the beginning of a procedure that inserts the value.
So far, the attaint procedure has performed steps 1, 2, and 3. The first DEFINE
statement declares a collection variable that holds the entire collection stored in
one row of numbers.
The second DEFINE statement declares an element variable that holds an element
of the collection. In this case, the element variable is itself a collection variable
because it holds a SET. The SELECT statement selects the entire collection from one
row into the collection variable, list_var.
The following figure shows how to declare a cursor so that you can move through
the elements of the outer collection.
Figure 11-98. Declare a cursor to move through the elements of the outer collection.
Executing routines
You can execute an SPL routine or external routine in any of these ways:
v Using a stand-alone EXECUTE PROCEDURE or EXECUTE FUNCTION
statement that you execute from DB-Access
v Calling the routine explicitly from another SPL routine or an external routine
v Using the routine name with an expression in an SQL statement
An additional mechanism for executing routines supports only the sysdbopen and
sysdbclose procedures, which the DBA can define. If a sysdbopen procedure
whose owner matches the login identifier of a user exists in the database when the
user connects to the database by the CONNECT or DATABASE statement, that
routine is executed automatically. If no sysdbopen routine has an owner that
matches the login identifier of the user, but a PUBLIC.sysdbopen routine exists,
that routine is executed. This automatic invocation enables the DBA to customize
the session environment for users at connection time. The sysdbclose routine is
similarly invoked when the user disconnects from the database. (For more
information about these session configuration routines, see the IBM Informix Guide
to SQL: Syntax and the IBM Informix Administrator's Guide.)
If the routine expects arguments, you must enter the argument values within
parentheses, as the following figure shows.
If the database has more than one procedure or function of the same name, IBM
Informix locates the right function based on the data types of the arguments. For
example, the statement in the previous figure supplies INTEGER and REAL values
as arguments, so if your database contains multiple routines named
scale_rectangles(), the database server executes only the scale_rectangles() function
that accepts INTEGER and REAL data types.
The parameter list of an SPL routine always has parameter names as well as data
types. When you execute the routine, the parameter names are optional. However,
if you pass arguments by name (instead of just by value) to EXECUTE
PROCEDURE or EXECUTE FUNCTION, as in the following figure, Informix
resolves the routine-by-routine name and arguments only, a process known as
partial routine resolution.
You can also execute an SPL routine stored on another database server by adding a
qualified routine name to the statement; that is, a name in the form
database@dbserver:owner_name.routine_name, as in the following figure.
When you execute a routine remotely, the owner_name in the qualified routine name
is optional.
END FUNCTION;
The SPL function uses a CALL statement that executes the external function area().
The value area() returns is stored in a and returned to the calling routine by the
RETURN statement.
In this example, area() is an external function, but you can use CALL in the same
manner with an SPL function.
For example, you might execute a function by a LET statement that assigns the
return value to a variable. The statements in the following figure perform the same
task. They execute an external function within an SPL routine and assign the
return value to the variable a.
You can also execute an SPL routine from an SQL statement, as the following
figure shows. Suppose you write an SPL function, increase_by_pct, which
DEFINE p DECIMAL;
RETURN p;
END FUNCTION;
The example selects the price column of a specified row of inventory and uses the
value as an argument to the SPL function increase_by_pct. The function then
returns the new value of price, increased by 20 percent, in the variable p.
Figure 11-106. A RETURN statement to execute an external function from within an SPL
routine.
When you execute the spl_func() function, the c_func() function is invoked, and
the SPL function returns the value that the external function returns.
The behavior of a cursor function is the same whether the function is an SPL
function or an external function. However, an SPL cursor function can return more
than one value per iteration, whereas an external cursor function (iterator function)
can return only one value per iteration.
In the following figure, the SPL procedure company_proc updates a large company
sales table and then assigns an SPL variable named salesperson_proc to hold the
dynamically created name of an SPL procedure that updates another, smaller table
that contains the monthly sales of an individual salesperson.
In example, the procedure company_proc accepts five arguments and inserts them
into company_tbl. Then the LET statement uses various values and the
concatenation operator || to generate the name of another SPL procedure to
execute. In the LET statement:
sales_person
An argument passed to the company_proc procedure.
current_month
The current month in the system date.
current_year
The current year in the system date.
11-58 IBM Informix Guide to SQL: Tutorial
Therefore, if a salesperson named Bill makes a sale in July 1998, company_proc
inserts a record in company_tbl and executes the SPL procedure
bill.tbl07_1998_proc, which updates a smaller table that contains the monthly sales
of an individual salesperson.
The SPL routine that the dynamic routine-name specification identifies must exist
before it can be executed. If you assign the SPL variable the name of a valid SPL
routine, the EXECUTE PROCEDURE or EXECUTE FUNCTION statement executes
the routine whose name is contained in the variable, even if a built-in function of
the same name exists.
Figure 11-108. SPL variable that contains a fully qualified routine name.
Privileges on routines
Privileges differentiate users who can create a routine from users who can execute
a routine. Some privileges accrue as part of other privileges. For example, the DBA
privilege includes permissions to create routines, execute routines, and grant these
privileges to other users.
For external routines written in the C language or the Java language, if the
IFX_EXTEND_ROLE configuration parameter is enabled, only users to whom the
DBSA has granted EXTERNAL role has been granted can register, drop, or alter
external UDRs or DataBlade modules. This parameter is enabled by default. By
setting the IFX_EXTEND_ROLE configuration parameter to OFF or to 0, the DBSA
can disable the requirement of holding the EXTEND role for DDL operations on
DataBlade modules and external UDRs. This security feature has no effect,
however, on SPL routines.
A DBA or the routine owner must explicitly grant the Execute privilege to
non-DBA users for the following conditions:
v A routine in an ANSI-compliant database
v A database with the NODEFDAC environment variable set to yes
v A routine that was created with the DBA keyword
An owner can restrict the Execute privilege on a routine even though the database
server grants that privilege to public by default. To do this, issue the REVOKE
EXECUTE ON PUBLIC statement. The DBA and owner can still execute the
routine and can grant the Execute privilege to specific users, if applicable.
The following example demonstrates both limiting privileges for a function and its
negator to one group of users. Suppose you create the following pair of negator
functions:
CREATE FUNCTION greater(y PERCENT, z PERCENT)
RETURNS BOOLEAN
NEGATOR= less(y PERCENT, z PERCENT);
. . .
CREATE FUNCTION less(y PERCENT, z PERCENT)
RETURNS BOOLEAN
NEGATOR= greater(y PERCENT, z PERCENT);
By default, any user can execute both the function and negator. The following
statements allow only accounting to execute these functions:
REVOKE EXECUTE ON FUNCTION greater FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION less FROM PUBLIC;
GRANT accounting TO mary, jim, ted;
GRANT EXECUTE ON FUNCTION greater TO accounting;
GRANT EXECUTE ON FUNCTION less TO accounting;
A user might receive the Execute privilege accompanied by the WITH GRANT
OPTION authority to grant the Execute privilege to other users. If a user loses the
For more information, see the GRANT and REVOKE statement descriptions in the
IBM Informix Guide to SQL: Syntax.
When a routine is run, the effective privilege is defined as the union of:
v The privileges of the user running the routine,
v The privileges that the owner has with the GRANT option.
By default, the database administrator has all the privileges in a database with the
GRANT option. Therefore, users executing routines that are owned by database
administrators can select from all of the tables in a given database.
The owner of the routine, and not the user who runs the routine, owns the
unqualified objects created in the course of executing the routine. For example,
assume user howie registers an SPL routine that creates two tables, with the
following SPL routine:
CREATE PROCEDURE promo()
. . .
CREATE TABLE newcatalog
(
catlog_num INTEGER
cat_advert VARCHAR(255, 65)
cat_picture BLOB
) ;
CREATE TABLE dawn.mailers
(
cust_num INTEGER
interested_in SET(catlog_num INTEGER)
);
END PROCEDURE;
User julia runs the routine, which creates the table newcatalog. Because no owner
name qualifies table name newcatalog, the routine owner (howie) owns
newcatalog. By contrast, the qualified name dawn.maillist identifies dawn as the
owner of maillist.
When a user executes a routine that was registered with the DBA keyword, that
user assumes the privileges of a DBA for the duration of the routine. If a user who
does not have the DBA privilege runs a DBA routine, the database server implicitly
grants a temporary DBA privilege to the invoker. Before exiting a DBA routine, the
database server implicitly revokes the temporary DBA privilege.
Objects created in the course of running a DBA routine are owned by the user who
executes the routine, unless a statement in the routine explicitly names someone
else as the owner. For example, suppose that tony registers the promo() routine
with the DBA keyword, as follows:
CREATE DBA PROCEDURE promo()
. . .
CREATE TABLE catalog
. . .
CREATE TABLE libby.mailers
. . .
END PROCEDURE;
Although tony owns the routine, if marty runs it, then marty owns the catalog
table, but user libby owns libby.mailers because her name qualifies the table
name, making her the table owner.
A called routine does not inherit the DBA privilege. If a DBA routine executes a
routine that was created without the DBA keyword, the DBA privileges do not
affect the called routine.
If a routine that is registered without the DBA keyword calls a DBA routine, the
caller must have Execute privileges on the called DBA routine. Statements within
the DBA routine execute as they would within any DBA routine.
The following example demonstrates what occurs when a DBA and non-DBA
routine interact. Suppose procedure dbspc_cleanup() executes another procedure
clust_catalog(). Suppose also that the procedure clust_catalog() creates an index
and that the SPL source code for clust_catalog() includes the following statements:
CREATE CLUSTER INDEX c_clust_ix ON catalog (catalog_num);
The DBA procedure dbspc_cleanup() invokes the other routine with the following
statement:
EXECUTE PROCEDURE clust_catalog(catalog);
If you are creating the routine in DB-Access, when you choose the Modify option
from the menu, the cursor moves to the line that contains the syntax error. You can
select Run and Modify again to check subsequent lines.
Compile-time warnings
If the database server detects a potential problem, but the syntax of the SPL routine
is correct, the database server generates a warning and places it in a listing file.
You can examine this file to check for potential problems before you execute the
routine.
The file name and path name of the listing file are specified in the WITH LISTING
IN clause of the CREATE PROCEDURE or CREATE FUNCTION statement. For
information about how to specify the path name of the listing file, see “Specify a
DOCUMENT clause” on page 11-9.
If you are working on a network, the listing file is created on the system where the
database resides. If you provide an absolute path name and file name for the file,
the file is created at the location you specify.
For UNIX, if you provide a relative path name for the listing file, the file is created
in your home directory on the computer where the database resides. (If you do not
have a home directory, the file is created in the root directory.)
For Windows, if you provide a relative path name for the listing file, the default
directory is your current working directory if the database is on the local
computer. Otherwise the default directory is %INFORMIXDIR%\bin.
After you create the routine, you can view the file that is specified in the WITH
LISTING IN clause to see the warnings that it contains.
To retrieve the text of the routine, select the data column from the sysprocbody
system catalog table. The datakey column for a text entry has the code T.
The SELECT statement in the following figure reads the text of the SPL routine
read_address.
Figure 11-109. SELECT statement to read the text of the SPL routine.
To generate a listing of traced values, first use the SQL statement SET DEBUG FILE
to name the file that is to contain the traced output. When you create the SPL
routine, include a TRACE statement.
The following figure demonstrates how you can use the TRACE statement to
monitor how an SPL function executes.
LET lcount = 1;
FOREACH
SELECT fname, lname, address1, city, state, zipcode
INTO p_fname, p_lname, p_add, p_city, p_state, p_zip
FROM customer
WHERE lname = lastname
RETURN p_fname, p_lname, p_add, p_city, p_state, p_zip
WITH RESUME;
LET lcount = lcount + 1; -- count of returned addresses
END FOREACH
END FUNCTION;
With the TRACE ON statement, each time you execute the traced routine, entries
are added to the file you specified in the SET DEBUG FILE statement. To see the
debug entries, view the output file with any text editor.
The following list contains some of the output that the function in previous
example generates. Next to each traced statement is an explanation of its contents.
Statement
Action
TRACE ON
Echoes TRACE ON statement.
TRACE Foreach starts
Traces expression, in this case, the literal string Foreach starts.
start select cursor
Provides notification that a cursor is opened to handle a FOREACH loop.
select cursor iteration
Provides notification of the start of each iteration of the select cursor.
expression: (+lcount, 1)
Evaluates the encountered expression, (lcount+1), to 2.
let lcount = 2
Echoes each LET statement with the value.
11-66 IBM Informix Guide to SQL: Tutorial
Exception handling
You can use the ON EXCEPTION statement to trap any exception (or error) that
the database server returns to your SPL routine or any exception that the routine
raises. The RAISE EXCEPTION statement lets you generate an exception within the
SPL routine.
In an SPL routine, you cannot use exception handling to handle the following
conditions:
v Success (row returned)
v Success (no rows returned)
BEGIN
DEFINE c INT;
ON EXCEPTION IN
(
-206, -- table does not exist
-217 -- column does not exist
) SET err_num
When an error occurs, the SPL interpreter searches for the innermost ON
EXCEPTION declaration that traps the error. The first action after trapping the
error is to reset the error. When execution of the error action code is complete, and
if the ON EXCEPTION declaration that was raised included the WITH RESUME
keywords, execution resumes automatically with the statement following the
statement that generated the error. If the ON EXCEPTION declaration did not
include the WITH RESUME keywords, execution exits the current block entirely.
For the exceptions specified in the IN clause (or for all exceptions, if no IN clause
is specified), the scope of the ON EXCEPTION statement includes all statements
that follow the ON EXCEPTION statement within the same statement block. If
other statement blocks are nested within that block, the scope also includes all
statements in the nested statement blocks that follow the ON EXCEPTION
statement, and any statements in statement blocks that are nested within those
nested blocks.
The following pseudocode shows where the exception is valid within the routine.
That is, if error 201 occurs in any of the indicated blocks, the action labeled a201
occurs.
User-generated exceptions
You can generate your own error using the RAISE EXCEPTION statement, as the
following figure shows.
In the example, the ON EXCEPTION statement uses two variables, esql and eisam,
to hold the error numbers that the database server returns. The IF clause executes
if an error occurs and if the SQL error number is -206. If any other SQL error is
caught, it is passed out of this BEGINEND block to the last BEGINEND block of
the previous example.
BEGIN
IF user = ’pault’ THEN
RAISE EXCEPTION -273; -- deny Paul update privilege
END IF
END
BEGIN
FOR i IN (1 TO 1000)
FOREACH select ..INTO aa FROM t
IF aa < 0 THEN
RAISE EXCEPTION 1; -- emergency exit
END IF
END FOREACH
END FOR
RETURN 1;
END
If the innermost condition is true (if aa is negative), then the exception is raised
and execution jumps to the code following the END of the block. In this case,
execution jumps to the TRACE statement.
Unless you set a trap for this error somewhere in the block, the error condition is
passed back to the block that contains the call and back to any blocks that contain
the block. If no ON EXCEPTION statement exists that is set to handle the error,
execution of the SPL routine stops, creating an error for the routine that is
executing the SPL routine.
The following figure shows an SPL function that uses the DBINFO function with
the 'sqlca.sqlerrd2' option to determine the number of rows that are deleted from a
table.
RETURN nrows;
END FUNCTION;
To ensure valid results, use this option after SELECT and EXECUTE PROCEDURE
or EXECUTE FUNCTION statements have completed executing. In addition, if you
use the 'sqlca.sqlerrd2' option within cursors, make sure that all rows are fetched
before the cursors are closed, to ensure valid results.
Summary
SPL routines provide many opportunities for streamlining your database process,
including enhanced database performance, simplified applications, and limited or
monitored access to data. You can also use SPL routines to handle extended data
types, such as collection types, row types, opaque types, and distinct types. For
syntax diagrams of SPL statements, see the IBM Informix Guide to SQL: Syntax.
You can use triggers to perform the following actions, as well as others that are not
found in this list:
v Create an audit trail of activity in the database. For example, you can track
updates to the orders table by updating corroborating information to an audit
table.
v Implement a business rule. For example, you can determine when an order
exceeds a customer's credit limit and display a message to that effect.
v Derive additional data that is not available within a table or within the database.
For example, when an update occurs to the quantity column of the items table,
you can calculate the corresponding adjustment to the total_price column.
v Enforce referential integrity. When you delete a customer, for example, you can
use a trigger to delete corresponding rows that have the same customer number
in the orders table.
UPDATE
trigger event
The CREATE TRIGGER statement consists of clauses that perform the following
actions:
v Declare a name for the trigger .
v Specify the DML operation on a specified table or view as the triggering event.
v Define the SQL operations that this event triggers.
To create a trigger, use DB-Access or one of the SQL APIs. This section describes
the CREATE TRIGGER statement as you enter it with the interactive
Query-language option in DB-Access. In an SQL API, you precede the statement
with the symbol or keywords that identify it as an embedded statement.
In the following excerpt from a CREATE TRIGGER statement, the trigger event is
defined as an update of the quantity column in the items table:
CREATE TRIGGER upqty
UPDATE OF quantity ON items -- an UPDATE trigger event
This portion of the statement identifies the table on which you define the trigger. If
the trigger event is an insert or delete operation, only the type of statement and
the table name are required, as the following example shows:
CREATE TRIGGER ins_qty
INSERT ON items -- an INSERT trigger event
A single trigger on a table can define actions for each of these times.
To define a triggered action, specify when it occurs and then provide the SQL
statement or statements to execute. You specify when the action is to occur with
the keywords BEFORE, AFTER, or FOR EACH ROW. The triggered actions follow,
enclosed in parentheses. The following triggered-action definition specifies that the
SPL routine upd_items_p1 is to be executed before the triggering statement:
BEFORE(EXECUTE PROCEDURE upd_items_p1) -- a BEFORE action
If a database object in the trigger definition, such as the SPL routine upd_items_p1
in this example, does not exist when the database server processes the CREATE
TRIGGER statement, it returns an error.
Among other uses, you can use BEFORE and AFTER triggered actions to
determine the effect of the triggering statement. For example, before you update
the quantity column in the items table, you could call the SPL routine
upd_items_p1 to calculate the total quantity on order for all items in the table, as
the following example shows. The procedure stores the total in a global variable
called old_qty.
CREATE PROCEDURE upd_items_p1()
DEFINE GLOBAL old_qty INT DEFAULT 0;
LET old_qty = (SELECT SUM(quantity) FROM items);
END PROCEDURE;
After the triggering update completes, you can calculate the total again to see how
much it has changed. The following SPL routine, upd_items_p2, calculates the total
of quantity again and stores the result in the local variable new_qty. Then it
compares new_qty to the global variable old_qty to see if the total quantity for all
orders has increased by more than 50 percent. If so, the procedure uses the RAISE
EXCEPTION statement to simulate an SQL error.
CREATE PROCEDURE upd_items_p2()
DEFINE GLOBAL old_qty INT DEFAULT 0;
DEFINE new_qty INT;
LET new_qty = (SELECT SUM(quantity) FROM items);
IF new_qty > old_qty * 1.50 THEN
RAISE EXCEPTION -746, 0, ’Not allowed - rule violation’;
END IF
END PROCEDURE;
If an update raises the total quantity on order for all items by more than 50
percent, the RAISE EXCEPTION statement in upd_items_p2 terminates the trigger
with an error. When a trigger fails in a database that has transaction logging, the
database server rolls back the changes that both the triggering statement and the
triggered actions make. For more information on what happens when a trigger
fails, see the CREATE TRIGGER statement in the IBM Informix Guide to SQL:
Syntax.
If the triggering event does not process any rows, a FOR EACH ROW triggered
action does not execute.
For a trigger on a table, if the triggering event is a SELECT statement, the trigger is
a called a Select trigger, and the triggered actions execute after all processing on
the retrieved row is complete. The triggered actions might not execute
immediately; however, because a FOR EACH ROW action executes for every
instance of a row that the query returns. For example, in a SELECT statement with
an ORDER BY clause, all rows must be qualified against the WHERE clause before
they are sorted and returned.
To supply values for the old_qty and new_qty columns in this table, you must be
able to refer to the old and new values of quantity in the items table; that is, the
values before and after the effect of the triggering statement. The REFERENCING
clause enables you to do this.
The REFERENCING clause lets you create two prefixes that you can combine with
a column name, one to reference the old value of the column, and one to reference
its new value. These prefixes are called correlation names. You can create one or
both correlation names, depending on your requirements. You indicate which one
you are creating with the keywords OLD and NEW. The following REFERENCING
clause creates the correlation names pre_upd and post_upd to refer to the old and
new values in a row:
REFERENCING OLD AS pre_upd NEW AS post_upd
In the following trigger example, the triggered action executes only if the condition
in the WHEN clause is true; that is, if the post-update unit price is greater than
two times the pre-update unit price:
CREATE TRIGGER up_price
UPDATE OF unit_price ON stock
REFERENCING OLD AS pre NEW AS post
FOR EACH ROW WHEN(post.unit_price > pre.unit_price * 2)
(INSERT INTO warn_tab
VALUES(pre.stock_num, pre.manu_code, pre.unit_price,
post.unit_price, CURRENT));
For more information on the WHEN condition, see the CREATE TRIGGER
statement in the IBM Informix Guide to SQL: Syntax.
Passing data to an SPL routine lets you use data values in the operations that the
routine performs.
Using SPL
The EXECUTE PROCEDURE statement in the preceding trigger calls the SPL
routine that the following example shows. The procedure uses SPL to calculate the
change that needs to be made to the total_price column when quantity is updated
in the items table. The procedure receives both the old and new values of quantity
and the old value of total_price. It divides the old total price by the old quantity to
derive the unit price. It then multiplies the unit price by the new quantity to obtain
the new total price.
CREATE PROCEDURE calc_totpr(old_qty SMALLINT, new_qty SMALLINT,
total MONEY(8)) RETURNING MONEY(8);
DEFINE u_price LIKE items.total_price;
DEFINE n_total LIKE items.total_price;
LET u_price = total / old_qty;
LET n_total = new_qty * u_price;
RETURN n_total;
END PROCEDURE;
In this example, SPL lets the trigger derive data that is not directly available from
the triggering table.
The value that is updated into total_price is returned by the RETURN statement at
the conclusion of the SPL procedure. The total_price column is updated for each
row that the triggering statement affects.
Trigger routines
You can define specialized SPL routines, called trigger routines, that can be invoked
only from the FOR EACH ROW section of the triggered action. Unlike ordinary
UDRs that EXECUTE FUNCTION or EXECUTE PROCEDURE routines can call
from the triggered action list, trigger routines include their own REFERENCING
clause that defines correlation names for the old and new column values in rows
that the triggered action modifies. These correlation names can be referenced in
SPL statements within the trigger routine, providing greater flexibility in how the
triggered action can modify data in the table or view.
Trigger routines can also use trigger-type Boolean operators, called DELETING,
INSERTING, SELECTING, and UPDATING, to identify what type of trigger has
called the trigger routine. Trigger routines can also invoke the mi_trigger* routines,
For information about syntax features that the CREATE FUNCTION, CREATE
PROCEDURE, EXECUTE FUNCTION, and EXECUTE PROCEDURE statements of
SQL support for defining and executing trigger routines, see your IBM Informix
Guide to SQL: Syntax. For more information about the mi_trigger* routines, see
your IBM Informix DataBlade API Programmer's Guide.
Select triggers
You can create a select trigger on a table or column(s) to perform certain types of
application-specific auditing, such as tracking the number of hits on a table. You
might create a select trigger to insert an audit record to an audit table each time a
user queries a certain table. For example, a DBA might create a select trigger to
provide a Web transaction history for Web DataBlade modules.
A Select trigger executes when the triggering column appears in the select list of a
stand-alone SELECT statement. The following statement executes a triggered action
on the hits_trig trigger for each instance of a row that the database server returns:
SELECT col_a FROM tab_a;
Suppose you create a routine new_proc that contains the statement SELECT col_a
FROM tab_a. Each of the following statements executes a triggered action on the
hits_trig trigger for each instance of a row that the embedded SELECT statement
returns:
SELECT new_proc() FROM tab_b;
EXECUTE PROCEDURE new_proc;
Views
Select triggers execute a triggered action for views whose base tables contain a
reference to a triggering column. You cannot, however, define a Select trigger on a
view.
The following statements execute a triggered action on the hits_trig trigger for
each instance of a row that the view returns:
SELECT * FROM view_tab;
For information about overriding and disabling inherited triggers, see “Triggers in
a table hierarchy” on page 12-8.
Re-entrant triggers
A re-entrant trigger refers to a case in which the triggered action can reference the
triggering table. In other words, both the triggering event and the triggered action
can operate on the same table. For example, suppose the following UPDATE
statement represents the triggering event:
UPDATE tab1 SET (col_a, col_b) = (col_a + 1, col_b + 1);
The following triggered action is legal because column col_c is not a column that
the triggering event has updated:
UPDATE tab1 SET (col_c) = (col_c + 3);
For a list of the rules that describe those situations in which a trigger can and
cannot be re-entrant, see the CREATE TRIGGER statement in the IBM Informix
Guide to SQL: Syntax.
For information on the CREATE VIEW statement and the INSTEAD OF trigger
syntax and rules, including an example of an INSTEAD OF trigger that will insert
rows on a view, see the IBM Informix Guide to SQL: Syntax.
After the tables, view, trigger, and SPL routine have been created, the database
server treats the following UPDATE statement as a triggering event:
UPDATE manager_info
SET empno = 3666, empname = “Steve”
WHERE deptno = 01;
This triggering UPDATE statement is not executed, but this event causes the
trigger action to be executed instead, invoking the updtab() SPL routine. The
UPDATE statements in the SPL routine update values into both the emp and dept
base tables of the manager_info view.
For more information about how to use the TRACE statement to diagnose logic
errors in SPL routines, see Chapter 11, “Create and use SPL routines,” on page 11-1.
When the triggered action is an SPL routine, you can generate error messages for
other error conditions with one of two reserved error numbers. The first one is
error number -745, which has a generalized and fixed error message. The second
one is error number -746, which allows you to supply the message text, up to a
maximum of 70 bytes.
You can apply this message with the RAISE EXCEPTION statement in SPL. The
following example generates error -745 if new_qty is greater than old_qty
multiplied by 1.50:
If you are using DB-Access, the text of the message for error -745 displays on the
bottom of the screen, as the following figure shows.
If your trigger calls a procedure that contains an error through an SQL statement
in your SQL API, the database server sets the SQL error status variable to -745 and
returns it to your program. To display the text of the message, follow the
procedure that your IBM Informix application development tool provides for
retrieving the text of an SQL error message.
If you invoke the trigger through an SQL statement in an SQL API, the database
server sets sqlcode to -746 and returns the message text in the sqlerrm field of the
SQL communications area (SQL;CA). For more information about how to use the
SQL;CA, see your SQL API publication.
Summary
To introduce triggers, this chapter discussed the following topics:
v The components of the CREATE TRIGGER statement
v Types of DML statements that can be triggering events
v Types of SQL statements that can be triggered actions
v How to create BEFORE and AFTER triggered actions and how to use them to
determine the impact of the triggering statement
v How to create a FOR EACH ROW triggered action and how to use the
REFERENCING clause to refer to the values of columns both before and after
the action of the triggering statement
v INSTEAD OF triggers on views, whose triggering event is ignored, but whose
triggered actions can modify the base tables of the view
v The advantages of using SPL routines as triggered actions
v Special features of calls to trigger routines as triggered actions
v How to trace triggered actions if they behave unexpectedly
v How to generate two types of error messages within a triggered action.
Accessibility features
The following list includes the major accessibility features in IBM Informix
products. These features support:
v Keyboard-only operation.
v Interfaces that are commonly used by screen readers.
v The attachment of alternative input and output devices.
Keyboard navigation
This product uses standard Microsoft Windows navigation keys.
In dotted decimal format, each syntax element is written on a separate line. If two
or more syntax elements are always present together (or always absent together),
the elements can appear on the same line, because they can be considered as a
single compound syntax element.
Each line starts with a dotted decimal number; for example, 3 or 3.1 or 3.1.1. To
hear these numbers correctly, make sure that your screen reader is set to read
punctuation. All syntax elements that have the same dotted decimal number (for
example, all syntax elements that have the number 3.1) are mutually exclusive
alternatives. If you hear the lines 3.1 USERID and 3.1 SYSTEMID, your syntax can
include either USERID or SYSTEMID, but not both.
The dotted decimal numbering level denotes the level of nesting. For example, if a
syntax element with dotted decimal number 3 is followed by a series of syntax
elements with dotted decimal number 3.1, all the syntax elements numbered 3.1
are subordinate to the syntax element numbered 3.
The following words and symbols are used next to the dotted decimal numbers:
? Specifies an optional syntax element. A dotted decimal number followed
by the ? symbol indicates that all the syntax elements with a
corresponding dotted decimal number, and any subordinate syntax
elements, are optional. If there is only one syntax element with a dotted
decimal number, the ? symbol is displayed on the same line as the syntax
element (for example, 5? NOTIFY). If there is more than one syntax element
with a dotted decimal number, the ? symbol is displayed on a line by
itself, followed by the syntax elements that are optional. For example, if
you hear the lines 5 ?, 5 NOTIFY, and 5 UPDATE, you know that syntax
elements NOTIFY and UPDATE are optional; that is, you can choose one or
none of them. The ? symbol is equivalent to a bypass line in a railroad
diagram.
! Specifies a default syntax element. A dotted decimal number followed by
the ! symbol and a syntax element indicates that the syntax element is the
default option for all syntax elements that share the same dotted decimal
number. Only one of the syntax elements that share the same dotted
decimal number can specify a ! symbol. For example, if you hear the lines
2? FILE, 2.1! (KEEP), and 2.1 (DELETE), you know that (KEEP) is the
default option for the FILE keyword. In this example, if you include the
FILE keyword but do not specify an option, default option KEEP is applied.
A default option also applies to the next higher dotted decimal number. In
this example, if the FILE keyword is omitted, default FILE(KEEP) is used.
However, if you hear the lines 2? FILE, 2.1, 2.1.1! (KEEP), and 2.1.1
(DELETE), the default option KEEP only applies to the next higher dotted
decimal number, 2.1 (which does not have an associated keyword), and
does not apply to 2? FILE. Nothing is used if the keyword FILE is omitted.
* Specifies a syntax element that can be repeated zero or more times. A
dotted decimal number followed by the * symbol indicates that this syntax
element can be used zero or more times; that is, it is optional and can be
Notes:
1. If a dotted decimal number has an asterisk (*) next to it and there is
only one item with that dotted decimal number, you can repeat that
same item more than once.
2. If a dotted decimal number has an asterisk next to it and several items
have that dotted decimal number, you can use more than one item
from the list, but you cannot use the items more than once each. In the
previous example, you can write HOST STATE, but you cannot write HOST
HOST.
3. The * symbol is equivalent to a loop-back line in a railroad syntax
diagram.
+ Specifies a syntax element that must be included one or more times. A
dotted decimal number followed by the + symbol indicates that this syntax
element must be included one or more times. For example, if you hear the
line 6.1+ data-area, you must include at least one data area. If you hear
the lines 2+, 2 HOST, and 2 STATE, you know that you must include HOST,
STATE, or both. As for the * symbol, you can repeat a particular item if it is
the only item with that dotted decimal number. The + symbol, like the *
symbol, is equivalent to a loop-back line in a railroad syntax diagram.
IBM may not offer the products, services, or features discussed in this document in
other countries. Consult your local IBM representative for information on the
products and services currently available in your area. Any reference to an IBM
product, program, or service is not intended to state or imply that only that IBM
product, program, or service may be used. Any functionally equivalent product,
program, or service that does not infringe any IBM intellectual property right may
be used instead. However, it is the user's responsibility to evaluate and verify the
operation of any non-IBM product, program, or service.
IBM may have patents or pending patent applications covering subject matter
described in this document. The furnishing of this document does not grant you
any license to these patents. You can send license inquiries, in writing, to:
For license inquiries regarding double-byte (DBCS) information, contact the IBM
Intellectual Property Department in your country or send inquiries, in writing, to:
The following paragraph does not apply to the United Kingdom or any other
country where such provisions are inconsistent with local law: INTERNATIONAL
BUSINESS MACHINES CORPORATION PROVIDES THIS PUBLICATION "AS IS"
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
PURPOSE. Some states do not allow disclaimer of express or implied warranties in
certain transactions, therefore, this statement may not apply to you.
Licensees of this program who wish to have information about it for the purpose
of enabling: (i) the exchange of information between independently created
programs and other programs (including this one) and (ii) the mutual use of the
information which has been exchanged, should contact:
IBM Corporation
J46A/G4
555 Bailey Avenue
San Jose, CA 95141-1003
U.S.A.
The licensed program described in this document and all licensed material
available for it are provided by IBM under terms of the IBM Customer Agreement,
IBM International Program License Agreement or any equivalent agreement
between us.
All statements regarding IBM's future direction or intent are subject to change or
withdrawal without notice, and represent goals and objectives only.
All IBM prices shown are IBM's suggested retail prices, are current and are subject
to change without notice. Dealer prices may vary.
This information is for planning purposes only. The information herein is subject to
change before the products described become available.
This information contains examples of data and reports used in daily business
operations. To illustrate them as completely as possible, the examples include the
names of individuals, companies, brands, and products. All of these names are
fictitious and any similarity to the names and addresses used by an actual business
enterprise is entirely coincidental.
COPYRIGHT LICENSE:
Each copy or any portion of these sample programs or any derivative work, must
include a copyright notice as follows:
© (your company name) (year). Portions of this code are derived from IBM Corp.
Sample Programs.
© Copyright IBM Corp. _enter the year or years_. All rights reserved.
If you are viewing this information softcopy, the photographs and color
illustrations may not appear.
Trademarks
IBM, the IBM logo, and ibm.com are trademarks or registered trademarks of
International Business Machines Corp., registered in many jurisdictions worldwide.
Other product and service names might be trademarks of IBM or other companies.
A current list of IBM trademarks is available on the web at "Copyright and
trademark information" at http://www.ibm.com/legal/copytrade.shtml.
Adobe, the Adobe logo, and PostScript are either registered trademarks or
trademarks of Adobe Systems Incorporated in the United States, and/or other
countries.
Java and all Java-based trademarks and logos are trademarks or registered
trademarks of Oracle and/or its affiliates.
UNIX is a registered trademark of The Open Group in the United States and other
countries.
Notices B-3
B-4 IBM Informix Guide to SQL: Tutorial
Index
Special characters Application (continued)
isolation level 10-9
!=, not equal, relational operator 2-18 update cursor 10-13
?, question mark Archiving
as placeholder in PREPARE 8-19 database server methods 6-35
>=, greater than or equal to, relational operator 2-19 description of 6-35
=, equals, relational operator 2-18, 2-42 transaction log 6-35
Arithmetic expressions 2-33
Arithmetic operators, in expression 2-33
A Ascending order in SELECT 2-8
Access modes, description of 10-15 Asterisk notation, in a SELECT statement 3-5
Access privilege on UDRs 11-59 Asterisk, wildcard character in SELECT 2-6
Accessibility A-1 Authorization identifier 6-6
dotted decimal format of syntax diagrams A-1 AVG function, as aggregate function 4-2
keyboard A-1
shortcut keys A-1
syntax diagrams, reading in a screen reader A-1 B
Active set BEGIN WORK statement 6-35
definition of 2-16, 8-8 BETWEEN keyword
of a cursor 8-14 using in WHERE clause 2-17
Aggregate functions using to specify a range of rows 2-19
and GROUP BY clause 5-2 BIGSERIAL data type
AVG 4-2 last BIGSERIAL value inserted 4-23
COUNT 4-2 Boolean expression 2-23
description of 4-1, 4-10 Braces ( { } ) comment delimiters 11-10
finding NULL values 8-11 Built-in data type, declaring variables 11-14
in ESQL 8-8 BYTE data type
in expressions 4-1 restrictions with GROUP BY 5-2
in SPL routine 11-23 using LENGTH function on 4-20
in subquery 5-22 with relational expression 2-17
MAX 4-3
MIN 4-3
null value signalled 8-6
RANGE 4-3 C
standard deviation 4-4 CALL statement, in SPL function 11-55
STDEV 4-4 Cardinality function
SUM 4-4 description of 4-13
VARIANCE 4-5 CARDINALITY function 4-13
Alias Cartesian product
for table name 2-47 basis of joins 2-41
to assign column names in temporary table 5-7 description of 2-40
using Cascading deletes
as a query shortcut 2-47 child tables 6-25
with a supertable 3-11 definition of 6-25
with self-join 5-7 locking associated with 6-25
ALL keyword logging 6-25, 6-35
beginning a subquery 5-21 referential integrity 6-25
in subquery 5-21 restriction 6-26
ALTER INDEX statement, locking table 10-5 Case conversion
AND logical operator 2-23 with INITCAP function 4-16
ANSI with LOWER function 4-15
isolation levels 10-12 with UPPER function 4-15
SQL version 1-11 CASE expression
ANSI standard description of 2-36
as extension to Informix syntax 1-11 in UPDATE statement 6-19
ANSI-compliant database using 2-36
FOR UPDATE not required in 9-10 CHAR data type
signalled in SQLWARN 8-6 converting to a DATE value 4-10
ANY keyword, in SELECT statement 5-22 converting to a DATETIME value 4-12
Application in relational expressions 2-17
handling errors 8-10 substrings of 2-14
Index X-3
double hyphen (--) comment indicator 11-10 EXECUTE FUNCTION statement
DROP INDEX statement, locking table 10-5 with SPL 11-54
Duplicate values, finding 2-39 EXECUTE IMMEDIATE statement, description of 8-21
Dynamic routine-name specification Execute privilege
for SPL function 11-58 DBA keyword, effect of 11-62
for SPL routine 11-58 objects referenced by a routine 11-62
rules for 11-59 EXECUTE PROCEDURE statement
Dynamic SQL with SPL 11-54
description of 8-2, 8-18 EXISTS keyword 5-38
freeing prepared statements 8-20 in a WHERE clause 5-21
in SELECT statement 5-24
Expression
E CASE 2-36
date-oriented 4-6
Embedded SQL
description of 2-33
definition of 8-1
display label for 2-35
languages available 8-1
in SPL routine 11-23
ENCRYPT_AES function 4-28
EXTEND function
ENCRYPT_TDES function 4-28
using in expression 4-9
End of data
with DATE and DATETIME values 4-6
signal in SQLCODE 8-5, 8-10
EXTEND role 11-59
signal only for SELECT 9-8
Extensibility, description of 1-10
SQLCODE 8-13
External database 7-1
when opening cursor 8-12
External routines 11-59
Entity integrity 6-23
Equals (=) relational operator 2-18, 2-42
Equi-join 2-42
Error checking F
simulating errors 11-69 FETCH statement 8-13
SPL routines 11-67, 11-69 ABSOLUTE keyword 8-14
Error message files 8-7 description of 8-13
Error messages sequential 8-14
applying fixed 12-12 with sequential cursor 8-15
for trigger failure 12-12 Field projection 3-4
generating a variable 12-13 Field, definition of 3-3
generating in a trigger 12-12 Files, compared to database 1-1
retrieving trigger text in a program 12-12, 12-13 Filtering mode 6-27
Errors FIRST clause
after DELETE 9-1 description of 2-30
codes for 8-5 using 2-31
dealing with 8-10 with ORDER BY clause 2-32
detected on opening cursor 8-12 FLUSH statement
during updates 6-33 count of rows inserted 9-6
inserting with a cursor 9-6 rollback 9-6
ISAM error code 8-5 writing rows to buffer 9-5
ESCAPE keyword, using in WHERE clause 2-28 FOR UPDATE keywords
ESQL/C conflicts with ORDER BY 9-3
cursor use 8-11, 8-16 not needed in ANSI-compliant database 9-10
DELETE statement in 9-1 specific columns 9-9
delimiting host variables 8-3 FOREACH statement 11-24
dynamic embedding 8-2, 8-18 Foreign key 6-24
error handling 8-10 FREE statement, freeing prepared statements 8-20
fetching rows from cursor 8-13 FROM clause
host variable 8-3, 8-4 subqueries in 5-20
indicator variable 8-9 FROM keyword, alias names 2-47
INSERT in 9-4 Functions
overview 8-1, 8-23, 9-1 aggregate 4-1
preprocessor 8-1 applying to expressions 4-5
scroll cursor 8-14 conversion 4-10
selecting single rows 8-8 DATE 4-10
SQL Communications Area 8-4 date-oriented 4-6
SQLCODE 8-4 DBINFO 4-23
SQLERRD fields 8-5 DECODE 4-24
static embedding 8-2 in SELECT statements 4-1
UPDATE in 9-9 INITCAP 4-16
EXCLUSIVE keyword LOWER 4-15
in DATABASE statement 10-4 LPAD 4-19
Exclusive lock 10-3 name confusion in SPL routine 11-19
Index X-5
J Locking (continued)
types of locks (continued)
Join row and key locks 10-5
ANSI outer-join syntax 5-11 shared 10-3
associative 2-44 smart-large-object locks 10-8
composite 2-40 table lock 10-4
condition 2-40 update cursor 10-8
creating 2-41 update lock 10-14
cross 2-42 WAIT keyword 10-16
definition of 2-5, 2-40 with DELETE 9-1
equi-join 2-42 Logical log
in MERGE statement 6-5, 6-6, 6-14 and backups 6-35
Informix outer join syntax 5-11 description of 6-34
left outer 5-11 Logical operator
multiple-table join 2-46 = (equals) 2-23
natural 2-44 AND 2-23
nested simple 5-14 NOT 2-23
on derived tables 5-11 OR 2-23
outer 5-10 Loop, exiting with RAISE exception 11-69
right outer 5-13 LOWER function, as string manipulation function 4-15
self-join 5-7 LPAD function, as string manipulation function 4-19
simple 2-40
K M
MATCHES keyword
Keywords using GLS 2-27
in a subquery 5-21 using in WHERE clause 2-17
in a WHERE clause 2-17 MATCHES relational operator
how locale affects 2-27
in WHERE clause 2-24
L MAX function, as aggregate function 4-3
Label 2-35, 5-35 MERGE statement
Left outer join 5-11 using Insert join 6-6
LENGTH function using Update join 6-14
on TEXT or BYTE strings 4-20 MERGE statements
on VARCHAR 4-20 using Delete join 6-5
use in expression 4-20 MIN function, as aggregate function 4-3
Less than or equal to (>=) relational operator 2-19 MODE ANSI keywords, specifying transactions 6-35
LET statement 11-21 MONEY data type
LIKE clause in INSERT statement 6-7
in SPL function 11-17 MONTH function
LIKE keyword using, TIME function
description of 2-24 MONTH 4-7
using in WHERE clause 2-17 MONTH function, as time function 4-6
Local variable, description of 11-13 Multiple-table join 2-46
LOCK TABLE statement, locking a table explicitly 10-4 Multiple-Table SELECTs 2-40
Locking MULTISET keyword
and concurrency 6-36 collection subquery 5-28
behavior of different lock types 10-14 Multithreaded application, definition of 8-1
deadlock 10-17
description of 10-3
end of transaction 10-18 N
integrity 10-1 Named row type, in VALUES clause 6-9
intent locks 10-14 Natural join 2-44
lock duration 10-8 NCHAR data type, querying on 2-2
number of rows to lock 10-11 Nested ordering, in SELECT 2-9
row and key locks 10-5 NODEFDAC environment variable, effect on privileges of
scope of lock 10-3 public 11-61
setting lock mode 10-16 NOT BETWEEN keywords in WHERE clause 2-20
time limit 10-17 Not equal (!=) relational operator 2-18
types of locks 10-3 NOT EXISTS keywords 5-39
coarse index lock 10-7 NOT IN keywords 5-39
database lock 10-4 NOT logical operator 2-23
exclusive 10-3 Null values
page lock 10-5, 10-6 detecting in ESQL 8-9
promotable 10-3 testing for 2-22
promotable lock 10-8
Index X-7
ROW data types (continued) SELECT statements (continued)
field projections in SELECT 3-5 EXISTS keyword 5-24
field, definition of 3-3 FIRST clause 2-30
in DELETE statement 6-4 for joined tables 2-49
selecting columns from 3-3 for single tables 2-6, 4-23
selecting data from 3-1 forms of 2-2
updating 6-17 functions 4-1, 4-23
using asterisk notation with SELECT 3-5 GROUP BY clause 5-2
Row type columns HAVING clause 5-5
definition of 3-3 in UPDATE statement 6-15
Null values 6-18 INTO clause with ESQL 8-8
Row-type data, selecting columns of 3-3 INTO TEMP clause 2-49
Row-type variables, delcaring 11-16 isolation level 10-9
ROWID, using to locate internal row numbers 2-39 join 2-41
Rows multiple-table 2-40
checking rows processed in SPL routines 11-70 natural join 2-44
definition of 1-9, 2-2 ORDER BY clause 2-7
finding number of rows processed 4-23 outer join 5-10
in relational model 1-9 select list 2-3
inserting 6-6 selecting a row type 3-1
locking 10-5 selecting a substring 2-14
number of rows returned 2-30 selecting expressions 2-33
removing 6-1 selection list 2-6
updating 6-14 self-join 5-7
RPAD function, as string manipulation function 4-20 set operations 5-32
simple 2-1
single-table 2-6
S singleton 2-16, 8-8
smart-large-object functions in 4-13
Screen reader
stand-alone 12-8
reading syntax diagrams A-1
subquery 5-17
Screens, example 12-12
UNION operator 5-32
Scroll cursors
using
active set 8-15
for join 2-5
definition of 8-14
for projection 2-4
SCROLL keyword, using in DECLARE 8-14
for selection 2-3
Select cursor
using functions 4-1
opening 8-12
SELECT triggers, description of 12-8
using 8-12
Select, description of 1-9
Select list
Selection, description of 2-3
display label 2-35
Self-join 5-7
expressions in 2-33
assigning column names with INTO TEMP 5-7
functions in 4-1, 4-23
description of 5-7
labels in 5-35
Self-referencing query 5-7, 6-26
selecting all columns 2-6
Semantic integrity 6-24
selecting specific columns 2-10
Sequence
specifying a substring in 2-14
definition of 1-9
SELECT statements
Sequential cursor, definition of 8-14
accessing collections 3-1, 3-6
SERIAL data type
active set 2-16, 8-8
generated number in SQLERRD 8-5
advanced 5-1
inserting a starting value 6-7
aggregate functions in 4-1, 4-10
last SERIAL value inserted 4-23
alias names 2-47
SERIAL8 data type
ALL keyword 5-21
last SERIAL8 value inserted 4-23
and end-of-data return code 9-8
Session ID, returned by DBINFO function 4-23
ANY keyword 5-22
SET clause, in UPDATE statement 6-16
basic concepts 2-2
SET COLLATION 2-27
collection expressions 5-27
Set intersection 5-38
collection subquery 5-28
SET ISOLATION statement
collection-derived table 5-30
and SET TRANSACTION 10-9
compound query 5-32
use of 10-9
cursor for 8-11, 8-12
SET keyword, in MERGE statement 6-14
date-oriented functions in 4-6
SET keyword, in UPDATE statement 6-15
description of 2-1
SET LOCK MODE statement, description of 10-16
display label 2-35
Set operation
DISTINCT keyword 2-10
difference 5-39
embedded 8-8, 8-9
intersection 5-36
executing triggered actions 12-8
Index X-9
SQLSTATE, problem values 8-10 Temporary tables (continued)
SQLWARN array example 6-13
description of 8-6 TEXT data type
syntax of naming 8-4 restrictions
with PREPARE 8-19 with GROUP BY 5-2
Standard deviation, aggregate function 4-4 using LENGTH function on 4-20
standards xi with relational expressions 2-17
START VIOLATIONS TABLE 6-30 Time function
Statement block 11-23 description of 4-6
Statement cache, SQL 10-19 use in SELECT 4-1
Static SQL 8-2 TIME function
STDEV function, as aggregate function 4-4 DAY and CURRENT 4-6
Stored routine, general programming 1-12 WEEKDAY 4-8
Stream pipe connection 7-3 YEAR 4-9
Subquery TO_CHAR function, as conversion function 4-11
ALL keyword 5-21 TO_DATE function, as conversion function 4-12
ANY keyword 5-22 TODAY function, in constant expression 4-22, 6-7
correlated 5-17, 5-23, 6-26 TRACE statement
in DELETE statement 6-4 debugging an SPL routine 11-65
in FROM clause 5-20 output 12-12
in select list 5-19 Transaction logging
in SELECT statement 5-17 contents of log 6-35
in UPDATE statement 6-15 description of 6-34
with SET clause 6-15 Transactions
in WHERE clause 5-21 description of 6-34
single-valued 5-22 end of 10-18
Subscripting example with DELETE 9-2
in a WHERE clause 2-29 locks held to end of 10-8
SPL variables 11-18 locks released at end of 10-8
SUBSTR function, as string manipulation function 4-18 logging 6-34
Substring 2-14, 11-18 use signalled in SQLWARN 8-6
SUBSTRING function, as string manipulation function 4-17 Trigger action
SUM function, as aggregate function 4-4 definition of 12-3
Supertable 3-11 REFERENCING clause 12-5
in a table hierarchy 3-9 Trigger event
inserting into 6-10 definition of 12-2
selecting from 3-10 example of 12-2
using an alias 3-11 Trigger routines 12-7
Syntax diagrams Triggered action
reading in a screen reader A-1 BEFORE and AFTER 12-4
SYSDATE function, as time function 4-6, 6-6 FOR EACH ROW 12-5
sysprocbody, system catalog table 11-64 generating an error message 12-12
System catalogs in relation to triggering statement 12-3
privileges in 6-22 SELECT statements 12-8
querying 6-22 statements 12-1
sysprocbody 11-64 tracing 12-11
systabauth 6-22 using 12-4
System descriptor area 8-20 using SPL routines 12-6
WHEN condition 12-6
Triggers
T creating 12-1
declaring the name 12-2
Table
definition of 12-1
description of 1-8
in a table hierarchy 12-8
hierarchy 3-9
INSTEAD OF 12-10
in relational model 1-8
re-entrant 12-10
loading data
select
with onload utility 6-35
defining on a table hierarchy 12-10
lock 10-4
description of 12-8
not in the current database 2-23
restrictions on execution 12-9
operations on a 1-9
when to use 12-1
Table hierarchy
TRUNCATE statement 6-2
triggers in 12-8
Truncation, signalled in SQLWARN 8-6
UPDATE statements 6-19
Typed table
TCP/IP connection 7-3
definition of 3-1
Temporary tables
inserting rows 6-8
and active set of cursor 8-15
selecting from 3-2
assigning column names 5-7
Index X-11
X-12 IBM Informix Guide to SQL: Tutorial
Printed in USA
SC27-3522-01
Spine information:
Informix Product Family Informix Version 11.70 IBM Informix Guide to SQL: Tutorial