SQL Language

Introduction

This chapter describes the CQL++ implementation of the SQL language. The CQL++ implementation includes the ANSI 1989 syntax, ODBC extensions, and CQL++ extensions.

The following conventions are used to distinguish between different types of symbols:

UPPER CASE
Keywords.
emphasized type
Non-terminal objects.
normal lower case type
Terminal objects.

The usual BNF notation is used. Optional arguments are enclosed in square brace ([]). If an object is followed by elipses (...), it is repeated 0 or more times. The vertical bar (|) is used to indicate a choice of objects among a group. Curly braces ({}) are used to group a set of choices.

Common Elements

This section describes the elements of the SQL language which are used in more than one SQL statement.

UserDefinedName ::=
	letter [ digit | letter | _ ]...

DataType ::=
	CharacterStringType
	| ExactNumericType
	| ApproximateNumericType
	| BinaryType
	| DateType
	| TimeType
	| TimestampType

CharacterStringType ::=
	CHARACTER( length )
	| CHAR( length )
	| CHARACTER VARYING( length  )
	| VARCHAR( length )
	| LONG VARCHAR

ExactNumericType ::=
	DECIMAL( precision, scale )
	| NUMERIC( precision, scale )
	| BIT
	| TINYINT
	| SMALLINT
	| INTEGER
	| BIGINT

ApproximateNumericType ::=
	FLOAT
	| DOUBLE PRECISION
	| REAL

BinaryType ::=
	BINARY( length )
	| VARBINARY( length )
	| LONG VARBINARY

DateType ::=
	DATE

TimeType ::=
	TIME

TimestampType ::=
	TIMESTAMP

Data Definition Language

The SQL term DDL (data definition language) refers to statements which create, change, or drop tables, views, and indices.

ALTER TABLE Statement

Type
ODBC Extension
Summary
ALTER TABLE is used to add columns to an existing base table.
Syntax
ALTER TABLE TableName
{
	ADD ColumnIdentifier DataType
	| ADD ( UserDefinedName DataType [ , UserDefinedName DataType ]... )
}
Description
ALTER TABLE is used to add one or more columns to a base table. The only column attributes available to ALTER TABLE are the column name and column type.

CREATE INDEX Statement

Type
ODBC Extension
Summary
CREATE INDEX is used to add indices to a base table.
Syntax
CREATE [ UNIQUE ] INDEX IndexName
ON BaseTableName
( ColumnIdentifier [ ASC | DSC ]  [ , ColumnIdentifier  [ ASC | DSC ] ]... )
Description
CREATE INDEX is used to create an index on a base table. The UNIQUE keyword specifies that the index does not allow duplicates. Modifiers ASC and DSC are for ascending and decending indices, respectively. If neither ASC nor DSC is specified, an ascending index is created.

CREATE TABLE Statement

Type
ANSI
Summary
CREATE TABLE Creates a base table.
Syntax
CREATE TABLE BaseTableName
( ColumnElement [ , ColumnElement ]... )

ColumnElement ::=
	ColumnDefinition | TableConstraintDefinition

ColumnDefinition ::=
	ColumnIdentifier DataType
	[ DEFAULT DefaultValue ]
	[ ColumnConstraintDefinition [ , ColumnConstraintDefinition ]... ]

ColumnConstraintDefinition ::=
	NOT NULL
	| UNIQUE
	| INDEX
	| PRIMARY KEY
	| REFERENCES BaseTableName ReferencedColumns
	| CHECK SearchCondition

DefaultValue ::=
	Literal | NULL | USER

TableConstraintDefinition ::=
	UNIQUE ( ColumnIdentifier [ , ColumnIdentifier ]... )
	| INDEX ( ColumnIdentifier [ , ColumnIdentifier ]... )
	| PRIMARY KEY ( ColumnIdentifier [ , ColumnIdentifier ]... )
	| CHECK ( SearchCondition )

	| FOREIGN KEY
	ReferencingColumns
	REFERENCES
	BaseTableName
	ReferencedColumns

ReferencingColumns ::=
	( ColumnIdentifier [ , ColumnIdentifier ]... )

ReferencedColumns ::=
	( ColumnIdentifier [ , ColumnIdentifier ]... )
Description
CREATE TABLE is used to create a base table. CREATE TABLE includes the table name, columns, and table constraint definitions. Information about each column includes the column name, type, default value, and column constraint definitions.

The column constraint options are:

NOT NULL
Null values are not allowed.
UNIQUE
No value for this column is allowed to be a duplicate of any other value of this column in the table. CQL++ implements this by creating a UNIQUE index.
INDEX
Creates an index on the column which allows duplicates. This is an extension to both ANSI and ODBC SQL. To remain compatible with the ODBC specification, use the CREATE INDEX statement instead.
PRIMARY KEY
Creates an index on the column which does not allow duplicates, and marks that index as the primary key. A table can have only one primary key.
REFERENCES
Specifies a referential integrity constraint. During INSERT, UPDATE, or DELETE, The column (or columns) in the list of ReferencedColumns must contain the value specified for the column. That is, the constraint says "don't allow a value in this table unless the value is also in table X column Y." A typical example of this is a detail table which contains information for a value, say purchases for a customer. The customer might have a customer number in a customer table. The constraint is that when a row is inserted into the purchases table, the value of the customer number column must refer to a customer that is in the customer table. The create statements for these tables are:
CREATE TABLE CUSTOMER (
NAME CHAR(100),
CUSTOMER_NUMBER INTEGER PRIMARY KEY
)
CREATE TABLE PURCHASES (
PURCHASE_DATE DATE,
CUSTOMER_NUMBER INTEGER
REFERENCES CUSTOMER CUSTOMER_NUMBER
ITEMNUMBER INTEGER,
...
)

The table constraint definition options are:

UNIQUE...
A list of columns which, as a group, must be unique. CQL++ creates an index on the columns which does not allow duplicates.
PRIMARY KEY...
A list of columns which are the table's primary key. CQL++ creates an index on the columns which does not allow duplicates.
INDEX...
A list of columns for which CQL++ creates a unique index. This is an extension to both ANSI and ODBC; to maintain ODBC compatibility, use CREATE INDEX instead.
CHECK...
The check condition is a conditional expression which rows must satisfy to be inserted into the table. The syntax of the check condition is the same as the syntax of a WHERE clause. The check condition may refer only to columns in the table and to literals.
FOREIGN KEY...
The foreign key clause relates a group of columns in the table being created to a group of columns which are defined as a UNIQUE index on another base table. Some databases restrict the allowable foreign keys to primary keys. ODBC and CQL++ do not have this restriction, allowing a FOREIGN KEY to be defined on any UNIQUE index in a foreign table. To maintain maximum interoperability among ODBC databases, the programmer may wish to limit the target keys of the FOREIGN KEY constaint to primary keys.

CREATE VIEW

Type
ANSI
Summary
CREATE VIEW defines a view.
Syntax
CREATE VIEW ViewedTableName
[ ( ColumnIdentifier [ , ColumnIdentifier ]... ) ]
AS QuerySpecification
[ WITH CHECK OPTION ]
Description
CREATE VIEW creates a view. A view is a virtual table which references one or more base tables. A view can also reference another view. The view column name list is optional; if the view column name list is omitted, the column names from the referenced tables are used. The CREATE VIEW statement includes a complete SQL SELECT statement, which defines the rows which are part of the virtual table. Views are completely dynamic, and always reflect the current data in the underlying base tables.

Depending on the SELECT statement, a view may or may not be updatable (that is, usable in an INSERT, UPDATE, or DELETE statement). A view is updatable if the following conditions are met:

If View insertions and updates are subject to the table and column constraints of the underlying base table(s). An insert/update on a view may fail because the value of a UNIQUE field is duplicated, even though this value is not displayed when doing SELECTs on the view (due to the WHERE clause of the CREATE VIEW statement). If the view definition does not include a base table column which has the NOT NULL attribute, then all insertions fail for that view.

DROP INDEX

Type
ODBC
Summary
Deletes an index on a base table.
Syntax
DROP INDEX IndexName
Description
Drop index drops an index from a base table. Only the owner of the table can drop the index. After dropping the index, CQL++ does not rebuild the index file. To reclaim the space occupied by the index, an off line index rebuild is required.

DROP TABLE

Type
ODBC
Summary
Drops a table.
Syntax
DROP TABLE BaseTableName
[ CASCADE | RESTRICT ]
Description
DROP TABLE is used to delete a table. If the RESTRICT modifier is used, then DROP TABLE fails if the table is referenced by any view or if any integrity constraints exist which reference the table. If the CASCADE modifier is used, then any tables which have constraints which become invalid are also dropped. CASCADE should be used with care. If neither CASCADE nor RESTRICT is used, the table is dropped unconditionally and no other tables are affected, which may result in INSERT or UPDATE failures (if any invalid references exist).

DROP VIEW

Type
ODBC
Summary
Drops a view.
Syntax
DROP VIEW BaseTableName
[ CASCADE | RESTRICT ]
Description
DROP VIEW is used to delete a view. If the RESTRICT modifier is used, then DROP VIEW fails if the view is referenced by any other view or is used in an integrity constraint for any other view or table. If the CASCADE modifier is used, then any views and tables which have constraints which become invalid are also dropped. CASCADE should be used with care. If neither CASCADE nor RESTRICT is used, the view is dropped unconditionally and no other tables or views are affected, which may result in INSERT or UPDATE failures (if any invalid references exist).

GRANT

Type
ANSI
Summary
Grants privileges to other users.
Syntax
GRANT { ALL | GrantPrivilege [ , GrantPrivilege ]... }
ON TableName
TO { PUBLIC | UserName [ , UserName ]... }

GrantPrivilege ::=
	DELETE
	| INSERT
	| SELECT
	| UPDATE [ ( ColumnIdentifier [ , ColumnIdentifier ]... ) ]
	| REFERENCES [ ( ColumnIdentifier [ , ColumnIdentifier ]... ) ]
	[ WITH GRANT OPTION ]
Description
GRANT is used to allow other users access to a table.

If the table owner grants ALL privileges to a user, the user can do everything the table owner can do, except DROP TABLE, DROP INDEX, CREATE INDEX, and ALTER TABLE. However, is the user granting the privileges is not the table owner, that user can only grant privileges which the user has and for which the user has authorization to grant. That is, if user X creates a table A, and user X grants to user Y SELECT privileges on A, then is user Y grants ALL privileges on A to user Z, user Z has only SELECT privileges.

If the user has INSERT privilege, he can insert rows into the table. If the user has SELECT privileges, he can use the table in SELECT statements. If the user has UPDATE privileges, he can update rows in the table. If UPDATE is followed by the option list of column names, then he can only update those columns; if the column name list is omitted, he may update all columns.

The WITH GRANT OPTION clause specifies whether or not the user may grant privileges to other users. Note that WITH GRANT OPTION is part of ANSI SQL but is not part of ODBC SQL.

For example, user X owns table A and grants ALL privileges to user Y WITH GRANT OPTION. User Y can then grant all privileges to other users. On the other hand, suppose that X grants SELECT privilege to Y WITH GRANT OPTION, and grants INSERT privilege to Y (without the grant option). User Y can then grant SELECT to user Z, but user Y cannot grant INSERT to user Z.

There is a special user name, PUBLIC, which is used to grant privileges to every user. For example, user X owns table A and grants SELECT privilege to PUBLIC. Any user can then use table A in a SELECT statement. Note, however, that any user other than X must use the syntax X.A for the table. Granting privileges to PUBLIC allows every user to access the table, but does not make every user an owner of the table.

Note that one GRANT statement can be used to grant privileges to more than one user. GRANT does not allow granting of privileges to PUBLIC and specific users in the same statement.

If user X has REFERENCES privilege on table A, user X can create a table Y with references to table A. REFERENCES is followed by an optional column list. If the column list is omitted, X can reference any keys in A. If the column list is present, X can only reference the named columns in A.

REVOKE

Type
ODBC
Summary
REVOKE reverses one or more privileges created by GRANT.
Syntax
REVOKE { ALL | RevokePrivilege [ , RevokePrivilege ]... }
ON TableName
FROM { PUBLIC | UserName [ , UserName ]... }
[ CASCADE | RESTRICT]

RevokePrivilege ::=
	DELETE
	| INSERT
	| SELECT
	| UPDATE
	| REFERENCES
REVOKE reverses the effects of GRANT. It removes privileges from one or more users (or PUBLIC). If RESTRICT is used, the REVOKE fails if it creates invalid references. If CASCADE is used, any tables whose references become invalid are dropped. If neither CASCADE no RESTRICT is used, the privileges are revoked and any "illegal" references continue to exist.

Data Manipulation Language

The SQL term DML (data manipulation language) refers to statements which select, insert, update, or delete data.

SELECT Statement

Type
ANSI and ODBC with CQL++ extensions
Summary
Retrieves data from one or more tables or views.
Syntax
SELECT [ ALL | DISTINCT ] SelectList
FROM TableReferenceList
[ WHERE SearchCondition ]
[ GROUP BY ColumnName [ , ColumnName ]... ]
[ HAVING SearchCondition ]
[UNION SelectStatement ]
[ OrderByClause ]
Description
The SELECT statement is the heart of the SQL language, and it has many variations and forms. The ANSI features are described first, followed by the CQL extensions. Readers who are not familiar with the SQL language may wish to consult a user level SQL book for a more detailed description of the standard features.

CQL implements extensions to the SelectList, SearchCondition, and OrderByClause constructs. These contructs will appear twice in this section. The first time that they appear, only the ANSI features are described. Subsequently, the CQL extensions for these constructs are explained. This is intended to make the distinction between ANSI features and extensions easy to determine. The second appearance of the construct includes a complete syntax expression, including both ANSI features and extensions.

The following sections begin with the simplest SELECT statement, and illustrate SELECT capabilities in progressively complex examples. These examples use the sample files S, P, and SP which are created and populated by the CQL++ script newf supplied with CQL++. The CREATE TABLE and INSERT statements for S, P, and SP are:

CREATE TABLE S (SNO CHAR(5) NOT NULL,
SNAME CHAR(20),
STATUS DECIMAL(3),
CITY CHAR(15),
UNIQUE (SNO));

CREATE TABLE P (PNO CHAR(6) NOT NULL,
PNAME CHAR(20),
COLOR CHAR(6),
WEIGHT DECIMAL(3),
CITY CHAR(15),
UNIQUE (PNO));

CREATE TABLE SP (SNO CHAR(5) NOT NULL,
PNO CHAR(6) NOT NULL,
QTY DECIMAL(5),
UNIQUE (SNO,PNO));

INSERT INTO S (SNO,SNAME,STATUS,CITY)
VALUES ('S1','Smith',20,'London');

INSERT INTO S (SNO,SNAME,STATUS,CITY)
VALUES ('S2','Jones',10,'Paris');

INSERT INTO S (SNO,SNAME,STATUS,CITY)
VALUES ('S3','Blake',30,'Paris');

INSERT INTO S (SNO,SNAME,STATUS,CITY)
VALUES ('S4','Clark',20,'London');

INSERT INTO S (SNO,SNAME,STATUS,CITY)
VALUES ('S5','Adams',30,'Athens');

INSERT INTO P (PNO,PNAME,COLOR,WEIGHT,CITY)
VALUES ('P1','Nut','Red',12,'London');

INSERT INTO P (PNO,PNAME,COLOR,WEIGHT,CITY)
VALUES ('P2','Bolt','Green',17,'Paris');

INSERT INTO P (PNO,PNAME,COLOR,WEIGHT,CITY)
VALUES ('P3','Screw','Blue',17,'Rome');

INSERT INTO P (PNO,PNAME,COLOR,WEIGHT,CITY)
VALUES ('P4','Screw','Red',14,'London');

INSERT INTO P (PNO,PNAME,COLOR,WEIGHT,CITY)
VALUES ('P5','Cam','Blue',12,'Paris');

INSERT INTO P (PNO,PNAME,COLOR,WEIGHT,CITY)
VALUES ('P6','Cog','Red',19,'London');

INSERT INTO SP (SNO,PNO,QTY) VALUES ('S1','P1',300);
INSERT INTO SP (SNO,PNO,QTY) VALUES ('S1','P2',200);
INSERT INTO SP (SNO,PNO,QTY) VALUES ('S1','P3',400);
INSERT INTO SP (SNO,PNO,QTY) VALUES ('S1','P4',200);
INSERT INTO SP (SNO,PNO,QTY) VALUES ('S1','P5',100);
INSERT INTO SP (SNO,PNO,QTY) VALUES ('S1','P6',100);
INSERT INTO SP (SNO,PNO,QTY) VALUES ('S2','P1',300);
INSERT INTO SP (SNO,PNO,QTY) VALUES ('S2','P2',400);
INSERT INTO SP (SNO,PNO,QTY) VALUES ('S3','P2',200);
INSERT INTO SP (SNO,PNO,QTY) VALUES ('S4','P2',200);
INSERT INTO SP (SNO,PNO,QTY) VALUES ('S4','P4',300);
INSERT INTO SP (SNO,PNO,QTY) VALUES ('S4','P5',400);

COMMIT WORK;

Simple SELECT

The simplest SELECT statement is:
SELECT * FROM S
The asterisk (*) means all fields. This statement prints all rows in S.

Instead of using *, one or more field names can be used. For example:

SELECT PNO FROM SP
SELECT PNO,QTY FROM SP

The Authorization Identifier Prefix

In a SELECT statement, you may use an expression of the form Authorization.TableName in place of TableName. For example, instead of

SELECT * FROM S

you can use

SELECT * FROM CQL.S

The authorization identifier in this type of expression is used with the current authorization identifier to determine whether SELECT access is allowed. The form ... FROM S... is only allowable when the current authorization identifier owns the table. For example, if the current authorization identifier is CQL, then the statement
SELECT * FROM S
is equivalent to
SELECT * FROM CQL.S
and access is allowed. If the current authorization identifier is JOHN, then the statement
SELECT * FROM S
is equivalent to
SELECT * FROM JOHN.S
Note that JOHN.S is a different table than CQL.S. It is perfectly legal to have two tables with the same "last" name.

Note also that if JOHN does not own a table S, then
SELECT * FROM JOHN.S
is an error. It does not matter whether JOHN has SELECT privileges for table CQL.S. Finally, note that
SELECT * FROM S
does not default to CQL.S, regardless of any privileges JOHN may have for table CQL.S.

Suppose, however, that the current authorization identifier is JOHN, and the SELECT statement is
SELECT * FROM CQL.S
This is allowed if JOHN has SELECT privilege for table CQL.S. This is true if one of the following is true:

SELECT with Condition

To limit the retrieved data to certain rows a SearchCondition or WHERE clause is used. For example:

SELECT SNO FROM S WHERE CITY = 'Paris'

The equals operator (=) means "is equal to". The comparison operators are:
<>
Not equal
<
Less than
<=
Less than or equal
>
Greater than
>=
Greater than or equal
More than one comparison operator can be used with the AND and OR operators. For example:
SELECT SNO FROM S
WHERE CITY='Paris' AND STATUS > 20
Parentheses can be used to specify the order of evaluation. Without parentheses, AND is stronger than OR. The keyword NOT can be used to reverse the sense of any operation, for example:
SELECT SNO FROM S
WHERE ( CITY = 'Paris' OR CITY = 'London' )
AND NOT ( STATUS > 20 )
The second set of parentheses are not required. As in any programming language, it is best to use parentheses to be explicit about what you want to machine to do, if only to enhance program readability.

SELECT with DISTINCT - Eliminating Duplicate Rows

The keyword DISTINCT is used to eliminate duplicate rows in the retrieved data. For example:

SELECT DISTINCT PNO FROM SP

DISTINCT can be used with any query, regardless of complexity, to remove duplicate logical rows from the output.

SELECT with Two Tables

More than one table can be accessed with a SELECT statement. This is sometimes referred to as the ability to "join" tables. In CQL, two (or more) tables may be joined, and a table may also be joined to itself. For example, the following query can be described in words as "for each part supplied, get part number and names of all cities supplying the part." (See Introduction to Database Systems, C.J. Date, Addison-Wesley (third edition).)
SELECT DISTINCT PNO,CITY FROM SP,S WHERE SP.SNO=S.SNO
Two tables are used, S and SP. Since both tables have a field named SNO, the table name is used as a prefix to specify which SNO we are referring to. Thus, SP.SNO refers to the SNO field associated with file SP, while S.SNO refers to the SNO field associated with file S. The table name prefix can be used even if the column name is unambiguous.

The condition SP.SNO = S.SNO tells CQL how to join tables SP and S. The results of the query are identical if it is written
SELECT DISTINCT PNO,CITY FROM S,SP WHERE S.SNO=SP.SNO
or
SELECT DISTINCT PNO,CITY FROM SP,S WHERE SP.SNO=S.SNO
The query results are not sensitive to the sequence of table names or to the order in which the columns are mentioned in the WHERE clause. However, while the same rows are retrieved, they may be retrieved in a different order. The order of the returned rows is undefined (although the ANSI specification requires that it always be the same for a given query). To force the logical rows to be returned in a known sequence, use the ORDER BY clause.

The following example joins table S to itself. The operation can be expressed in words as "Get all pairs of supplier numbers such that the two suppliers are located in the same city."
SELECT FIRST.SNO, SECOND.SNO FROM S FIRST, S SECOND WHERE FIRST.CITY = SECOND.CITY AND FIRST.SNO < SECOND.SNO
The files are specified as S FIRST and S SECOND. FIRST and SECOND are aliases (or correlation names) for S. The first "incarnation" of S is subsequently referred to as FIRST, and the second "incarnation" is subsequently referred to as SECOND. The second comparison clause, FIRST.SNO < SECOND.SNO, eliminates pairs where both rows are the same, and eliminates duplicates. For example, the row where FIRST.SNO='S1' AND SECOND.SNO='S2' is selected, but the row where FIRST.SNO='S4' and SECOND.SNO='S1' is not selected.

Joins are not limited to two tables. The FIPS SQL test suite includes a SELECT where 10 tables are joined.

SELECT with ANY

Consider the following two queries, which both produce the same result set. The operation is "Get supplier names for suppliers who supply part P2."
SELECT DISTINCT SNAME FROM S,SP
WHERE S.SNO=SP.SNO AND SP.PNO='P2'

SELECT SNAME FROM S WHERE SNO = ANY
SELECT SNO FROM SP WHERE PNO='P2'
The second form introduces the ANY keyword, which can be used after all the comparison operators.

The format here is
SELECT SNAME FROM S WHERE SNO = ANY ( SubQuery )
where SubQuery is an entire additional select statement (actually, a SelectExpression which is similar to a select statement but cannot include the ORDER BY, GROUP BY, or HAVING clauses).

The result of the SubQuery is effectively a result set. CQL then tests whether SNO is equal to ANY of the values retrieved by the SubQuery. If SNO is equal to at least one of the values retrieved by the subquery, then = ANY is true.

Here is another example, using < ANY.

SELECT SNO FROM S WHERE STATUS < ANY
(SELECT STATUS FROM S)
This finds values of SNO for suppliers whose status is less than the status of at least one other supplier.

SELECT with IN

IN is used with sub-queries and is the same as = ANY. For example:
SELECT SNAME FROM S WHERE SNO IN 
(SELECT SNO FROM SP WHERE PNO='P2')

SELECT with Three Levels

A SubQuery can reference another SubQuery. For example:
SELECT SNAME FROM S WHERE SNO IN
(SELECT SNO FROM SP WHERE PNO IN
(SELECT PNO FROM P WHERE COLOR='Red'))
The number of levels allowed in a SELECT statement is 3. This can be increased, if desired, using a compile time switch.

SELECT with Interblock Reference

An interblock reference occurs when a SubQuery uses a value (in its SearchCondition) of a column from an outer query. For example, this query is a variation on a previous example:
SELECT SNAME FROM S WHERE 'P2' IN
(SELECT PNO FROM SP WHERE SNO=S.SNO)
Incidentally, this form is less efficient because of the interblock reference. However, there are many queries which cannot be performed without an interblock reference. An query with an interblock reference is sometimes referred to as a "correlated subquery".

The significance of the interblock reference is that the SubQuery must be performed for each row in the outer query. Since the value from the outer query changes, the inner query results are different for each outer query row.

Interblock Reference - Same Table in Both SELECTs

In this query, the interblock reference is to two incarnations of the same table.
SELECT DISTINCT PNO FROM SP SPX WHERE PNO IN
(SELECT PNO FROM SP WHERE SNO <> SPX.SNO)
This may be phrased "Gets part numbers for all parts supplied by more than one supplier". For each part (each outer query row), is there another supplier of that part (is the SubQuery empty)? This type of query cannot be performed without the interblock reference.

SELECT with ALL

The ALL keyword is used like the ANY keyword, and can follow any of the comparison operators. The following query "gets supplier names for suppliers who do not supply part P2".
SELECT SNAME FROM S WHERE 'P2' <> ALL
(SELECT PNO FROM SP WHERE SNO=S.SNO)
Note again the use of the interblock reference.

SELECT with Comparison Operator and SubQuery

The comparison operators can reference a SubQuery without ALL or ANY. For example, the following query "gets supplier numbers for suppliers who are located in the same city as supplier S1".
SELECT SNO FROM S WHERE CITY =
(SELECT CITY FROM S WHERE SNO='S1')
Note that subqueries in this context must return exactly one value. If zero values or two or more values are returned, an error occurs.

SELECT with EXISTS

EXISTS is used with subqueries to determine whether any values exist for a particular condition. The following query is another example of "get supplier names for suppliers who supply part P2".
SELECT SNAME FROM S WHERE EXISTS
(SELECT * FROM SP WHERE SNO=S.SNO AND PNO='P2')
Subqueries associated with EXISTS usually use SELECT *, since all that matters is whether any rows are found by the SubQuery. The meaning of EXISTS can be reversed with the keyword NOT. For example, the following query "gets supplier names for suppliers who do not supply part P2".
SELECT SNAME FROM S WHERE NOT EXISTS
(SELECT * FROM SP WHERE SNO=S.SNO AND PNO='P2')
Here is a three level query using NOT EXISTS. The query "gets supplier names for suppliers who supply all parts".
SELECT SNAME FROM S WHERE NOT EXISTS
(SELECT * FROM P WHERE NOT EXISTS
(SELECT * FROM SP WHERE SNO=S.SNO AND PNO=P.PNO))
To understand how this works, the following is an alternate word expression of the query: "Select supplier names for suppliers such that there does not exist a part that they do not supply". Note that the innermost SubQuery is correlated to both the outermost query and the middle query.

SELECT with Computed Values

The FieldExpression in a SELECT statement may be an expression rather than a simple field name. For example:

SELECT PNO, WEIGHT*454 FROM P

Supported operations are addition, subtraction, multiplication, division, and parentheses. Some other mathematical capabilities are provided by SQL functions, described in the next section.

SELECT with ANSI Functions

There are five ANSI functions: SUM, AVG, MIN, MAX, and COUNT. These functions are called "aggregate" functions because they make sense only when applied to more than one row. Aggregate functions can be used in the list of FieldExpressions or in a HAVING clause (described later). Aggregate functions cannot be used in a WHERE clause.
SELECT with COUNT Function
Syntax
COUNT ( expression ) | COUNT (*)
Counts the number of rows produced by a SELECT. COUNT is the only function which may be used with *. DISTINCT can be used to count the number of non-identical rows.

Examples

SELECT COUNT(*) FROM S
SELECT COUNT(*) FROM SP WHERE PNO='P2'
SELECT with SUM Function
Syntax
SUM ( NumericExpression )
ResultTypes InputTypes Description
SUM accumulates the total of its argument over the rows selected the query. For example,

SELECT SUM(QTY) FROM SP WHERE PNO = 'P2'

Computes the sum of the QTYs for rows in SP whose part number is 'P2'.
SELECT with AVG Function
Syntax
AVG ( NumericExpression )
Result Types Input Types AVG is the average function. For example:

SELECT AVG(QTY) FROM SP

Prints the average of all the QTY values in SP.

SELECT AVG(QTY) FROM SP WHERE PNO = 'P2'

Prints the average of QTY values whose PNO value is 'P2'.
SELECT with MIN Function
Syntax
MIN ( NumericExpression )
Result Types Input Types Description
MIN is the minimum value function. For example:

SELECT MIN(QTY) FROM SP

Prints the lowest QTY value in SP.

SELECT MIN(QTY) FROM SP WHERE PNO = 'P2'

Prints the lowest QTY value from rows in SP whose PNO value is 'P2'.
SELECT with MAX Function
Syntax
MAX ( NumericExpression )
Input Types Input Types MAX is the maximum value function. For example:

SELECT MAX(QTY) FROM SP

prints the highest QTY value in SP.

SELECT MAX(QTY) FROM SP WHERE PNO = 'P1'

prints the highest QTY value from rows in SP whose PNO value is 'P1'.

Select with String Functions

This section describes the string scalar functions. These functions are ODBC extensions. In the descriptions which follow, sexp means string expression.

The following list provides each function name, its arguments, and a brief description.

ASCII(sexp)
Returns the first character of the string as an integer.
CHAR(integer)
Returns the ASCII character corresponding to the integer argument.
CONCAT(sexp1,sexp2)
Concatinates sexp2 to sexp1.
INSERT(sexp1,start,len,sexp2)
Deletes length from sexp1 and inserts sexp2 into sexp1 beginning at character start.
LEFT(sexp,count)
Truncates sexp to leftmost count characters.
LTRIM(sexp)
Removes leading blanks from sexp.
LENGTH(sexp)
Returns length of sexp as an integer.
LOCATE(sexp1,sexp2)
Looks for sexp1 in sexp2. Returns integer indicating the position in sexp2 where sexp1 starts.
LOCATE(sexp1,sexp2,start)
Looks for sexp1 in sexp2, beginning start from the left of sexp2.
LCASE(sexp)
Converts all characters in sexp to lower case.
REPEAT(sexp,count)
Returns a string which contains count occurances of the first character of sexp. sexp must be a string of length 1.
REPLACE(sexp1,sexp2,sexp3)
Replaces occurances of sexp2 in sexp1 with sexp3.
RIGHT(sexp,count)
Returns the rightmost count characters of sexp.
RTRIM(sexp)
Removes trailing blanks from sexp.
SUBSTRING(sexp1,start,length)
Returns a portion of sexp1 beginning at start and continuing for length characters.
UCASE(sexp)
Converts all characters in sexp to upper case.

Numeric Functions

This section describes the numeric scalar functions. These functions are ODBC extensions. In the descriptions which follow, iexp means integer expression, nexp means numeric expression, and fexp means float expression. For trignometric functions, angles are expressed in radians.

The following list provides each function name, its arguments, and a brief description.

ABS(nexp)
Absolute value.
ACOS(fexp)
Arccosine.
ASIN(fexp)
Arcsine.
ATAN(fexp)
Arctangent.
ATAN2(fexp1,fexp2)
Arctangent. fexp1 and fexp2 specify x and y coordinates.
CEILING(nexp)
Returns the smallest integer greater than or equal to nexp.
COS(fexp)
Cosine.
COT(fexp)
Cotangent.
EXP(fexp)
Exponential value.
FLOOR(nexp)
Returns the largest integer less than or equal to nexp.
LOG(fexp)
Returns the natural log of fexp.
MOD(iexp1,iexp2)
Returns remainder of iexp1/iexp2.
PI()
Returns pi as a floating point number.
RAND()
Returns a random floating point number.
RAND(iexp)
Returns a random floating point number. Uses iexp as seed.
SIGN(nexp)
Returns -1 if nexp is negative, 0 if nexp is 0, and 1 if nexp is positive.
SIN(fexp)
Sine.
SQRT(fexp)
Square root.
TAN(fexp)
Tangent.

Time and Date Functions

This section describes functions acting on DATE, TIME, and TIMESTAMP columns. First, the ODBC time and date functions are described. Second, the CQL++ extension time and date functions are described. In these descriptions, dexp means date expression, texp means time expression, and tsexp means timestamp expression.
ODBC Time and Date Functions

The following list provides each function name, its arguments, and a brief description.

NOW()
Current date and time (as a TIMESTAMP value).
CURDATE()
Current date as a date value.
DAYOFMONTH(dexp)
Returns an integer between 1 and 31 representing the day of the month.
DAYOFWEEK(dexp)
Returns the day of the week corresponding to dexp. Returns an integer between 1 and 7, where 1 is Sunday and 7 is Saturday.
DAYOFYEAR
Returns the day of the year corresponding to dexp. 1 is first day of year, and 365 (or 366) is the last day of the year.
MONTH(dexp)
Returns the month as an integer, where 1 is January, 12 is December.
QUARTER(dexp)
Returns the quarter as an integer between 1 and 4.
WEEK(dexp)
Returns the week of the year as an integer, between 1 and 53.
YEAR(dexp)
Returns the year as an integer, using A.D. 1 as year 1.
CURTIME
Returns current time as a time value.
HOUR(texp)
Returns the hour in texp as an integer between 0 and 23.
MINUTE(texp)
Returns the minute in texp as an integer between 0 and 59.
SECOND(texp)
Returns the second in texp as an integer between 0 and 59.
CQL++ Extension Time and Date Functions

The following list provides each function name, its arguments, and a brief description.

TRUNCATE(tsexp)
Returns the time portion of tsexp as a TIME value.
TO_DATE(sexp)
Returns the date represented by string sexp as a DATE value.
TO_CHAR(tsexp,sexp)
Returns character representation of timestamp in tsexp, formatting according to sexp, as a CHAR value.
ADD_YEARS(tsexp,iexp)
Adds an integer number of years to a timestamp value.
ADD_MONTHS(tsexp,iexp)
Adds an integer number of months to a timestamp value.
ADD_DAYS(tsexp,iexp)
Adds an integer number of days to a timestamp value.
ADD_HOURS(tsexp,iexp)
Adds an integer number of hours to a timestamp value.
ADD_MINUTES(tsexp,iexp)
Adds an integer number of minutes to a timestamp value.
ADD_SECONDS(tsexp,iexp)
Adds an integer number of seconds to a timestamp value.
YEARS_BETWEEN(tsexp1,tsexp2)
Returns an integer value representing the number of years between two timestamp values.
MONTHS_BETWEEN(tsexp1,tsexp2)
Returns an integer value representing the number of months between two timestamp values.
DAYS_BETWEEN(tsexp1,tsexp2)
Returns an integer value representing the number of days between two timestamp values.
HOURS_BETWEEN(tsexp1,tsexp2)
Returns an integer value representing the number of hours between two timestamp values.
MINUTES_BETWEEN(tsexp1,tsexp2)
Returns an integer value representing the number of minutes between two timestamp values.
SECONDS_BETWEEN(tsexp1,tsexp2)
Returns an integer value representing the number of seconds between two timestamp values.

SELECT with BETWEEN

The BETWEEN operator provides a natural syntax for a condition which is greater than one value and less than another. For example,

SELECT * FROM S WHERE STATUS BETWEEN 15 AND 25

This is identical in meaning to

SELECT * FROM S WHERE STATUS >= 15 AND STATUS <= 25

SELECT with LIKE

The LIKE operator provides pattern matching capability for strings. The pattern is specified in a manner similar to Unix regular expressions, except that the symbols are different. For those familiar with Unix regular expressions, SQL uses % instead of *, and _ instead of the period. For those unfamiliar with Unix regular expressions, the following list describes each special symbol.
_ (underscore)
Any character (exactly one)
%
Any sequence of characters (zero or more)
Thus, '%s' matches any string which ends is s. 'abc' matches only the string abc. 'a_c' matches any three character string which begins with a and ends with c, such as abc, azc, etc. 'a%c' matches any string (of whatever length) which begins with a and ends with c, for example, abc, abdefghijklmnopqrc, ac, etc. Here is an example of an SQL statement using LIKE:

SELECT * FROM S WHERE SNAME LIKE '%k'

In sample table S, this retrieves data for Clark, which ends in k.

SELECT with GROUP BY

GROUP BY allows records in a file to be grouped, usually for subtotals but also for display purposes. For example, "for each part supplied, get the part number and the total quantity supplied of that part".

SELECT PNO,SUM(QTY) FROM SP GROUP BY PNO

The GROUP BY clause causes a subtotal to be taken and a line printed whenever the value of PNO changes.

If there is no explicit ORDER BY clause, then the query is ordered by the GROUP BY fields. If an explicit ORDER BY clause is used, the results may not be what you were expecting.

SELECT with GROUP BY and HAVING

The HAVING clause is a conditional expression used to eliminate some groups from a report. For example, "get part numbers for all parts supplied by more than one supplier".
SELECT PNO FROM SP
GROUP BY PNO HAVING
COUNT(*) > 1
The function within the HAVING clause applies to the group. Thus within a HAVING clause "SUM(QTY) > 100" may be true for a group although the individual values of QTY may be less than 100.

SELECT with UNION

The UNION operator is used to combine the results of two SELECT statements. The columns selected by each statement must match with respect to type. For example (from A Guide to the SQL Standard, C.J. Date, First Edition, P84):
SELECT P.PNO FROM P WHERE P.WEIGHT > 16
UNION
SELECT SP.PNO FROM SP WHERE SP.SNO = 'S2'
The UNION operator is different from other SQL operators with respect to its behavior with duplicate rows. For SELECT, duplicate rows are selected unless the DISTINCT qualifier is used. For UNION, duplicate rows are rejected unless the ALL qualifier is used. For example:
SELECT P.PNO FROM P WHERE P.WEIGHT > 16
UNION ALL
SELECT SP.PNO FROM SP WHERE SP.SNO = 'S2'

SELECT with INTERSECTION

The INTERSECTION operator finds the intersecting rows from two complete SELECT statements. That is, only rows which are retrieved by both statements are retrieved. INTERSECTION is a CQL extension and is not part of ANSI or ODBC SQL.

SELECT with MINUS

The MINUS operator finds the rows in one SELECT statement, and then removes from the result any of those rows which are also rows from the second SELECT statement. MINUS is a CQL extension and is not part of ANSI or ODBC SQL.

SELECT with ORDER BY

This section describes the various features of the ORDER BY clause. The standard ANSI features are described first, followed by the CQL extensions.
ANSI ORDER BY Features
In combination with the features described previously, the order of the resulting table may be specified with an ORDER BY clause. The format is either
ORDER BY ColumnName [ , ColumnName ]...

or

ORDER BY ColumnName [ , ColumnName ]... DESC
DESC means descending, and reverses the order of the output. Examples are:
SELECT * FROM S ORDER BY STATUS

SELECT SNO,STATUS FROM S
WHERE CITY='Paris'
ORDER BY STATUS DESC
ORDER BY with NODUP
NODUP prints blanks in a column if the value to be printed is the same as the value for the preceeding line. This is often desirable when ordering by a column for which there are multiple report lines for each column value. The syntax is:
ORDER BY ColumnName NODUP
For example,

SELECT * FROM S ORDER BY CITY NODUP
ORDER BY with BREAK
The BREAK keyword allows the user to skip lines between groups of a report. It is similar to GROUP BY, except that it allows the user to control how many lines are skipped. The syntax is:
ORDER BY ColumnName BREAK NumericExpression
where NumericExpression is the number of lines. For example,

SELECT * FROM S ORDER BY CITY BREAK

or

SELECT SNO, SNAME, CITY FROM S ORDER BY CITY DESC BREAK 2

To advance the report to the next page, use BREAK PAGE, for example:

SELECT * FROM S ORDER BY CITY BREAK PAGE

ORDER BY with COMPUTE
COMPUTE is used to print some value or computation when an ordering field changes. This is most frequently used for printing subtotals. The syntax is:
ORDER BY ColumnName
COMPUTE ValueExpression
AT ColumnNumber
For example,

SELECT SNO, SNAME, STATUS, CITY FROM S ORDER BY CITY COMPUTE SUM(STATUS) AT 3

Note that the aggregate functions (SUM, AVG, MIN, and MAX) are allowed in the COMPUTE statement.

COMPUTE can be combined with BREAK or BREAK PAGE. For example:

SELECT SNO, SNAME, STATUS, CITY
FROM S
ORDER BY CITY BREAK 2
COMPUTE SUM(STATUS) AT 3
BREAK PAGE

SELECT with AT END REPORT

The AT END REPORT clause is used to print values or calculations at the end of a report. The syntax is similar to the COMPUTE clause. AT END REPORT is often used to print totals (or grand totals). Aggregate functions are allowed in the AT END REPORT clause. The syntax is:
SELECT ... AT END REPORT
[ BREAK [ NumericExpression | PAGE ] ]
COMPUTE ValueExpression
AT ColumnNumber

For example,

SELECT * FROM S
AT END REPORT
COMPUTE SUM(STATUS) AT 3

Some variations:

SELECT * FROM S
AT END REPORT BREAK 2
COMPUTE SUM(STATUS) AT 3

SELECT * FROM S
WHERE STATUS > 10
AT END REPORT BREAK PAGE
COMPUTE SUM(STATUS) AT 3

Report Generation Enhancements

This section describes CQL features which allow control of the format of a report produced by a SELECT statement. All the features described are extensions to ANSI and ODBC SQL and are not available on other SQL systems.
NODUP Modifier
The user can follow a ValueExpression with the keyword NODUP, which prints a blank if the value of a column is the same as the value of that column on the preceeding line. The syntax is:
SELECT ValueExpression NODUP ...

For example,

SELECT SNO, SNAME, CITY NODUP FROM S
Pattern String Override
In CQL, each column has a pattern string associated with it. This pattern string, produced from the column attributes length, scale, and precision, controls the format of that column in select statements.

CQL allows the default pattern string to be replaced by a pattern string explicitly included in a SELECT statement. The syntax is:

SELECT ValueExpression % PatternString ...

For example,

SELECT SNAME % 'XXXXXXXX' FROM S
X allows any character, and the width is 8 because there are 8 X's. This could have been written as '10X'. For complete pattern string details, see
PatternStrings.
Column Heading Override
In CQL, each column has a column heading associated with it. This column heading is usually the column name, or the expression being displayed (e.g., "STATUS + 2").

CQL allows the default column heading to be replaced by a column heading explicitly included in a SELECT statement. The syntax is:

SELECT ValueExpression ColumnHeading ...

For example,

SELECT SNAME 'Supplier Name' FROM S
The symbol | has a special meaning within column headings. It is used to split a heading onto several lines. The preceeding example with a two line heading is:
SELECT SNAME 'Supplier|Name' FROM S
Column Justification
By default, all columns are left justified in a SELECT statement. CQL allows the user to specify centering or right justification as part of the SELECT. The syntax is:
SELECT ValueExpression JustificationSpecifier ...

JustificationSpecifier:
CENTER | RIGHT

For example,

SELECT SNAME CENTER, STATUS RIGHT FROM S
Wrapping
For CHAR columns or expressions, CQL normally truncates the output if the width is greater than the column width (as specified by the pattern string). The user can specify wrapping or word wrapping in the SELECT statement. The syntax is:
SELECT ValueExpression WrapSpecifier...

WrapSpecifier ::=
WRAPPED | WORD_WRAPPED

For example,

SELECT CITY WRAPPED % 'XXX' FROM S
Note the use of the pattern string override. Since the default pattern string for CITY specifies the maximum width allowed for CITY, wrapping won't occur unless the default pattern string is overridden (except for VARCHAR columns).
Summary of Column Specification Features
The syntax of the SELECT column specification is:
ValueExpression
[ NODUP ]
[ ColumnHeading ]
[ LEFT | CENTER | RIGHT ]
[ WRAPPED | WORD_WRAPPED ]
[ % PatternOverride ]

INSERT, UPDATE, and DELETE

This section describes the form of the statements used to insert data into tables, update table data, and delete data from tables. For all the statements in this section, changes are not permanent until a COMMIT WORK statement is executed. Unwanted changes may be removed using the ROLLBACK WORK statement.

The INSERT INTO Statement

There are two forms of the INSERT INTO statement. The first form uses a list of inserted values. The second form uses a SELECT statement to specify values to be retrieved from another table and inserted.
INSERT INTO with Values
Syntax
INSERT INTO TableName
[ ( ColumnName [ , ColumnName ]... ) ]
VALUES (  value, [ , value ]... )
INSERT INTO is used to insert data into a table. The list of column names following INSERT INTO TableName is optional. If the list is included, the number of values must be the same as the number of column names listed. If the list is omitted, then the number of values must be the same as the number of columns in the table.

The value types must match the types of the columns associated with the table. CHAR fields must be initialized with quoted strings. Numeric columns must be initialized with appropriate numeric data (the expected automatic conversions are provided). DATE columns must be initialized with valid DATE values. TIME columns must be initialized with valid TIME values. TIMESTAMP columns must be initialized with a valid DATE, TIME, or TIMESTAMP value.

INSERT INTO with SELECT
Syntax
INSERT INTO TableName1
[ ( ColumnName [ , ColumnName ]... ) ]
SELECT ValueExpression [ , ValueExpression ]...
FROM TableName2
WHERE SearchCondition
This statement inserts the results of the SELECT from TableName2 into TableName1. The list of column names following INSERT INTO TableName is optional. If the list is included, the number of ValueExpressions in the SELECT statement must be the same as the number of column names listed. If the list is omitted, then the number of ValueExpressions in the SELECT statement must be the same as the number of columns in the table.

The ValueExpression types must match the table column types. CHAR fields must be initialized with CHAR ValueExpressions. Numeric columns must be initialized with appropriate numeric data (the expected conversions are made automatically). DATE columns must be initialized with valid DATE values.

The UPDATE Statements

There are two forms of the UPDATE statement. The first form, called "searched" update, uses a WHERE clause to specify the rows to be updated, and can be used in C programs and in the interpreter. The second form, called "positioned" update, can only be used in C programs to update the "current logical row" of an open cursor.
The Searched UPDATE Statement
Syntax
UPDATE TableName
SET ColumnName = ValueExpression [ , ColumnName = ValueExpression ]...
WHERE SearchCondition
The searched UPDATE modifies one or more rows satisfying a WHERE clause. The values provided must match the type of the table columns involved. CHAR columns must be SET with CHAR expressions, numeric columns must be initialized with numeric data (the expected conversions are made automatically), and DATE fields must be initialized with valid date values.
Positioned UPDATE
Syntax
UPDATE TableName
SET ColumnName = ValueExpression [ , ValueExpression ]...
WHERE CURRENT OF CursorName
The "current logical row" of cursor CursorName is updated. The expressions must match the types of the column names. Character string fields must be assigned character string values, and numeric fields must be assigned numeric values (the expected conversions are made automatically). Only floating point fields can be assigned values expressed in exponential notation.

Positioned UPDATE is used only in C programs. The referenced cursor must be open and not at beginning or end of file.

The DELETE Statements

There are two forms of the DELETE statement, known as "searched" delete and "positioned" delete. Searched delete removes records satisfying a WHERE clause. Positioned delete deletes the "current logical row" of an open cursor.
Searched DELETE
Syntax
DELETE FROM TableName WHERE SearchCondition
The searched DELETE can delete 0 rows (if none match the search condition), or one or more rows. In the interpreter, CQL displays the number of records deleted.
Positioned DELETE
Syntax
DELETE FROM TableName
WHERE CURRENT OF CursorName
Cursor CursorName must be opened and must not be at beginning of file or end of file.

Statements Specific to Embedded SQL

This section describes statements used in embedded SQL files. Embedded SQL files are C++ (or C) files containing EXEC SQL statements. The SQL preprocessor pp translates these files into ordinary C++ (or C) files. Embedded SQL statements follow the SQL syntax described in this chapter, surrounded by EXEC SQL and ending with a semicolon. Embedded SQL statements can also reference host variables, which are C++ or C variables defined in the source file. To make these variables known to the SQL preprocessor, their declarations are contained within an SQL declare section. A declare section begins with the statement:
EXEC SQL BEGIN DECLARE SECTION;

and ends with the statement

EXEC SQL END DECLARE SECTION;

For example,

EXEC SQL BEGIN DECLARE SECTION;
char x[10];
short y;
EXEC SQL END DECLARE SECTION;
Host variables x and y are then referred to within SQL statements as :x and :y. For example,
EXEC SQL SELECT A,B INTO :x, :y FROM XYZ WHERE C='constant1';
After this statement is executed, the values of columns A and B are in x and y, respectively.