1.2.1 What is SQL?

Structured Query Language (SQL) is a standardized language for defining and manipulating data in a relational database. In accordance with the relational model of data, the database is perceived as a set of tables, relationships are represented by values in tables, and data is retrieved by specifying a result table that can be derived from one or more tables. DB2 transforms the specification of a result table into a sequence of internal operations that optimize data retrieval. This transformation occurs when the SQL statement is prepared. This transformation is also known as binding. 1.2.2 Static SQL The source form of a static SQL statement is embedded within an application program written in a host language such as COBOL. The statement is prepared before the program is executed and the operational form of the statement persists beyond the execution of the program. A source program containing static SQL statements must be processed by an SQL precompiler before it is compiled. The precompiler checks the syntax of the SQL statements, turns them into host language comments, and generates host language statements to invoke DB2. 1.2.3 Dynamic SQL A dynamic SQL statement is prepared during the execution of an SQL application, and the operational form of the statement is not persistent. The source form of the statement is a character string passed to DB2 by an application program using the static SQL statement PREPARE or EXECUTE IMMEDIATE. DB2 also uses dynamic SQL for system-directed access (1) . 1.2.4 Interactive SQL In this book, interactive SQL refers to SQL statements submitted to SPUFI (SQL processor using file input). SPUFI prepares and executes these statements dynamically.

1.2.6 Indexes An index is an ordered set of pointers to rows of a base table. Each index is based on the values of data in one or more columns. An index is an object that is separate from the data in the table. When you define an index using the CREATE INDEX statement, DB2 builds this structure and maintains it automatically. Indexes can be used by DB2 to improve performance and ensure uniqueness. In most cases, access to data is faster with an index. A table with a unique index cannot have rows with identical keys.

1.2.7 Keys A key is one or more columns that are identified as such in the description of a table, an index, or a referential constraint. The same column can be part of more than one key. A key composed of more than

one column is called a composite key. A composite key is an ordered set of columns of the same table. The ordering of the columns is not constrained by their ordering within the table. The term value, when used with respect to a composite key, denotes a composite value. Thus, a rule such as, "the value of the foreign key must be equal to the value of the primary key" means that each component of the value of the foreign key must be equal to the corresponding component of the value of the primary key. A unique key is a key that is constrained so that no two of its values are equal. The constraint is enforced by DB2 during the execution of the LOAD utility and the SQL INSERT and UPDATE statements. The mechanism used to enforce the constraint is a unique index. Thus, every unique key is a key of a unique index. Such an index is also said to have the UNIQUE attribute. Subtopics 1.2.7.1 Primary Keys 1.2.7.2 Foreign Keys

1.2.7.1 Primary Keys A primary key is a unique key that is a part of the definition of a table. A table cannot have more than one primary key, and the columns of a primary key cannot contain null values. Primary keys are optional and can be defined in SQL CREATE TABLE or ALTER TABLE statements. The unique index on a primary key is called a primary index. When a primary key is defined in an SQL CREATE TABLE statement, the table is marked unavailable until the primary index is created by the user. When a primary key is defined in an SQL ALTER TABLE statement, a unique index must already exist on the columns of that primary key. This unique index is designated as the primary index. 1.2.7.2 Foreign Keys A foreign key is a key that is specified in the definition of a referential constraint. A foreign key refers to or is related to a specific primary key. A table can have zero or more foreign keys. The value of a composite foreign key is null if any component of the value is null. 1.2.8 Storage Structures In DB2, a storage structure is a set of one or more VSAM data sets that hold DB2 tables or indexes. A storage structure is also called a page set. A storage structure can be one of the following: table space A table space can hold one or more base tables. All tables are kept in table spaces. A table space can be defined using the CREATE TABLESPACE statement. An index space contains a single index. An index space is defined when the index is defined using the CREATE INDEX statement.

index space

1.2.9 Storage Groups Defining and deleting the data sets of a storage structure can be left to DB2. If it is left to DB2, the storage structure has an associated storage group. The storage group is a list of DASD volumes on which DB2 can allocate data sets for associated storage structures. 1.2.10 Databases In DB2, a database is a set of table spaces and index spaces. These index spaces contain indexes on the tables in the table spaces of the same database. Databases are defined using the CREATE DATABASE statement and are primarily used for administration. Whenever a table space is created, it is explicitly or implicitly assigned to an existing database. 1.2.11 Catalog Each DB2 maintains a set of tables containing information about the data under its control. These tables are collectively known as the catalog. The catalog tables contain information about DB2 objects such as tables, views, and indexes. In contrast, the catalogs maintained by access method services are known as "integrated catalog facility catalogs." Tables in the catalog are like any other database tables with respect to retrieval. If you have authorization, you can use SQL statements to look at data in the catalog tables in the same way that you retrieve data from any other table in the system. Each DB2 ensures that the catalog contains accurate descriptions of the objects that the DB2 controls. 1.2.12 Views A view provides an alternative way of looking at the data in one or more tables. A view is a named specification of a result table. The specification is an SQL SELECT statement that is effectively executed whenever the view is referenced in an SQL statement. At any time, the view consists of the rows that would result if the subselect were executed. Thus, a view can be thought of as having columns and rows just like a base table. However, columns added to the base tables after the view is defined do not appear in the view. For retrieval, all views can be used like base tables. Views can be used to control access to a table and make data easier to use. Access to a view can be granted without granting access to the table. The view can be defined to show only portions of data in the table. A view can show summary data for a given table, combine two or more tables in meaningful ways, or show only the selected rows that are pertinent to the process using the view. Example: The following SQL statement defines a view named XYZ. The view represents a table whose columns are named EMPLOYEE and WHEN_HIRED. The data in the table comes from the columns EMPNO and HIREDATE of the sample table DSN8310.EMP. The rows from which the data is taken are for employees in departments A00 and D11. CREATE VIEW XYZ (EMPLOYEE, WHEN_HIRED) AS SELECT EMPNO, HIREDATE FROM DSN8310.EMP WHERE WORKDEPT IN ('A00', 'D11');

An index cannot be created for a view. However, an index created for a table on which a view is based might improve the performance of operations on the view. Read-only views cannot be used for insert, update, and delete operations. Read-only views are discussed under the CREATE VIEW statement in Chapter 6 of SQL Reference. Like all result tables, the table represented by a view is not persistent. On the other hand, the definition of a view persists in the DB2 catalog. An SQL DROP VIEW statement can drop a view, and the definition of the view is removed from the catalog. The definition of a view is also removed from the catalog when any view or base table on which the view depends is dropped. 1.2.13 Application Processes All SQL programs execute as part of an application process. An application process involves the execution of one or more programs, and is the unit to which DB2 allocates resources and locks. Different application processes may involve the execution of different programs, or different executions of the same program. The means of initiating and terminating an application process are dependent on the environment.

1.2.14 Packages and Application Plans A package contains control structures used to execute SQL statements. Packages are produced during program preparation. The control structures can be thought of as the bound or operational form of SQL statements taken from a database request module (DBRM). The DBRM contains SQL statements extracted from the source program during program preparation. All control structures in a package are derived from the SQL statements embedded in a single source program. An application plan relates an application process to a local instance of DB2, specifies processing options, and contains one or both of the following elements: _ _ A list of package names The bound form of SQL statements taken from one or more DBRMs.

Every DB2 application requires an application plan. Plans and packages are created using the DB2 subcommands BIND PLAN and BIND PACKAGE, respectively. 1.2.15 Authorization and Privileges Before it can execute a specific SQL statement, a process must have appropriate DB2 authority. A process derives this authority from its authorization IDs. These supply the needed authority in the following ways: _ _ By their ownership of objects referred to in the statement By their possession of various DB2 authorities and privileges.

2.1.1 Result Tables The data retrieved through SQL is always in the form of a table. In the DB2 library, this table is called a result table. Like the tables from which the data is retrieved, a result table has rows and columns. A program fetches this data one row at a time. Example: This SELECT statement:

SELECT LASTNAME, FIRSTNME, PHONENO FROM DSN8310.EMP WHERE WORKDEPT = 'D11' ORDER BY LASTNAME; gives this result: LASTNAME =============== ADAMSON BROWN JOHN JONES LUTZ PIANKA SCOUTTEN STERN WALKER YAMAMOTO YOSHIMURA FIRSTNME ============ BRUCE DAVID REBA WILLIAM JENNIFER ELIZABETH MARILYN IRVING JAMES KIYOSHI MASATOSHI PHONENO ======== 4510 4501 0672 0942 0672 3782 1682 6423 2986 2890 2890

The result table is displayed in this form after it is fetched and formatted by SPUFI. Your results might not look the same.

2.1.3 Selecting All Columns:

SELECT *

You do not need to know the column names to select DB2 data. Use an asterisk (*) in the SELECT clause to indicate "all columns" from each selected row of the named table. This SQL statement: SELECT * FROM DSN8310.DEPT; gives this result: DEPTNO ====== A00 B01 C01 D01 DEPTNAME ==================================== SPIFFY COMPUTER SERVICE DIV. PLANNING INFORMATION CENTER DEVELOPMENT CENTER MGRNO ====== 000010 000020 000030 -----ADMRDEPT ======== A00 A00 A00 A00 LOCATION =============

D11 D21 E01 E11 E21 F22 G22 H22 I22 J22

MANUFACTURING SYSTEMS ADMINISTRATION SYSTEMS SUPPORT SERVICES OPERATIONS SOFTWARE SUPPORT BRANCH OFFICE F2 BRANCH OFFICE G2 BRANCH OFFICE H2 BRANCH OFFICE I2 BRANCH OFFICE J2

000060 000070 000050 000090 000100 --------------------------

D01 D01 A00 E01 E01 E01 E01 E01 E01 E01

The SELECT statement example retrieves data from each column (SELECT *) of each retrieved row of the DSN8310.DEPT table. Because no WHERE clause is specified, data from all rows are retrieved. The dashes for MGRNO in the fourth row of the result table indicate that this value is null. It is null because a manager is not identified for this department. Null values are described under "Selecting Rows with Null Values" in topic 2.1.5.1. 2.1.4 Selecting Some Columns: SELECT column-name

Select the column or columns you want by naming each column. All columns appear in the order you specify, not in their order in the table. This SQL statement: SELECT MGRNO, DEPTNO FROM DSN8310.DEPT; gives this result: MGRNO ====== 000010 000020 000030 -----000050 000060 000070 000090 000100 -------------------------DEPTNO ====== A00 B01 C01 D01 E01 D11 D21 E11 E21 F22 G22 H22 I22 J22

The example SELECT statement retrieves data contained in the two named columns of each row in the DSN8310.DEPT table. You can retrieve one column or up to as many as 750 columns.

Use a WHERE clause to select data from only the rows that meet certain conditions. A WHERE clause specifies a search condition. A search condition consists of one or more predicates. A predicate specifies a test you want DB2 to apply to each table row.

When a predicate is evaluated for a row, it yields one of the following: TRUE FALSE UNKNOWN Apply the operation to the row. Do not apply the operation to the row. Do not apply the operation to the row.

+------------------------------------------------------------------------+ ¦ Table 2. Comparison Operators Used in Conditions ¦ +------------------------------------------------------------------------¦ ¦ Type of ¦ Specified ¦ Example ¦ ¦ comparison ¦ with... ¦ ¦ +--------------------------------+--------------+------------------------¦ ¦ Equal to null ¦ IS NULL ¦ PHONENO IS NULL ¦ +--------------------------------+--------------+------------------------¦ ¦ Equal to ¦ = ¦ DEPTNO = 'X01' ¦ +--------------------------------+--------------+------------------------¦ ¦ Not equal to ¦ <> ¦ DEPTNO <> 'X01' ¦ +--------------------------------+--------------+------------------------¦ ¦ Less than ¦ < ¦ AVG(SALARY) < 30000 ¦ +--------------------------------+--------------+------------------------¦ ¦ Less than or equal to ¦ <= ¦ AGE <= 25 ¦ +--------------------------------+--------------+------------------------¦ ¦ Not less than ¦ >= ¦ AGE >= 21 ¦ +--------------------------------+--------------+------------------------¦ ¦ Greater than ¦ > ¦ SALARY > 2000 ¦ +--------------------------------+--------------+------------------------¦ ¦ Greater than or equal to ¦ >= ¦ SALARY >= 5000 ¦ +--------------------------------+--------------+------------------------¦ ¦ Not greater than ¦ <= ¦ SALARY <= 5000 ¦ +--------------------------------+--------------+------------------------¦ ¦ Similar to another value ¦ LIKE ¦ NAME LIKE '%SMITH%' or ¦ ¦ ¦ ¦ STATUS LIKE 'N_' ¦ +--------------------------------+--------------+------------------------¦ ¦ At least one of two conditions ¦ OR ¦ HIREDATE < ¦ ¦ ¦ ¦ '1965-01-01' OR SALARY ¦ ¦ ¦ ¦ < 16000 ¦ +--------------------------------+--------------+------------------------¦ ¦ Both of two conditions ¦ AND ¦ HIREDATE < ¦ ¦ ¦ ¦ '1965-01-01' AND ¦ ¦ ¦ ¦ SALARY < 16000 ¦ +--------------------------------+--------------+------------------------¦ ¦ Between two values ¦ BETWEEN ¦ SALARY BETWEEN 20000 ¦ ¦ ¦ ¦ AND 40000 ¦ +--------------------------------+--------------+------------------------¦ ¦ Equals a value in a set ¦ IN (X, Y, Z) ¦ DEPTNO IN ('B01', ¦ ¦ ¦ ¦ 'C01', 'D01') ¦ +------------------------------------------------------------------------+ You can also search for rows that do not satisfy one of the above conditions, by using the NOT keyword before the specified condition. "Selecting Rows Using the NOT Keyword or Comparison Operators" in topic 2.1.5.4 for more information about using the NOT keyword. Subtopics 2.1.5.1 Selecting Rows with Null Values 2.1.5.2 Selecting Rows by Character or Numeric Data Values 2.1.5.3 Selecting Rows Using Inequalities See

2.1.5.4 Selecting Rows Using the NOT Keyword or Comparison Operators 2.1.5.5 Selecting Values Similar to a Character String

2.1.5.1 Selecting Rows with Null Values A null value indicates the absence of a column value from a row. value is not the same as zero or all blanks. A null

A WHERE clause can specify a column that, for some rows, contains a null value. Normally, values from such a row are not retrieved, because a null value is neither less than, equal to, nor greater than the value specified in the condition. To select values from rows that contain null values; specify: WHERE column-name IS NULL You can also use a predicate to screen out null values, specify: WHERE column-name IS NOT NULL 2.1.5.2 Selecting Rows by Character or Numeric Data Values Use the equal (=) comparison operator to select data only from the rows that contain a data value in the specified column that is equivalent to the value specified. To select only the rows where the department number is A00, use WHERE WORKDEPT = 'A00' in your SQL statement: SELECT WORKDEPT, FIRSTNME, LASTNAME FROM DSN8310.EMP WHERE WORKDEPT = 'A00'; The statement retrieves the department number and the first and last name of each employee in department A00. 2.1.5.3 Selecting Rows Using Inequalities You can use the following inequality comparison operators in predicates: _ _ _ _ less than (<) greater than (>) less than or equal to (<=) greater than or equal to (>=).

To select all employees hired before January 1, 1960, use: SELECT HIREDATE, FIRSTNME, LASTNAME FROM DSN8310.EMP WHERE HIREDATE < '1960-01-01'; The example retrieves the date hired and the name for each employee hired before 1960. 2.1.5.4 Selecting Rows Using the NOT Keyword or Comparison Operators

Use the NOT keyword or comparison operators (<>, >=, and <=) to select all rows except the rows identified with the search condition. (2) The NOT keyword or comparison operators must precede the search condition. To select all managers whose compensation is not greater than $30,000, use: SELECT WORKDEPT, EMPNO FROM DSN8310.EMP WHERE NOT (SALARY + BONUS + COMM) > 30000 AND JOB = 'MANAGER' ORDER BY WORKDEPT; The following WHERE clauses are equivalent: _ _ WHERE NOT DEPTNO ='A00' WHERE DEPTNO <> 'A00' The

The NOT keyword cannot be used with the comparison operators. following WHERE clause results in an error: Wrong: WHERE DEPT NOT = 'A00'

You also can precede other SQL keywords with NOT: NOT LIKE, NOT IN, or NOT BETWEEN are all acceptable. For example, the following two clauses are equivalent: WHERE MGRNO NOT IN ('000010', '000020') WHERE NOT MGRNO IN ('000010', '000020') (2) Although the not sign (¬) can be used with comparison operators, it is not recommended. See Chapter 3 of SQL Reference for more information on basic predicates.

2.1.5.5 Selecting Values Similar to a Character String Use LIKE to specify a character string that is similar to the column value of rows you want to select: _ _ _ Use a percent sign (%) to indicate any string of zero or more characters. Use an underscore (_) to indicate any single character. Use the LIKE predicate with character or graphic data only, not with numeric or datetime data.

Subtopics 2.1.5.5.1 Selecting Values Similar to a String of Unknown Characters 2.1.5.5.2 Selecting a Value Similar to a Single Unknown Character 2.1.5.5.3 Selecting a Value Similar to a String Containing a % or an _ 2.1.5.5.1 Selecting Values Similar to a String of Unknown Characters The percent sign (%) means "any string or no string." The following SQL statement selects data from each row for employees with the initials "E. H." SELECT FIRSTNME, LASTNAME, WORKDEPT FROM DSN8310.EMP WHERE FIRSTNME LIKE 'E%' AND LASTNAME LIKE 'H%';

The following SQL statement selects data from each row of the department table where the department name contains "CENTER" anywhere in its name. SELECT DEPTNO, DEPTNAME FROM DSN8310.DEPT WHERE DEPTNAME LIKE '%CENTER%'; Assume the DEPTNO column is defined as a three-character column of fixed length. The sample SQL statement ...WHERE DEPTNO LIKE 'E%1'; could be specified to select all department numbers that begin with E and end with 1. If E1 is one of your department numbers, it is not selected because the blank that follows 1 is not taken into account. If the DEPTNO column had been defined as a three-character column of varying-length, department E1 would have been selected because varying-length columns can have any number of characters, up to and including the maximum number specified when the column was created. The following SQL statement selects data from each row of the department table where the department number starts with an E and contains a 1. SELECT DEPTNO, DEPTNAME FROM DSN8310.DEPT WHERE DEPTNO LIKE 'E%1%';

2.1.5.5.2 Selecting a Value Similar to a Single Unknown Character The underscore (_) means "any single character." In the following SQL statement, SELECT DEPTNO, DEPTNAME FROM DSN8310.DEPT WHERE DEPTNO LIKE 'E_1'; 'E_1' means "E, followed by any character, followed by 1." (Be careful: '_' is an underscore character, not a hyphen.) If two-character department numbers were also allowed, 'E_1' would select only three-character department numbers that begin with E and end with 1. The SQL statement below selects data from each row whose four-digit phone number has the first three digits of 378. SELECT LASTNAME, PHONENO FROM DSN8310.EMP WHERE PHONENO LIKE '378_'; 2.1.5.5.3 Selecting a Value Similar to a String Containing a % or an _ To search for a % or an _ as a literal part of your string, use the ESCAPE clause and an escape character with the LIKE predicate. In the following example the ESCAPE '+' indicates that the + is the escape character in the search condition. For example: ...WHERE C1 LIKE 'AAAA+%BBB%' ESCAPE '+' searches for a string starting with AAAA%BBB. The escape character (+) in front of the first % indicates that the % is a single character and that it is part of the search string. The second %, which is not preceded by

an escape character, indicates that the string can be followed by any number of (or no) characters. In this example, putting '++' in the string would allow you to search for a single plus sign (+) as part of the string.

2.1.6 Selecting Rows Subject to Multiple Conditions The following sections explain ways to use more than one predicate. The order in which the statement is processed does not depend on the order in which you specify comparisons. Using parentheses with multiple search conditions can help control the order of processing. Subtopics 2.1.6.1 Using AND, OR, and Parentheses to Relate Predicates 2.1.6.2 Using BETWEEN to Specify Ranges to Select 2.1.6.3 Using IN to Specify Values in a List

2.1.6.1 Using AND, OR, and Parentheses to Relate Predicates Use AND, OR, and parentheses to form Boolean combinations of predicates. Use AND to specify multiple predicates that must be satisfied: SELECT EMPNO, HIREDATE, SALARY FROM DSN8310.EMP WHERE HIREDATE < '1965-01-01' AND SALARY < 16000; This example retrieves the employee number, date hired, and salary for each employee hired before 1965 and having a salary of less than $16,000 per year. Use OR when specifying two predicates, of which at least one must be satisfied: SELECT EMPNO, HIREDATE, SALARY FROM DSN8310.EMP WHERE HIREDATE < '1965-01-01' OR SALARY < 16000; This example retrieves the employee number, date hired, and salary for each employee who either was hired before 1965, or has a salary less than $16,000 per year, or both. If you use more than two conditions with AND or OR, you can use parentheses to make sure the intended condition is interpreted correctly. For example, this WHERE clause: WHERE (HIREDATE < '1965-01-01' AND SALARY < 20000) OR (EDLEVEL < 13) selects the row of each employee that satisfies at least one of the following conditions: _ _ The employee was hired before 1965 AND is paid less than $20,000. The employee's education level is less than 13.

Based on this WHERE clause, the selected rows are for employees 000290, 000310, and 200310. With the parentheses moved, however, the meaning of the WHERE clause

changes significantly: WHERE HIREDATE < '1965-01-01' AND (SALARY < 20000 OR EDLEVEL < 13) This clause selects the row of each employee that satisfies both of the following conditions: _ _ The employee was hired before 1965. The employee's salary is less than $20,000 OR the employee's education level is less than 13. one for employee

Based on this WHERE clause, two rows are selected: 000310 and one for employee 200310.

The following SQL statement selects the employee number of each employee that satisfies one of the following conditions: _ _ Hired before 1965 and salary is less than $20,000 Hired after January 1, 1965, and salary is greater than $20,000. SELECT EMPNO FROM DSN8310.EMP WHERE (HIREDATE < '1965-01-01' AND SALARY < 20000) OR (HIREDATE > '1965-01-01' AND SALARY > 20000); When using the NOT condition with AND and OR, the placement of the parentheses is important. In this SQL statement, only the first predicate (SALARY >= 50000) is negated. SELECT EMPNO, EDLEVEL, JOB FROM DSN8310.EMP WHERE NOT (SALARY >= 50000) AND (EDLEVEL < 18); This SQL statement retrieves the employee number, education level, and job title of each employee who satisfies both of the following conditions: _ _ The employee's salary is less $50,000. The employee's education level is less than 18.

To negate a set of predicates, enclose the entire set in parentheses and precede the set with the NOT keyword. SELECT EMPNO, EDLEVEL, JOB FROM DSN8310.EMP WHERE NOT (SALARY >= 50000 AND EDLEVEL >= 18); This SQL statement retrieves the employee number, education level, and job title of each employee who satisfies at least one of the following conditions: _ _ The employee's salary is less than $50,000. The employee's education level is less than 18.

2.1.6.2 Using BETWEEN to Specify Ranges to Select You can retrieve data from each row whose column has a value within two limits; use BETWEEN.

Specify the lower boundary of the BETWEEN condition first, then the upper boundary. The limits are inclusive. If you specify WHERE column-name BETWEEN 6 AND 8 (and if the value of the column-name column is an integer), DB2 selects all rows whose column-name value is 6, 7, or 8. If you specify a range from a larger number to a smaller number (for example, BETWEEN 8 AND 6), the predicate is always false. Example SELECT DEPTNO, MGRNO FROM DSN8310.DEPT WHERE DEPTNO BETWEEN 'C00' AND 'D31'; The example retrieves the department number and manager number of each department whose number is between C00 and D31. Example SELECT EMPNO, SALARY FROM DSN8310.EMP WHERE SALARY NOT BETWEEN 40000 AND 50000; The example retrieves the employee numbers and the salaries for all employees who either earn less than $40,000 or more than $50,000. You can use the BETWEEN predicate when comparing floating point data in your program with decimal data in the database to define a tolerance around the two numbers being compared. Assume the following: _ _ _ MYTABLE is a table in the database that has a decimal column named COL1. There is a row in MYTABLE that has a value of 113.01 for COL1. The host variable HOSTVAR is a floating point number that represents 113.01.

To compare HOSTVAR (3) to COL1, DB2 has to convert 113.01 to a floating point value. Because floating point numbers are approximations, the following query might not yield the expected result (that is, a host variable floating point value for 113.01 might not be considered equivalent to the floating point representation of the decimal value, 113.01, that DB2 uses for the comparison). Possibly wrong: SELECT * FROM MYTABLE WHERE :HOSTVAR = COL1; The following embedded SQL statement uses a host variable, FUZZ, that contains a tolerance factor. Better: SELECT * FROM MYTABLE WHERE :HOSTVAR BETWEEN (COL1 - :FUZZ) AND (COL1 + :FUZZ); (3) The HOSTVAR cannot be issued interactively because a host variable is used. See "Accessing Data Using Host Variables and Host Structures" in topic 3.1.4 for more information

about using host variables to access data. 2.1.6.3 Using IN to Specify Values in a List You can use the IN predicate to retrieve data from each row that has a column value equal to one of several listed values. In the values list after IN, the order of the items is not important and does not affect the ordering of the result. Enclose the entire list in parentheses, and separate items by commas; the blanks are optional. SELECT DEPTNO, MGRNO FROM DSN8310.DEPT WHERE DEPTNO IN ('B01', 'C01', 'D01'); The example retrieves the department number and manager number for departments B01, C01, and D01. Using the IN predicate gives the same results as a much longer set of conditions separated by the OR keyword. For example, the WHERE clause in the SELECT statement above could be coded: WHERE DEPTNO = 'B01' OR DEPTNO = 'C01' OR DEPTNO = 'D01' However, the IN predicate saves coding time and is easier to understand. The SQL statement below finds any sex code not properly entered. SELECT EMPNO, SEX FROM DSN8310.EMP WHERE SEX NOT IN ('F', 'M'); 2.1.7 Using Concatenation Operations: CONCAT You can use

You can concatenate strings by using the CONCAT keyword. CONCAT in any string expression. For example, SELECT LASTNAME CONCAT ',' CONCAT FIRSTNME FROM DSN8310.EMP;

concatenates the last name, comma, and first name of each result row. CONCAT is the preferred keyword. See Chapter 3 of SQL Reference for more information on expressions using the concatenation operator. 2.1.8 Using Calculated Values Calculations can be performed on numeric data or datetime data. See Chapter 3 of SQL Reference for detailed information about calculations involving date, time, and timestamp data. Subtopics 2.1.8.1 Using Numeric Data 2.1.8.2 A Warning about 31-Digit Precision for Decimal Numbers 2.1.8.3 Using Datetime Data 2.1.8.1 Using Numeric Data You can retrieve calculated values, just as you display column values, for selected rows.

For example, if you write the following SQL statement: SELECT EMPNO, SALARY / 12, SALARY / 52 FROM DSN8310.EMP WHERE WORKDEPT = 'A00'; you get this result: EMPNO ====== 000010 000110 000120 200010 200120 SALARY/12 ============== 4395.83333333 3875.00000000 2437.50000000 3875.00000000 2437.50000000 SALARY/52 ============== 1014.42307692 894.23076923 562.50000000 894.23076923 562.50000000

Here a name is given to each unnamed column to help you follow the example, although the name shown (for example, SALARY/12) does not appear when the example is executed through SPUFI. If a column in a result comes directly from a column in a table, SPUFI uses the column name as a heading; if the column is the result of a calculation made by DB2, SPUFI does not give it a heading. The SELECT statement example displays the monthly and weekly salaries of employees in department A00. To retrieve the department number, employee number, salary, bonus, and commission for those employees whose combined bonus and commission is greater than $5000, write: SELECT WORKDEPT, EMPNO, SALARY, BONUS, COMM FROM DSN8310.EMP WHERE BONUS + COMM > 5000; which gives the following result: WORKDEPT ======== A00 A00 EMPNO ====== 000010 200010 SALARY ============ 52750.00 46500.00 BONUS COMM ============ ============ 1000.00 4220.00 1000.00 4220.00

2.1.8.3 Using Datetime Data If you will be using dates, you should assign datetime data types to all columns containing dates. This not only allows you to do more with your table but it can save you from problems like the following: Suppose that in creating the table YEMP (described in "How To Create a New Department Table" in topic 2.2.1.1.1), you assign data type DECIMAL(8,0) to the BIRTHDATE column and then fill it with dates of the form yyyymmdd. You then execute the following query to determine who is 27 years old or older: SELECT EMPNO, FIRSTNME, LASTNAME FROM YEMP WHERE YEAR(CURRENT DATE - BIRTHDATE) > 26; Suppose now that at the time the query is executed, one person represented

in YEMP is 27 years, 0 months, and 29 days old but is not shown in the results. What happens is this: If the data type of the column is decimal, DB2 regards BIRTHDATE as a duration, and therefore calculates CURRENT DATE - BIRTHDATE as a date. (A duration is a number representing an interval of time. See Chapter 3 of SQL Reference for more information about datetime operands and durations.) As a date, the result of the calculation (27/00/29) is not legitimate, so it is transformed into 26/12/29. Based on this erroneous transformation, DB2 then recognizes the person as 26 years old, not 27. If, however, the table is created with BIRTHDATE as a DATE column, CURRENT DATE - BIRTHDATE is a duration, and the problem is resolved. If you have existing date data that is not stored in datetime columns, you can use conversions to avoid errors. The following examples illustrate a few conversion techniques, given the existing type of data: _ For DECIMAL(8,0) values of the form yyyymmdd in column C1, use SUBSTR to isolate the pieces and then use CONCAT to reassemble them in ISO format (with hyphens): DATE(SUBSTR(DIGITS(C2),1,4)CONCAT'-' CONCAT SUBSTR(DIGITS(C2),5,2) CONCAT'-'CONCAT SUBSTR(DIGITS(C2),7,2))

2.1.9 Using Built-In Functions Two types of built-in functions are available for use with a SELECT statement: column and scalar. Subtopics 2.1.9.1 Using Column Functions 2.1.9.2 Using Scalar Functions 2.1.9 Using Built-In Functions Two types of built-in functions are available for use with a SELECT statement: column and scalar. Subtopics 2.1.9.1 Using Column Functions 2.1.9.2 Using Scalar Functions

2.1.9.1 Using Column Functions A column function produces a single value for a group of rows. You can use the SQL column functions to calculate values based on entire columns of data which can then be retrieved. The calculated values are based on selected rows only (all rows that satisfy the WHERE clause). The column functions are as follows: SUM MIN AVG MAX Returns Returns Returns Returns the the the the total value. minimum value. average value. maximum value.

COUNT

Returns the number of selected rows.

The following SQL statement calculates for department D11, the sum of employee salaries, the minimum, average, and maximum salary, and the count of employees in the department: SELECT SUM(SALARY), MIN(SALARY), AVG(SALARY), MAX(SALARY), COUNT(*) FROM DSN8310.EMP WHERE WORKDEPT = 'D11'; The following result is displayed: SUM MIN AVG MAX COUNT(*) (SALARY) (SALARY) (SALARY) (SALARY) ========= ========= =============== ========= ======== 276620.00 18270.00 25147.27272727 32250.00 11

DISTINCT can be used with the SUM, AVG, and COUNT functions. DISTINCT means that the selected function will be performed on only the unique values in a column. The specification of DISTINCT with the MAX and MIN functions has no effect on the result and is not advised. In this case SUM and AVG can only be applied to numbers. COUNT can be applied to values of any type. MIN, MAX, and

The following SQL statement counts the number of employees described in the table. SELECT COUNT(*) FROM DSN8310.EMP; This SQL statement calculates the average education level of employees in a set of departments. SELECT AVG(EDLEVEL) FROM DSN8310.EMP WHERE WORKDEPT LIKE '_0_'; The SQL statement below counts the different jobs in the DSN8310.EMP table. SELECT COUNT(DISTINCT JOB) FROM DSN8310.EMP; 2.1.9.2 Using Scalar Functions A scalar function also produces a single value, but unlike a column function, a scalar function's argument is a single value. If the scalar function has several arguments, each argument results in a single value. The SQL statement below returns the year each employee in a particular department was hired: SELECT YEAR(HIREDATE) FROM DSN8310.EMP WHERE WORKDEPT = 'A00'; gives this result:

YEAR(HIREDATE) ============== 1972 1965 1965 1958 1963 The scalar function YEAR produces a single scalar value for each row of DSN8310.EMP that satisfies the search condition. In this example, there are three rows that satisfy the search condition, so YEAR is applied three times resulting in three scalar values. Table 3 shows the scalar functions that can be used. For complete details on using these functions see Chapter 4 of SQL Reference. +------------------------------------------------------------------------+ ¦ Table 3. Scalar Functions ¦ +------------------------------------------------------------------------¦ ¦ Scalar ¦ Returns... ¦ Example ¦ ¦ Function ¦ ¦ ¦ +-------------+-----------------------------+----------------------------¦ ¦ CHAR ¦ a string representation of ¦ CHAR(HIREDATE) ¦ ¦ ¦ its first argument. ¦ ¦ +-------------+-----------------------------+----------------------------¦ ¦ DATE ¦ a date derived from its ¦ DATE('1989-03-02') ¦ ¦ ¦ argument. ¦ ¦ +-------------+-----------------------------+----------------------------¦ ¦ DAY ¦ the day part of its ¦ DAY(DATE1 - DATE2) ¦ ¦ ¦ argument. ¦ ¦ +-------------+-----------------------------+----------------------------¦ ¦ DAYS ¦ an integer representation ¦ DAYS('1990-01-08') ¦ ¦ ¦ of its argument. ¦ DAYS(HIREDATE) + 1 ¦ +-------------+-----------------------------+----------------------------¦ ¦ DECIMAL ¦ a decimal representation of ¦ DECIMAL(AVG(SALARY), 8,2) ¦ ¦ ¦ a numeric value. ¦ ¦ +-------------+-----------------------------+----------------------------¦ ¦ DIGITS ¦ a character string ¦ DIGITS(COLUMNX) ¦ ¦ ¦ representation of its ¦ ¦ ¦ ¦ argument. ¦ ¦ +-------------+-----------------------------+----------------------------¦ ¦ FLOAT ¦ floating-point ¦ FLOAT(SALARY)/COMM ¦ ¦ ¦ representation of its ¦ ¦ ¦ ¦ argument. ¦ ¦ +-------------+-----------------------------+----------------------------¦ ¦ HEX ¦ a hexadecimal ¦ HEX(BCHARCOL) ¦ ¦ ¦ representation of its ¦ ¦ ¦ ¦ argument. ¦ ¦ +-------------+-----------------------------+----------------------------¦ ¦ HOUR ¦ the hour part of its ¦ HOUR(TIMECOL) > 12 ¦ ¦ ¦ argument. ¦ ¦ +-------------+-----------------------------+----------------------------¦ ¦ INTEGER ¦ an integer representation ¦ INTEGER(AVG(SALARY)+.5) ¦ ¦ ¦ of its argument. ¦ ¦ +-------------+-----------------------------+----------------------------¦ ¦ LENGTH ¦ the length of its argument. ¦ LENGTH(ADDRESS) ¦ +-------------+-----------------------------+----------------------------¦ ¦ MICROSECOND ¦ the microsecond part of its ¦ MICROSECOND(TSTMPCOL) <> 0 ¦ ¦ ¦ argument. ¦ ¦ +-------------+-----------------------------+----------------------------¦ ¦ MINUTE ¦ the minute part of its ¦ MINUTE(TIMECOL) = 0 ¦ ¦ ¦ argument. ¦ ¦

+-------------+-----------------------------+----------------------------¦ ¦ MONTH ¦ the month part of its ¦ MONTH(BIRTHDATE) = 5 ¦ ¦ ¦ argument. ¦ ¦ +-------------+-----------------------------+----------------------------¦ ¦ SECOND ¦ the seconds part of its ¦ SECOND(RECEIVED) ¦ ¦ ¦ argument. ¦ ¦ +-------------+-----------------------------+----------------------------¦ ¦ SUBSTR ¦ a substring of a string. ¦ SUBSTR(FIRSTNME,2,3) ¦ +-------------+-----------------------------+----------------------------¦ ¦ TIME ¦ a time derived from its ¦ TIME(TSTMPCOL) < ¦ ¦ ¦ argument. ¦ '13:00:00' ¦ +-------------+-----------------------------+----------------------------¦ ¦ TIMESTAMP ¦ a timestamp derived from ¦ TIMESTAMP(DATECOL, ¦ ¦ ¦ its argument or arguments. ¦ TIMECOL) ¦ +-------------+-----------------------------+----------------------------¦ ¦ VALUE ¦ the first argument that is ¦ VALUE(SMLLINT1,100) + ¦ ¦ ¦ not null. ¦ SMLLINT2 > 1000 ¦ +-------------+-----------------------------+----------------------------¦ ¦ VARGRAPHIC ¦ a graphic string from its ¦ VARGRAPHIC (:MIXEDSTRING) ¦ ¦ ¦ argument. ¦ ¦ +-------------+-----------------------------+----------------------------¦ ¦ YEAR ¦ the year part of its ¦ YEAR(BIRTHDATE) = 1956 ¦ ¦ ¦ argument. ¦ ¦ +------------------------------------------------------------------------+ Subtopics 2.1.9.2.1 Examples 2.1.9.2.2 Nesting Column and Scalar Functions 2.1.10 Putting the Rows in Order: ORDER BY ORDER BY lets you specify the order in which rows are retrieved. Subtopics 2.1.10.1 Specifying the Column Names 2.1.10.2 Specifying the Column Numbers

2.1.10.1 Specifying the Column Names The order of the selected rows is based on the column identified in the ORDER BY clause; this column is the ordering column. To specify that the result will be retrieved in ascending order by the values in the HIREDATE column, state: SELECT EMPNO, LASTNAME, HIREDATE FROM DSN8310.EMP WHERE WORKDEPT = 'A00' ORDER BY HIREDATE ASC; This is the result: EMPNO ====== 000110 000120 000010 200010 200120 LASTNAME =============== LUCCHESI O'CONNELL HAAS HEMMINGER ORLANDO HIREDATE ========== 1958-05-16 1963-12-05 1965-01-01 1965-01-01 1972-05-05

The example retrieves data showing the seniority of employees. Rows are shown in ascending order, based on each row's HIREDATE column value. ASC is the default sorting order. To put the rows in descending order, specify DESC. For example, to retrieve the department numbers, last names, and employee numbers of female employees in descending order of department numbers, use the following SQL statement: SELECT WORKDEPT, LASTNAME, EMPNO FROM DSN8310.EMP WHERE SEX = 'F' ORDER BY WORKDEPT DESC; It gives you this result: WORKDEPT ======== E21 E11 E11 E11 E11 E11 D21 D21 D21 D11 D11 D11 D11 C01 C01 C01 C01 A00 A00 LASTNAME =============== WONG HENDERSON SCHNEIDER SETRIGHT SCHWARTZ SPRINGER PULASKI JOHNSON PEREZ PIANKA SCOUTTEN LUTZ JOHN KWAN QUINTANA NICHOLLS NATZ HAAS HEMMINGER EMPNO ====== 200330 000090 000280 000310 200280 200310 000070 000260 000270 000160 000180 000220 200220 000030 000130 000140 200140 000010 200010

Rows are sorted in the EBCDIC collating sequence. To order the rows by more than one column's value, use more than one column name in the ORDER BY clause. When several rows have the same first ordering column value, those rows are in an order based on the second column identified in the ORDER BY clause, and then on the third ordering column, and so on. For example, there is a difference between the results of the following two SELECT statements. The first one orders selected rows by job and next by education level. The second SELECT statement orders selected rows by education level and next by job. 1) This SQL statement: SELECT JOB, EDLEVEL, LASTNAME FROM DSN8310.EMP WHERE WORKDEPT = 'E21' ORDER BY JOB, EDLEVEL; gives this result: JOB ======== FIELDREP EDLEVEL ======= 14 LASTNAME =============== LEE

FIELDREP FIELDREP FIELDREP FIELDREP MANAGER

14 16 16 16 14

WONG GOUNOT ALONZO MEHTA SPENSER

2) This SQL statement: SELECT JOB, EDLEVEL, LASTNAME FROM DSN8310.EMP WHERE WORKDEPT = 'E21' ORDER BY EDLEVEL, JOB; gives this result: JOB ======== FIELDREP FIELDREP MANAGER FIELDREP FIELDREP FIELDREP EDLEVEL ======= 14 14 14 16 16 16 LASTNAME =============== LEE WONG SPENSER MEHTA GOUNOT ALONZO

When a null value is encountered, its value is treated as if it were higher than all other values. Therefore, a null value appears last in an ascending sort and first in a descending sort. A field procedure can also be used to change the normal collating sequence. See SQL Reference and Administration Guide for more detailed information about sorting (string comparisons) and field procedures. All columns identified in the ORDER BY clause must be identified in the SELECT clause. For example, the following SQL statement orders the selected information first by department, next by job, and lastly by date of hire. SELECT LASTNAME, WORKDEPT, JOB, HIREDATE FROM DSN8310.EMP ORDER BY WORKDEPT, JOB, HIREDATE;

2.1.10.2 Specifying the Column Numbers A column derived from a function or an expression is an unnamed column and every column in the result table of a UNION ALL statement is an unnamed column. To order the rows of a result table by an unnamed column, use its numerical position in the series of columns listed in the SELECT clause. For example, ORDER BY 3 orders the rows by the third column of the result table. The following SELECT statement example calculates the length of service of every employee in Department E21. The results are put in descending order by length of service. The ORDER BY clause says, "Order the results by the values in the third column of results, in descending order." SELECT EMPNO, LASTNAME, (CURRENT DATE - HIREDATE) FROM DSN8310.EMP WHERE WORKDEPT = 'E21' ORDER BY 3 DESC; gives this result:

EMPNO ====== 200340 000340 000320 000330 200330 000100

LASTNAME =============== ALONZO GOUNOT MEHTA LEE WONG SPENSER

======== 450830 450830 270628 161110 161110 120715

You can use the column number even if the ordering column has a name. The following SQL statement lists names and phone numbers of all employees in ascending order by LASTNAME. SELECT LASTNAME, PHONENO FROM DSN8310.EMP ORDER BY 1;

2.1.11 Eliminating Duplicate Rows:

DISTINCT Each row

The DISTINCT keyword removes duplicate rows from your result. contains unique data.

The following SELECT statement lists the department numbers of the administrating departments: SELECT DISTINCT ADMRDEPT FROM DSN8310.DEPT; which produces the following result: ADMRDEPT ======== A00 D01 E01 Compare the result of the previous example with this one: SELECT ADMRDEPT FROM DSN8310.DEPT; which gives this result: ADMRDEPT ======== A00 A00 A00 A00 A00 D01 D01 E01 E01 E01 E01 E01 E01 E01

When the DISTINCT keyword is omitted, the ADMRDEPT column value of each selected row is returned, even though the result includes several duplicate rows. 2.1.12 Summarizing Group Values: GROUP BY

Use GROUP BY to specify the application of a column function to each group of column-name values. Except for the grouping columns (the columns named in the GROUP BY clause), any other column value that is selected must be specified as an operand of one of the column functions. The following SQL statement lists, for each department, the lowest and highest education level within that department. SELECT WORKDEPT, MIN(EDLEVEL), MAX(EDLEVEL) FROM DSN8310.EMP GROUP BY WORKDEPT; You can use ORDER BY to specify the order in which rows are retrieved. GROUP BY accumulates column values from the selected rows by group, but does not order the groups. The following SQL statement calculates the average salary for each department: SELECT WORKDEPT, AVG(SALARY) FROM DSN8310.EMP GROUP BY WORKDEPT ORDER BY WORKDEPT; The following result is displayed: WORKDEPT ======== A00 B01 C01 D11 D21 E01 E11 E21 AVG(SALARY) =============== 40850.00000000 41250.00000000 29722.50000000 25147.27272727 25668.57142857 40175.00000000 21020.00000000 24086.66666666

The average salary for each department is calculated. The GROUP BY clause in this example specifies that you want DB2 to apply a function (AVG) to each group of column values (SALARY) and return one row for each group of department numbers (WORKDEPT). WORKDEPT can be selected without a column function because its value determines the group (that is, every member of each group has the same WORKDEPT value). You can also specify that you want the rows grouped by more than one column. For example, you can find the average salary for men and women in departments A00 and C01. The following SQL statement: SELECT WORKDEPT, SEX, AVG(SALARY) FROM DSN8310.EMP WHERE WORKDEPT IN ('A00', 'C01') GROUP BY WORKDEPT, SEX;

gives this result: WORKDEPT ======== A00 A00 C01 SEX === F M F AVG(SALARY) =========== 49625.00000000 35000.00000000 29722.50000000

The rows are grouped first by department number and next (within each department) by sex before DB2 derives the average SALARY value for each group. If there are null values in the column you specify in the GROUP BY clause, DB2 considers those values equal and returns a single-row result summarizing the data in those rows with null values. When it is used, the GROUP BY clause follows the FROM clause and any WHERE clause, and precedes the ORDER BY clause. The SQL statement below lists, for each job, the number of employees with that job and their average salary. SELECT JOB, COUNT(*), AVG(SALARY) FROM DSN8310.EMP GROUP BY JOB ORDER BY JOB; For static SQL, GROUP BY can be used only in cursor declarations. Using GROUP BY in any other way (SELECT INTO statement, for example) results in an error. See "Chapter 3-2. Using a Cursor to Retrieve a Set of Rows" in topic 3.2 for more information on cursors.

2.1.13 Selecting Groups Subject to Conditions:

HAVING

Use HAVING to specify a condition that each group to be retrieved must satisfy. The HAVING clause acts like a WHERE clause for groups, and can contain the same kind of search conditions you can specify in a WHERE clause. The search condition in the HAVING clause tests properties of each group rather than properties of individual rows in the group. This SQL statement: SELECT WORKDEPT, AVG(SALARY) FROM DSN8310.EMP GROUP BY WORKDEPT HAVING COUNT(*) > 1 ORDER BY WORKDEPT; gives this result: WORKDEPT ======== A00 C01 D11 D21 E11 E21 AVG(SALARY) =============== 40850.00000000 29722.50000000 25147.27272727 25668.57142857 21020.00000000 24086.66666666

Compare the preceding example with the second example shown in

"Summarizing Group Values: GROUP BY" in topic 2.1.12. The HAVING COUNT(*) > 1 clause ensures that only departments with more than one member are displayed. (In this case, departments B01 and E01 are not displayed.) The HAVING clause tests a property of the group. To specify that you want the average salary and minimum education level of women in each department in which all female employees have an education level greater than or equal to 16, you can use the HAVING clause. Assuming you are only interested in departments A00 and D11, the following SQL statement tests the group property, MIN(EDLEVEL): SELECT WORKDEPT, AVG(SALARY), MIN(EDLEVEL) FROM DSN8310.EMP WHERE SEX = 'F' AND WORKDEPT IN ('A00', 'D11') GROUP BY WORKDEPT HAVING MIN(EDLEVEL) >= 16; The SQL statement above gives this result: WORKDEPT ======== A00 D11 AVG(SALARY) =============== 49625.00000000 25817.50000000 MIN(EDLEVEL) ============ 18 17

When GROUP BY and HAVING are both specified, the HAVING clause must follow the GROUP BY clause. A function in a HAVING clause can include DISTINCT if you have not used DISTINCT anywhere else in the same SELECT statement. You can also use multiple predicates in a HAVING clause by connecting them with AND and OR, and you can use HAVING NOT for any predicate of a search condition. 2.1.14 Selecting From More Than One Table (Joining Tables) Data from two or more tables can be combined or joined. need to do the following: 1. 2. To join data, you

In the FROM clause, identify the names of the tables to be joined. In the WHERE clause, specify a search condition for the join.

The search condition for the join is the link between the tables that restricts the selection of rows. For example, the search condition could identify two columns that must be equal (one from each of the tables being joined) in order for the rows to be joined and included in the result. Each row of the result table contains data that has been joined from both tables (for rows that satisfy the search condition). Each column of the result table contains data from one, but not both, of the tables. If you do not specify a search condition in the WHERE clause, the result table contains all possible combinations of rows for the tables identified in the FROM clause. If this happens, the number of rows in the result table is equal to the product of the number of rows in each table specified. Subtopics 2.1.14.1 Example: Joining Two Tables 2.1.14.2 Example: Joining a Table to Itself 2.1.14.3 Example: An Outer Join Using UNION

2.1.14.1 Example:

Joining Two Tables

The SELECT statement example below retrieves all manager department numbers, managers last and first names, and department names from table DSN8310.DEPT and DSN8310.EMP and lists them in an order based on department number. This SQL statement: SELECT DEPTNO, LASTNAME, FIRSTNME, DEPTNAME FROM DSN8310.DEPT, DSN8310.EMP WHERE MGRNO = EMPNO ORDER BY DEPTNO; gives this result: DEPTNO ====== A00 B01 C01 D11 D21 E01 E11 E21 LASTNAME ========= HAAS THOMPSON KWAN STERN PULASKI GEYER HENDERSON SPENSER FIRSTNME ========= CHRISTINE MICHAEL SALLY IRVING EVA JOHN EILEEN THEODORE DEPTNAME ==================================== SPIFFY COMPUTER SERVICES DIV. PLANNING INFORMATION CENTER MANUFACTURING SYSTEMS ADMINISTRATION SYSTEMS SUPPORT SERVICES OPERATIONS SOFTWARE SUPPORT

The SELECT statement example displays data retrieved from two tables: _ _ DEPTNO and DEPTNAME come from DSN8310.DEPT. LASTNAME and FIRSTNME come from DSN8310.EMP.

The WHERE clause makes the connection between the two tables. Column values are selected only from those rows where MGRNO (in DSN8310.DEPT) equals EMPNO (in DSN8310.EMP). The following SQL statement lists (in descending order by education level) education level, first name, last name, and department name for each employee whose education level is greater than 18. SELECT EDLEVEL, FIRSTNME, LASTNAME, DEPTNAME FROM DSN8310.EMP, DSN8310.DEPT WHERE (WORKDEPT = DEPTNO) AND (EDLEVEL > 18) ORDER BY EDLEVEL DESC;

2.1.14.2 Example:

Joining a Table to Itself

The following example joins table DSN8310.PROJ to itself and returns the number and name of each "major" project followed by the number and name of the project that is part of it. In this example, A indicates the first instance of table DSN8310.PROJ and B indicates a second instance of this table. The join condition is such that the value in column PROJNO in

table DSN8310.PROJ A must be equal to a value in column MAJPROJ in table DSN8310.PROJ B. This SQL statement: SELECT A.PROJNO, A.PROJNAME, B.PROJNO, B.PROJNAME FROM DSN8310.PROJ A, DSN8310.PROJ B WHERE A.PROJNO = B.MAJPROJ; gives this result: PROJNO ====== AD3100 AD3110 AD3110 . . . OP2010 PROJNAME ======================== ADMIN SERVICES GENERAL AD SYSTEMS GENERAL AD SYSTEMS PROJNO ======= AD3110 AD3111 AD3112 PROJNAME ======================== GENERAL AD SYSTEMS PAYROLL PROGRAMMING PERSONNEL PROGRAMMG

SYSTEMS SUPPORT

OP2013

DB/DC SUPPORT

2.1.14.3 Example:

An Outer Join Using UNION This

The query in the following example is often called an outer join. query returns rows for the following: _ _ _ All employees assigned to departments All employees that are not assigned to departments All departments that do not have employees assigned to them. SELECT DEPTNO, LASTNAME, FIRSTNME, DEPTNAME FROM DSN8310.DEPT, DSN8310.EMP WHERE MGRNO = EMPNO UNION ALL SELECT DEPTNO, '*' , '*' , DEPTNAME FROM DSN8310.DEPT A WHERE NOT EXISTS (SELECT * FROM DSN8310.EMP WHERE EMPNO = A.MGRNO) UNION ALL SELECT '*' , LASTNAME, FIRSTNME, '*' FROM DSN8310.EMP B WHERE NOT EXISTS (SELECT * FROM DSN8310.DEPT WHERE MGRNO = B.EMPNO);

-- first condition

-- second condition

-- third condition

2.1.15 Specifying UNION Using the UNION keyword, you can combine two or more SELECT statements to form a single result table. When DB2 encounters the UNION keyword, it processes each SELECT statement to form an interim result table, and then combines the interim result table of each statement. You use UNION to merge lists of values from two or more tables. You can use any of the clauses and techniques you have learned so far when coding SELECT statements, including ORDER BY. UNION is often used to eliminate duplicates when merging lists of values obtained from several tables. For example, you can obtain a combined list

of employee numbers that includes both of the following: _ _ People in department D11 People whose assignments include projects MA2112, MA2113, and AD3111.

For example, this SQL statement: SELECT EMPNO FROM DSN8310.EMP WHERE WORKDEPT = 'D11' UNION SELECT EMPNO FROM DSN8310.EMPPROJACT WHERE PROJNO = 'MA2112' OR PROJNO = 'MA2113' OR PROJNO = 'AD3111' ORDER BY 1; gives the following combined result table containing values in ascending order with no duplicates: ====== 000060 000150 000160 000170 000180 000190 000200 000210 000220 000230 000240 200170 200220 Any ORDER BY clause must appear after the last SELECT statement that is part of the union. In this example, the sequence of the results is based on the first column of the result table. ORDER BY specifies the order of the final result table. To specify the columns by which DB2 is to order the results, use numbers (in a union, you cannot use column names for this). The number refers to the position of the column in the result table. To identify which SELECT statement each row is from, a constant can be included at the end of the select list of each SELECT statement in the union. When DB2 returns your results, the last column contains the constant for the SELECT statement that was the source of that row. For example, you can write: SELECT A, B, 'A1' ... UNION SELECT X, Y, 'B2' When a row is returned, it includes a value (either A1 or B2) to indicate the source of the row's values.

2.1.16 Specifying UNION ALL

If you want to keep duplicates in the result of a UNION, specify the optional keyword ALL after the UNION keyword. This SQL statement: SELECT EMPNO FROM DSN8310.EMP WHERE WORKDEPT = 'D11' UNION ALL SELECT EMPNO FROM DSN8310.EMPPROJACT WHERE PROJNO = 'MA2112' OR PROJNO = 'MA2113' OR PROJNO = 'AD3111' ORDER BY 1; gives this result: ====== 000060 000150 000150 000150 000160 000160 000170 000170 000170 000170 000180 000180 000190 000190 000190 000200 000210 000210 000210 000220 000230 000230 000230 000230 000230 000240 000240 200170 200220

2.1.17 Retrieving Data Using System-Directed Access System-directed access allows one DB2 to execute statements at another DB2. If you are using DB2's system-directed access, the statements you compose for remote objects differ from those you compose for local objects in the way they identify tables and views. For remote objects, you must use either three-part table and view names or aliases. Subtopics 2.1.17.1 Using Three-Part Table and View Names

2.1.17.2 Using Aliases 2.1.17.3 Creating Aliases

2.1.17.1 Using Three-Part Table and View Names A three-part table or view name consists of three identifiers separated by periods: _ _ _ The first identifier is the location name for the object. The second identifier is the owning authorization ID. The third identifier is the actual table name.

For example, the name DALLAS.DSN8310.EMP could represent a table at the DALLAS location. The owning authorization ID is DSN8310., and the table name is EMP. The location name could be the name of your local subsystem, instead of a remote location. Suppose that you want the name, employee number, and department ID of every employee whose last name ends in "son" in table DSN8310.EMP at location DALLAS. If you have the appropriate authority, you could run the following query: SELECT LASTNAME, MIDINIT, FIRSTNME, EMPNO, WORKDEPT FROM DALLAS.DSN8310.EMP WHERE LASTNAME LIKE '%SON'; 2.1.17.2 Using Aliases An alias, like a synonym, is a DB2 object that represents a table or a view. Unlike a synonym, an alias can represent remote tables and views, and it can be used by anyone, not just its creator. In addition, you do not need DB2 authority to use it. However, you must have authority to use the table or view that it represents. A reference to an alias could be a one-, two-, or three-part name. The rules are basically the same as those used to refer to a table or a view: _ A one-part name refers to a local alias. If the statement being executed is dynamic, the owner of the alias is your current SQL authorization ID. Otherwise, it is the value specified on the QUALIFIER bind option. If a value is not specified on the QUALIFIER bind option, then the owner of your package or plan is the qualifier of the alias. Example: A reference to EMP in an interactively executed query could refer to the alias SMITH.EMP if your current SQL authorization ID is SMITH. _ A two-part name also refers to a local alias. As is true for a table or view, the first qualifier identifies the owner. Example: JONES.NEWTAB could refer to an alias named NEWTAB and owned by JONES. _ A three-part name could refer to either a local or a remote alias. As is true for a table or view, the first qualifier specifies the location, and the second qualifier identifies the owner. If the alias is remote, it must represent a table or view at its own location. The alias for a remote object is resolved at bind time, and existence

checking is performed at execution time. Example: A statement issued at the SAN_FRANCISCO subsystem refers to an alias at DALLAS. The alias referred to must represent a table or view at DALLAS and nowhere else. Assume now that the alias SMITH.DALEMP has been defined at your local subsystem for the table DALLAS.DSN8310.EMP. You could then substitute the alias for this table name in the previous query. The result would look like this: SELECT LASTNAME, MIDINIT, FIRSTNME, EMPNO, WORKDEPT FROM SMITH.DALEMP WHERE LASTNAME LIKE '%SON'; An advantage to using a locally defined alias is that the SQL statements in which it appears need not be changed if the table or view for the alias is either moved to another location or renamed. To make these statements valid, drop the original alias and create it again, and, for imbedded SQL, rebind the program in which it appears 2.1.17.3 Creating Aliases To create aliases, you need either SYSADM authority or the CREATEALIAS privilege. If you do not have that authorization, you must use aliases created by others. With SYSADM authority, you can create aliases to be owned by others. For example, you could create the alias appearing in the previous example, with the statement CREATE ALIAS SMITH.DALEMP FOR DALLAS.DSN8310.EMP; For more on aliases and their creation, see the description of CREATE ALIAS in SQL Reference. 2.2 Chapter 2-2. Creating Tables and Modifying Data This chapter summarizes these features: _ _ _ "Creating Your Own Tables: CREATE TABLE" in topic 2.2.1 "Modifying DB2 Data: INSERT, UPDATE, and DELETE" in topic 2.2.2 "Dropping Tables: DROP" in topic 2.2.3.

See SQL Reference and Section 4 (Volume 2) of Administration Guide for more information about creating tables and modifying data. Subtopics 2.2.1 Creating Your Own Tables: CREATE TABLE 2.2.2 Modifying DB2 Data: INSERT, UPDATE, and DELETE 2.2.3 Dropping Tables: DROP 2.2.1 Creating Your Own Tables: CREATE TABLE The following SQL

Use the CREATE TABLE statement to create a table. statement creates a table named PRODUCT:

CREATE TABLE PRODUCT (SERIAL CHAR(8) NOT NULL, DESCRIPTION VARCHAR(60) NOT NULL WITH DEFAULT, MFGCOST DECIMAL(8,2),

MFGDEPT MARKUP SALESDEPT CURDATE

CHAR(3), SMALLINT, CHAR(3), DATE

NOT NULL WITH DEFAULT);

The elements of the CREATE statement are: _ _ CREATE TABLE, which names the table PRODUCT. A list of the columns that make up the table. specify: The column's name (for example, SERIAL). The data type and length attribute (for example, CHAR(8)). further information about data types, see "Data Types" in topic 2.1.2. For For each column,

-

NOT NULL, when the column cannot contain null values and does not have a default value. NOT NULL WITH DEFAULT, when the column cannot contain null values but does have a default value, as follows: For numeric fields, zero is the default value. For fixed-length strings, blank is the default value. For variable-length strings, the empty string (string of zero-length) is the default value. For datetime fields, the current value of the associated special register is the default value.

You must separate each column description from the next with a comma and enclose the entire list of column descriptions in parentheses. Subtopics 2.2.1.1 Creating 2.2.1.2 Creating 2.2.1.3 Defining 2.2.1.4 Changing Work Tables Tables with Referential Constraints a View Data through a View

2.2.1.1 Creating Work Tables Before executing sample SQL statements that insert, update, and delete rows, you probably want to create work tables (duplicates of the DSN8310.EMP and DSN8310.DEPT tables) that you can practice with so the original sample tables remain intact. This section shows how to create two work tables and how to fill a work table with the contents of another table. Each example shown in this chapter assumes you are logged on using your own authorization ID. The authorization ID qualifies the name of each object you create. For example, if your authorization ID is SMITH, and you create table YDEPT, the name of the table is SMITH.YDEPT. If you want to access table DSN8310.DEPT, you must refer to it by its complete name.

If you want to access your own table YDEPT, you need only to refer to it as "YDEPT". Subtopics 2.2.1.1.1 How To Create a New Department Table 2.2.1.1.2 How to Create a New Employee Table

2.2.1.1.1 How To Create a New Department Table Use the following statements to create a new department table called YDEPT, modeled after an existing table called DSN8310.DEPT, and an index for YDEPT: CREATE TABLE YDEPT LIKE DSN8310.DEPT; CREATE UNIQUE INDEX YDEPTX ON YDEPT (DEPTNO); You must use two statements to create YDEPT and its index as shown above. If you want DEPTNO to be a primary key as in the sample table, you must explicitly define the key. Use an ALTER TABLE statement: ALTER TABLE YDEPT PRIMARY KEY(DEPTNO); An INSERT statement is used with a SELECT clause to copy rows from one table to another. The following statement fills the table: INSERT INTO YDEPT SELECT * FROM DSN8310.DEPT; The INSERT statement is explained in "Modifying DB2 Data: and DELETE" in topic 2.2.2. INSERT, UPDATE,

2.2.1.1.2 How to Create a New Employee Table You can use the following statements to create and fill a new employee table called YEMP. CREATE TABLE YEMP (EMPNO CHAR(6) FIRSTNME VARCHAR(12) MIDINIT CHAR(1) LASTNAME VARCHAR(15) WORKDEPT CHAR(3) PHONENO CHAR(4) HIREDATE DATE JOB CHAR(8) EDLEVEL SMALLINT SEX CHAR(1) BIRTHDATE DATE SALARY DECIMAL(9, 2) BONUS DECIMAL(9, 2) COMM DECIMAL(9, 2) PRIMARY KEY(EMPNO), NOT NOT NOT NOT NULL, NULL, NULL, NULL, , , , , , , , , , ,

FOREIGN KEY RED (WORKDEPT) REFERENCES YDEPT ON DELETE SET NULL); This statement also creates a referential constraint between the foreign key in YEMP (WORKDEPT) and the primary key in YDEPT (DEPTNO). Now, create an index with the following statement: CREATE UNIQUE INDEX YEMPX ON YEMP (EMPNO); The following statement fills the table: INSERT INTO YEMP SELECT * FROM DSN8310.EMP; 2.2.1.2 Creating Tables with Referential Constraints The CREATE TABLE statement can create tables with primary keys or foreign keys in order to establish referential constraints. You can define a single primary key composed of specific columns. (These columns cannot allow nulls.) However, the definition of the table is incomplete until its primary index is created. The primary index is a unique index that matches the primary key and enforces the uniqueness of the primary key. When you specify a foreign key, a referential constraint is defined with a delete rule. Delete rules are described in "Deleting from Tables with Referential Constraints" in topic 2.2.2.4.1. Examples of creating tables with referential constraints can be found in Appendix A, "DB2 Sample Tables" in topic APPENDIX1.1. When the referential constraint is defined, DB2 enforces the constraint on every SQL INSERT, DELETE, and UPDATE operation, and through the LOAD utility. For an example of a CREATE TABLE statement that defines both a primary key and a foreign key, see "How to Create a New Employee Table" in topic 2.2.1.1.2. 2.2.1.3 Defining a View A view does not and columns. A tables, and, in Using views can contain data; it is a stored definition of a set of rows view can present any or all of the data in one or more most cases, can be used interchangeably with tables. simplify writing SQL statements.

Use the CREATE VIEW statement to define a view and give the view a name, just as you do for a table. CREATE VIEW VDEPTM AS SELECT DEPTNO, MGRNO, LASTNAME, ADMRDEPT FROM DSN8310.DEPT, DSN8310.EMP WHERE DSN8310.EMP.EMPNO = DSN8310.DEPT.MGRNO; This view adds each department manager's name to the department data in the DSN8310.DEPT table. When a program accesses the data defined by a view, DB2 uses the view definition to return a set of rows the program can access with SQL

statements. Now that the view VDEPTM exists, you can manipulate data by means of it. To see the departments administered by department D01 and the managers of those departments, execute the following statement: SELECT DEPTNO, LASTNAME FROM VDEPTM WHERE ADMRDEPT = 'DO1'; When a view is created, the USER and CURRENT SQLID special registers may be referenced in the CREATE VIEW statement. When the view is referenced, the value used for USER or CURRENT SQLID is related to the person executing the SQL statement (SELECT, UPDATE, INSERT, or DELETE) rather than the person who created the view. In other words, a reference to a special register in the definition of a view refers to its runtime value. You can use views to limit access to certain kinds of data, such as salary information. Views can also be used to do the following: _ Make a subset of a table's data available to an application. For example, a view based on the employee table might contain rows for a particular department only. Combine data from two or more tables and make the combined data available to an application. By using a SELECT statement that matches values in one table with those in another table, you can create a view that presents data from both tables. However, data defined by this type of view can only be selected. You cannot update, delete, or insert data into a view that joins two tables. Perform functions or operations on data in a table, and make the resulting data available to an application. For example, the resulting data computed by DB2 can be: The sum of the values in a column The maximum value in a column The average of the values in a column The length of a value in a column The value in a column converted to another data type The result of an arithmetic expression applied to one or more columns, such as (COLB + COLA)/COLC.

_

_

2.2.1.4 Changing Data through a View Some views are considered to be read-only, while others are subject update or insert restrictions. (See Chapter 6 of SQL Reference for more information about read-only views.) If a view does not have update restrictions, there are some additional things to consider: _ The owner of the plan or package that contains the program must be authorized to update, delete, or insert rows into the view. You are so authorized if you have created that view or have privileges for the table on which the view is based. Otherwise, to bind the program you have to obtain authorization through a GRANT statement. When inserting a row into a table (via a view), the row must have a value for each column of the table that does not have a default value.

_

If a column in the table on which the view is based is not specified in the view's definition, and if the column does not have a default value, you cannot insert rows into the table via the view. _ Views that can be updated are subject to the same referential constraints as the tables upon which they are defined. INSERT, UPDATE, and DELETE

2.2.2 Modifying DB2 Data:

Tables can be modified using the INSERT, UPDATE, and DELETE statements. Subtopics 2.2.2.1 Inserting a Row: INSERT 2.2.2.2 Updating Current Values: UPDATE 2.2.2.3 Updating Tables with Referential Constraints 2.2.2.4 Deleting Rows: DELETE2.2.2.1 Inserting a Row:

INSERT Using an

Use an INSERT statement to add new rows to a table or view. INSERT statement, you can do the following: _ _

Specify values for columns of a single row to be inserted in the INSERT statement. Include a SELECT statement in the INSERT statement to tell DB2 that data for the new row (or rows) is contained in another table or view. "Filling a Table from Another Table: Mass INSERT" in topic 2.2.2.1.3, explains how to use the SELECT statement within an INSERT statement to add multiple rows to a table.

In either case, for every row you insert, you must provide a value for any column that does not have a default value. You can name all columns for which you are providing values. Alternatively, you can omit the column name list; when the program is bound, DB2 inserts the name of each column of the table or view into the column name list. By naming the columns, you need not list the values based on their position in the table. When you list the column names, supply their corresponding values in the same order as the listed column names. It is a good idea to name all columns into which you are inserting values because: _ _ _ Your source statements are more self-descriptive. You can verify that you are giving the values in order. Your insert statement is independent of the table format.

For example, INSERT INTO YDEPT (DEPTNO, DEPTNAME, MGRNO, ADMRDEPT, LOCATION) VALUES ('E31', 'DOCUMENTATION', '000010', 'E01', ' '); Because LOCATION column is a 16-character field, the single blank specified for insertion is automatically padded with enough blanks to fill in the 16-character field. After inserting a new department row into your YDEPT table, you can use a SELECT statement to see what you have loaded into the table. This SQL statement:

SELECT * FROM YDEPT WHERE DEPTNO LIKE 'E%' ORDER BY DEPTNO; shows you all the new department rows that you have inserted: DEPTNO ====== E01 E11 E21 E31 DEPTNAME ==================================== SUPPORT SERVICES OPERATIONS SOFTWARE SUPPORT DOCUMENTATION MGRNO ====== 000050 000090 000100 000010 ADMRDEPT ======== A00 E01 E01 E01 LOCATION =========== -----

There are other ways to enter data into tables: _ _ You can copy one table into another, as explained in "Filling a Table from Another Table: Mass INSERT" in topic 2.2.2.1.3. You can write an application program to enter large amounts of data into a table. For details, see "Section 3. Coding SQL in Your Host Application Program" in topic 3.0. You can use the DB2 LOAD utility to enter data from other sources. See Command and Utility Reference for more information about the LOAD utility.

_

Subtopics 2.2.2.1.1 Inserting into Tables with Referential Constraints 2.2.2.1.2 Using an INSERT Statement in an Application Program 2.2.2.1.3 Filling a Table from Another Table: Mass INSERT

2.2.2.1.1 Inserting into Tables with Referential Constraints If you are inserting into a parent table: _ _ _ If the primary index does not currently exist, then define a unique index on the primary key. Do not enter duplicate values for the primary key. Do not insert a null value for any column of the primary key.

If you are inserting into a dependent table: _ Each non-null value you insert into a foreign key column must be equal to some value in the primary key (the primary key is in the parent table). If any field in the foreign key is null, the entire foreign key is considered null. If the index enforcing the primary key of the parent table has been dropped, the INSERT into either the parent table or dependent table fails.

_ _

For example, the sample application project table (PROJ) has foreign keys

on the department number (DEPTNO), referencing the department table, and the employee number (RESPEMP), referencing the employee table. Every row inserted into the project table must have a value of RESPEMP that is either equal to some value of EMPNO in the employee table or is null. The row must also have a value of DEPTNO that is equal to some value of DEPTNO in the department table. (The null value is not allowed because DEPTNO in the project table is defined as NOT NULL.)

2.2.2.1.2 Using an INSERT Statement in an Application Program If DB2 finds an error while executing the INSERT statement, it stops inserting data. Nothing is inserted into the table, and error codes are set in the SQLCODE and SQLSTATE fields of the SQLCA. If the row is inserted without error, however, the SQLERRD(3) field of the SQLCA has a value of 1. SQLERRD(3) is the third of six integer variables named SQLERRD. INSERT with subselect can insert more than one row. The number of rows is reflected in SQLERRD(3). See Appendix C of SQL Reference for more information. Examples: This statement inserts information about a new employee into the YEMP table. Since YEMP has a foreign key WORKDEPT referencing the primary key DEPTNO in YDEPT, the value being inserted for WORKDEPT (E31) must be a value of DEPTNO in YDEPT. INSERT INTO YEMP VALUES ('000400', 'RUTHERFORD', 'B', 'HAYES', 'E31', '5678', '1983-01-01', 'MANAGER', 16, 'M', '1943-07-10', 24000, 500, 1900); The following statement also inserts a row into the YEMP table. However, several column values are not specified. Because the unspecified columns allow it, null values are inserted into columns not named: PHONENO, EDLEVEL, SEX, BIRTHDATE, SALARY, BONUS, COMM, and HIREDATE. Since YEMP has a foreign key WORKDEPT referencing the primary key DEPTNO in YDEPT, the value being inserted for WORKDEPT (D11) must be a value of DEPTNO in YDEPT. INSERT INTO YEMP (EMPNO, FIRSTNME, MIDINIT, LASTNAME, WORKDEPT, JOB) VALUES ('000410', 'MILLARD', 'K', 'FILLMORE', 'D11', 'MANAGER'); 2.2.2.1.3 Filling a Table from Another Table: Mass INSERT

Use a SELECT statement within an INSERT statement to select rows from one table to be inserted into another table. This SQL statement is used to create a table named TELE: CREATE TABLE TELE (NAME2 VARCHAR(15) NAME1 VARCHAR(12) PHONE CHAR(4)); NOT NULL, NOT NULL,

This statement copies data from DSN8310.EMP into the newly created table: INSERT INTO TELE SELECT LASTNAME, FIRSTNME, PHONENO FROM DSN8310.EMP WHERE WORKDEPT = 'D21';

The two previous statements create and fill a table, TELE, that looks like this: NAME2 =============== PULASKI JEFFERSON MARINO SMITH JOHNSON PEREZ MONTEVERDE NAME1 ============ EVA JAMES SALVATORE DANIEL SYBIL MARIA ROBERT PHONE ===== 7831 2094 3780 0961 8953 9001 3780

The CREATE TABLE statement example creates a table which, at first, is empty. The table has columns for last names, first names, and phone numbers, but does not have any rows. The INSERT statement fills the newly created table with data selected from the DSN8310.EMP table: the names and phone numbers of employees in Department D21. (The SELECT statement within the INSERT statement specifies the data you want selected from one table to be inserted into another table.) Example: The following CREATE statement creates a table that contains an employee's department name as well as the phone number. The SELECT statement within the INSERT statement fills the DLIST table with data from rows selected from two existing tables, DSN8310.DEPT and DSN8310.EMP. The example also illustrates a join of two tables. CREATE TABLE DLIST (DEPT CHAR(3) DNAME VARCHAR(36) LNAME VARCHAR(15) FNAME VARCHAR(12) INIT CHAR PHONE CHAR(4) ); NOT NULL, , NOT NULL, NOT NULL, ,

INSERT INTO DLIST SELECT DEPTNO, DEPTNAME, LASTNAME, FIRSTNME, MIDINIT, PHONENO FROM DSN8310.DEPT, DSN8310.EMP WHERE DEPTNO = WORKDEPT; 2.2.2.2 Updating Current Values: UPDATE

To change the data in a table, use the UPDATE statement. You can also use the UPDATE statement to delete a value from a row's column (without removing the row) by changing the column's value to NULL. For example, suppose an employee has been relocated. To update several items of the employee's data in the YEMP work table to reflect the move, you can execute: UPDATE YEMP SET JOB = 'MANAGER ', PHONENO ='5678' WHERE EMPNO = '000400'; The SET clause names the columns you want updated and provides the values

you want them changed to.

The value you specify can be:

A column name. Replace the column's current value with the contents of another column in the same row. A constant. Replace the column's current value with the constant.

A null value. Replace the column's current value with a null value. The column must have been defined as capable of containing a null value when the table was created or when the column was added, or an error occurs. A host variable. Replace the column's current value with the contents of the host variable.

A special register. Replace the column's current value with a special register value: _ _ _ _ _ _ _ _ _ CURRENT CURRENT CURRENT CURRENT CURRENT CURRENT CURRENT CURRENT USER DATE DEGREE PACKAGESET SERVER SQLID TIME TIMESTAMP TIMEZONE

An expression. Replace the column's current value with the value that results from an expression. Next, identify the rows to be updated: _ _ To update a single row, use a WHERE clause that locates one, and only one, row To update several rows, use a WHERE clause that locates only the rows you want to update.

If you omit the WHERE clause; DB2 updates every row in the table or view with the values you supply. If DB2 finds an error while executing your UPDATE statement (for instance, an update value that is too large for the column), it stops updating and returns error codes in the SQLCODE and SQLSTATE fields in the SQLCA. No rows in the table are changed (rows already changed, if any, are restored to their previous values). Examples: The following statement supplies a missing middle initial and changes the job for employee 000200. UPDATE YEMP SET MIDINIT = 'H', JOB = 'FIELDREP' WHERE EMPNO = '000200';

The following statement gives everyone in department D11 a $400 raise. The statement can update several rows. UPDATE YEMP SET SALARY = SALARY + 400.00 WHERE WORKDEPT = 'D11'; 2.2.2.3 Updating Tables with Referential Constraints If you are updating a parent table, you cannot modify a primary key for which dependent rows exist. (Doing so would violate referential constraints for dependent tables and leave some rows without a parent.) Also, you cannot give a primary key a null value. If you are updating a dependent table, any non-null foreign key values that you enter must match the primary key for each relationship in which the table is a dependent. For example, department numbers in the employee table depend on the department numbers in the department table; you can assign no department to an employee, but you cannot assign an employee to a department that does not exist. If an UPDATE against a table with a referential constraint fails, all changes made during the operation are rolled back. 2.2.2.4 Deleting Rows: DELETE You can use the DELETE statement to remove entire rows from a table. The DELETE statement removes zero or more rows of a table, depending on how many rows satisfy the search condition you specified in the WHERE clause. If you omit a WHERE clause from a DELETE statement, DB2 removes all the rows from the table or view you have named. The DELETE statement does not remove specific columns from the row. This DELETE statement deletes each row in the YEMP table that has an employee number 000060. DELETE FROM YEMP WHERE EMPNO = '000060'; When this statement is executed, DB2 deletes any row from the YEMP table that meets the search condition. If DB2 finds an error while executing your DELETE statement, it stops deleting data and returns error codes in the SQLCODE and SQLSTATE fields in the SQLCA. The table's data is not changed. Subtopics 2.2.2.4.1 Deleting from Tables with Referential Constraints 2.2.2.4.2 Deleting Every Row in a Table 2.2.2.4.1 Deleting from Tables with Referential Constraints If a table has a primary key and dependent tables, an attempt to delete a row must obey the delete rules specified for the table. All delete rules of all affected relationships must be satisfied in order for the delete operation to succeed. If a referential constraint is violated, the DELETE fails. Delete Rules:

DELETE RESTRICT The row can be deleted only if no other row depends on it. If a dependent row exists in the relationship, the DELETE fails. For example, you cannot delete a department from the department table if it is still responsible for some project, which is described by a dependent row in the project table. DELETE SET NULL Each nullable column of the foreign key in each dependent row is set to null. This means that a nullable column is set to null only if it is a member of a foreign key that references the row being deleted. Only the dependent rows that are immediate descendents are affected. For example, you can delete an employee from the employee table even if the employee manages some department. In that case, the value of MGRNO is set to null in the department table. DELETE CASCADE First the named rows are deleted, then the dependent rows are deleted, honoring the delete rules of their dependents. For example, you can delete a department by deleting its row in the department table; that also deletes the rows for all departments that report to it, all departments that report to them, and so forth. If all is successful, all descendent rows are either deleted or their nullable columns have been nullified depending on the delete rules. Encountering ON DELETE SET NULL ends one branch of the cascade. For example, deleting a department from the department table sets WORKDEPT (in the employee table) to null for every employee assigned to that department. Because no rows of the employee table are actually deleted, the delete does not cascade any further. If a descendent table has a delete rule of RESTRICT and a row is found such that a descendent row cannot be deleted, the DELETE fails. The number of rows deleted is returned in SQLERRD(3) in the SQLCA. This number includes only the number of rows deleted in the table specified in the DELETE statement. It does not include those rows deleted according to the CASCADE rule. Self-Referencing Tables and Cycles: Special restrictions apply if the table is a member of a cycle of relationships or is self-referencing. See Chapter 6 of SQL Reference for more information about these restrictions.

2.2.2.4.2 Deleting Every Row in a Table The DELETE statement is a powerful statement that deletes all rows of a table unless you specify a WHERE clause that limits the deletion. (With segmented table spaces, deleting all rows of a table is very fast.) For example, this statement: DELETE FROM YDEPT; deletes every row in the YDEPT table. If the statement is executed, the

table continues to exist (that is, you can insert rows into it) but it is empty. All existing views and authorizations on the table remain intact when using DELETE. If you use DROP, all views and authorizations are dropped which can invalidate plans and packages. Refer to "Dropping Tables: DROP" in topic 2.2.3 for a description of the DROP statement. 2.2.3 Dropping Tables: DROP

This SQL statement drops the YEMP table: DROP TABLE YEMP; Use the DROP TABLE statement with care: When a table is dropped, it loses its data as well as its definition. When you drop a table, all synonyms, views, indexes, and referential constraints associated with that table are also dropped. All authorities granted on the table are lost. Similarly, a view can be dropped, using the DROP VIEW statement. This does not cause a loss of data in the table on which the view was created. With the proper authorization, you can also drop a database. When a database is dropped, all tables, views, indexes, and referential constraints defined on that database are also dropped. DROP TABLE drops all constraints in which the table is a parent or dependent. When a table is dropped, all indexes on the table (including the primary index) are implicitly dropped. If a primary index is explicitly dropped, the definition of its table is changed to incomplete and an SQL warning message is issued. If a table is defined as incomplete, programs cannot use the table. Dropping a table is NOT equivalent to deleting all its rows. Instead, when you drop a table you also drop all the relationships in which the table participates, either as parent or dependent. This can affect application programs that depend on the existence of a parent table, so use DROP carefully. For more information on the DROP statement, see Chapter 6 of SQL Reference. 2.3 Chapter 2-3. Using Subqueries

This chapter presents a conceptual overview of subqueries, shows how to include subqueries in either a WHERE or a HAVING clause, and shows how to use correlated subqueries. Subtopics 2.3.1 Conceptual Overview 2.3.2 Using Subqueries 2.3.3 Using Correlated Subqueries 2.3.1 Conceptual Overview Suppose you want a list of the employee numbers, names, and commissions of all employees working on a particular project, say project number MA2111. The first part of the SELECT statement is easy to write: SELECT EMPNO, LASTNAME, COMM FROM DSN8310.EMP WHERE EMPNO

. . . But you cannot go further because the DSN8310.EMP table does not include project number data. You do not know which employees are working on project MA2111 without issuing another SELECT statement against the DSN8310.EMPPROJACT table. You can nest one SELECT statement within another to solve this problem. The inner SELECT statement is called a subquery. The SELECT statement surrounding the subquery is called the outer SELECT. SELECT EMPNO, LASTNAME, COMM FROM DSN8310.EMP WHERE EMPNO IN (SELECT EMPNO FROM DSN8310.EMPPROJACT WHERE PROJNO = 'MA2111'); To better understand what results from this SQL statement, imagine that DB2 goes through the following process: 1. DB2 evaluates the subquery to obtain a list of EMPNO values: . . . (SELECT EMPNO FROM DSN8310.EMPPROJACT WHERE PROJNO = 'MA2111'); The evaluation results in an interim result table: (from DSN8310.EMPPROJACT) +------+ ¦000200¦ +------¦ ¦000220¦ +------+ 2. The interim result table then serves as a list in the search condition of the outer SELECT. Effectively, DB2 executes this statement: SELECT EMPNO, LASTNAME, COMM FROM DSN8310.EMP WHERE EMPNO IN ('000200 ', '000220'); As a consequence, the result table looks like this: EMPNO LASTNAME COMM Fetch +------------------------------+ 1 --_ ¦ 000200 ¦ BROWN ¦ 2217 ¦ +---------+-----------+--------¦ 2 --_ ¦ 000220 ¦ LUTZ ¦ 2387 ¦ +------------------------------+ Subtopics 2.3.1.1 Correlation 2.3.1.2 Subqueries and Predicates 2.3.1.3 The Subquery Result Table

2.3.1.4 Subqueries with UPDATE, DELETE, and INSERT

2.3.1.1 Correlation The purpose of a subquery is to supply information needed to qualify a row (WHERE clause) or a group of rows (HAVING clause). This is done through the result table that the subquery produces. Conceptually, the subquery is invoked whenever a new row or group of rows must be qualified. In fact, if the subquery is the same for every row or group, it is executed only once. A case in point is the previous query. Its content is the same for every row of the table DSN8310.EMP. Subqueries like this are said to be uncorrelated. Some subqueries do vary in content from row to row or group to group. The mechanism that allows this is called correlation, and the subqueries are said to be correlated. Correlated subqueries are described on page 2.3.3. All of the information described preceding that section applies to both correlated and uncorrelated subqueries.

2.3.1.3 The Subquery Result Table A subquery must produce a one-column result table unless the EXISTS keyword is used. This means that the SELECT clause in a subquery must name a single column or contain a single expression. For example, both of the following SELECT clauses would be acceptable: SELECT AVG(SALARY) SELECT EMPNO The result table produced by a subquery can have zero or more rows. For some usages, no more than one row is allowed. 2.3.1.4 Subqueries with UPDATE, DELETE, and INSERT When you use a subquery in an UPDATE, DELETE, or INSERT statement, the subquery cannot be based on the same table as the UPDATE, DELETE, or INSERT statement. 2.3.2 Using Subqueries There are a number of ways to include a subquery in either a WHERE or HAVING clause. They are as follows: _ _ _ _ _ Basic predicate Quantified Predicates: ALL, ANY, and SOME Using the IN Keyword Using the EXISTS Keyword Correlated subqueries.

Subtopics 2.3.2.1 Basic Predicate 2.3.2.2 Quantified Predicates: ALL, ANY, and SOME 2.3.2.3 Using the IN Keyword 2.3.2.4 Using the EXISTS Keyword

2.3.2.1 Basic Predicate You can use a subquery immediately after any of the comparison operators. If you do, the subquery can return at most one value. DB2 compares that value with the value to the left of the comparison operator. For example, the following SQL statement returns the employee numbers, names, and salaries for employees whose education level is higher than the average company-wide education level. SELECT EMPNO, LASTNAME, SALARY FROM DSN8310.EMP WHERE EDLEVEL > (SELECT AVG(EDLEVEL) FROM DSN8310.EMP); 2.3.2.2 Quantified Predicates: ALL, ANY, and SOME

You can use a subquery after a comparison operator followed by the keyword ALL, ANY, or SOME. When used in this way, the subquery can return zero, one, or many values, including null values. You use ALL, ANY, and SOME in the following ways: _ Use ALL to indicate that the value you have supplied must compare in the indicated way to all the values the subquery returns. For example, suppose you use the greater-than comparison operator with ALL: . . . WHERE expression > ALL (subquery) To satisfy this WHERE clause, the greater than all the values (that returned by the subquery. If the is, no values were selected), the _ value in the expression must be is, greater than the highest value) subquery returns an empty set (that predicate is satisfied.

Use ANY or SOME to indicate that the value you have supplied must compare in the indicated way to at least one of the values the subquery returns. For example, suppose you use the greater-than comparison operator with ANY: . . . WHERE expression > ANY (subquery) To satisfy this WHERE clause, the value in the expression must be greater than at least one of the values (that is, greater than the lowest value) returned by the subquery. If what the subquery returns is empty, the condition is not satisfied.

The results when a subquery returns one or more null values could in some cases surprise you. For applicable rules, read the description of quantified predicates in Chapter 3 of SQL Reference. 2.3.2.3 Using the IN Keyword

You can use IN to say that the value in the expression must be among the values returned by the subquery. Using IN is equivalent to using "= ANY" or "= SOME." 2.3.2.4 Using the EXISTS Keyword In the subqueries presented thus far, DB2 evaluates the subquery and uses the result as part of the WHERE clause of the outer SELECT. In contrast, when you use the keyword EXISTS, DB2 simply checks whether the subquery returns one or more rows. If it does, the condition is satisfied; if it does not (if it returns no rows), the condition is not satisfied. For example: SELECT EMPNO,LASTNAME FROM DSN8310.EMP WHERE EXISTS (SELECT * FROM DSN8310.PROJ WHERE PRSTDATE > '1986-01-01'); 2.3.3 Using Correlated Subqueries In the subqueries previously described, DB2 executes the subquery once, substitutes the result of the subquery in the right side of the search condition, and evaluates the outer-level SELECT based on the value of the search condition. You can also write a subquery that DB2 has to re-evaluate when it examines a new row (WHERE clause) or group of rows (HAVING clause) as it executes the outer SELECT. This is called a correlated subquery. Subtopics 2.3.3.1 An Example of a Correlated Subquery 2.3.3.2 Using Correlated Names in References 2.3.3.3 Using Correlated Subqueries in an UPDATE Statement 2.3.3.4 Using Correlated Subqueries in a DELETE Statement In the example, the search condition holds if any project represented in the DSN8310.PROJ table has an estimated start date which is later than 1 January 1986. This example does not show the full power of EXISTS, because the result is always the same for every row examined for the outer SELECT. As a consequence, either every row appears in the results, or none appear. In a more powerful example, the subquery itself would be correlated, and would change from row to row. As shown subquery also use when the code in the example, you do not need to specify column names in of an EXISTS clause. Instead, you can code SELECT *. You the EXISTS keyword with the NOT keyword in order to select data or condition you specify does not exist; that is, you the can rows can

WHERE NOT EXISTS (SELECT ...);

2.3.3 Using Correlated Subqueries In the subqueries previously described, DB2 executes the subquery once, substitutes the result of the subquery in the right side of the search condition, and evaluates the outer-level SELECT based on the value of the search condition. You can also write a subquery that DB2 has to re-evaluate when it examines a new row (WHERE clause) or group of rows

(HAVING clause) as it executes the outer SELECT. correlated subquery.

This is called a

Subtopics 2.3.3.1 An Example of a Correlated Subquery 2.3.3.2 Using Correlated Names in References 2.3.3.3 Using Correlated Subqueries in an UPDATE Statement 2.3.3.4 Using Correlated Subqueries in a DELETE Statement 2.3.3.1 An Example of a Correlated Subquery Suppose that you want a list of all the employees whose education levels are higher than the average education levels in their respective departments. To get this information, DB2 must search the DSN8310.EMP table. For each employee in the table, DB2 needs to compare the employee's education level to the average education level for the employee's department. This is the point at which a correlated subquery differs from a noncorrelated subquery. In the earlier example on subqueries, the purpose was to compare the education level to the average of the entire company, thus looking at the entire table. With the correlated subquery, only the department which corresponds to the particular employee in question is evaluated. In the subquery, you tell DB2 to compute the average education level for the department number in the current row. A query that does this follows: SELECT EMPNO, LASTNAME, WORKDEPT, EDLEVEL FROM DSN8310.EMP X WHERE EDLEVEL > (SELECT AVG(EDLEVEL) FROM DSN8310.EMP WHERE WORKDEPT = X.WORKDEPT); A correlated subquery looks like a noncorrelated one, except for the presence of one or more correlated references. In the example, the single correlated reference is the occurrence of X.WORKDEPT in the WHERE clause of the subselect. In this clause, the qualifier X is the correlation name defined in the FROM clause of the outer SELECT statement. X is introduced as the name of one of the instances of the table DSN8310.EMP. Consider what happens when the subquery is executed for a given row of DSN8310.EMP. Before it is executed, the occurrence of X.WORKDEPT is replaced with the value of the WORKDEPT column for that row. Suppose, for example, that the row is for CHRISTINE HAAS. Her work department is A00, which is the value of WORKDEPT for this row. The subquery executed for this row is therefore: (SELECT AVG(EDLEVEL) FROM DSN8310.EMP WHERE WORKDEPT = 'A00'); Thus, for the row considered, the subquery produces the average education level of Christine's department. This is then compared in the outer statement to Christine's own education level. For some other row for which WORKDEPT has a different value, that value appears in the subquery in place of A00. For example, for the row for MICHAEL L THOMPSON, this value is B01, and the subquery for his row delivers the average education level for department B01.

The result table produced by the query has the following values: (from DSN8310.EMP) EMPNO LASTNAME WORKDEPT EDLEVEL Fetch +------------------------------------------+ 1--_ ¦ 000010 ¦ HAAS ¦ A00 ¦ 18 ¦ +---------+-----------+----------+---------¦ 2--_ ¦ 000030 ¦ KWAN ¦ C01 ¦ 20 ¦ +---------+-----------+----------+---------¦ 3--_ ¦ 000090 ¦ HENDERSON ¦ E11 ¦ 16 ¦ +---------+-----------+----------+---------¦ 4--_ ¦ 000110 ¦ LUCCHESI ¦ A00 ¦ 19 ¦ +---------+-----------+----------+---------¦ _ _ _ _ _ _ _ _ _ _ _ _ 2.3.3.2 Using Correlated Names in References A correlated reference can appear only in a search condition in a subquery. The reference should be of the form X.C, where X is a correlation name and C is the name of a column in the table that X represents. The correlation name is defined in the FROM clause of some query. This query could be the outer-level SELECT, or any of the subqueries that contain the reference. Suppose, for example, that a query contains subqueries A, B, and C, and that A contains B and B contains C. Then a correlation name used in C could be defined in B, A, or the outer SELECT. You can define a correlation name for each table name appearing in a FROM clause. Simply append the correlation name after its table name. Leave one or more blanks between a table name and its correlation name, and place a comma after the correlation name if it is followed by another table name. The following FROM clause, for example, defines the correlation names TA and TB for the tables TABLEA and TABLEB, and no correlation name for the table TABLEC. FROM TABLEA TA, TABLEC, TABLEB TB Any number of correlated references can appear in a subquery. There are no restrictions on variety. For example, one correlated name in a reference can be defined in the outer SELECT, while another can be defined in a containing subquery. 2.3.3.4 Using Correlated Subqueries in a DELETE Statement When you use a correlated subquery in a DELETE statement, the correlation name represents the row you delete. DB2 evaluates the correlated subquery once for each row in the table named in the DELETE statement to decide whether or not to delete the row. For example, suppose that a department considers a project to be completed when the combined amount of time currently spent on it is half a person's time or less. The department then deletes the rows for that project from the DSN8310.PROJ table. In the example statements that follow, PROJ and PROJACT are independent tables. DELETE FROM DSN8310.PROJ X WHERE .5 > (SELECT SUM(ACSTAFF)

FROM DSN8310.PROJACT WHERE PROJNO = X.PROJNO); To process this statement, DB2 determines for each project (represented by a row in the DSN8310.PROJ table) whether or not the combined staffing for that project is less than 0.5. If it is, DB2 deletes that row from the DSN8310.PROJ table. To continue this example, suppose a row in the DSN8310.PROJ table has been deleted. Rows related to the deleted project in the DSN8310.PROJACT table must also be deleted. To do this, use: DELETE FROM DSN8310.PROJACT X WHERE NOT EXISTS (SELECT * FROM DSN8310.PROJ WHERE PROJNO = X.PROJNO); DB2 determines, for each row in the DSN8310.PROJACT table, whether a row with the same project number exists in the DSN8310.PROJ table. If not, the DSN8310.PROJACT row is deleted. A subquery of a DELETE statement must not reference the same table from which rows are deleted. In the sample application, some departments administer other departments. Consider the following statement, which seems to delete every department that does not administer another one: DELETE FROM DSN8310.DEPT X WHERE NOT EXISTS (SELECT * FROM DSN8310.DEPT WHERE ADMRDEPT = X.DEPTNO); The result of an operation must not depend on the order in which rows of a table are accessed. If this statement could be executed, its result would depend on whether the row for any department was accessed before or after deleting the rows for the departments it administers. Hence, the operation is prohibited. The same rule extends to dependent tables involved in referential constraints. If a DELETE statement has a subquery that references a table that is involved in the DELETE operation, the last delete rule in the path to that table must be RESTRICT. For example, without referential constraints, the following statement deletes departments from the department table whose managers are not listed correctly in the employee table: DELETE FROM DSN8310.DEPT THIS WHERE NOT DEPTNO = (SELECT WORKDEPT FROM DSN8310.EMP WHERE EMPNO = THIS.MGRNO); With the referential constraints defined for the sample tables, the statement causes an error. The delete operation involves the table referred to in the subquery (DSN8310.EMP is a dependent of DSN8310.DEPT) and the last delete rule in the path to EMP is SET NULL, not RESTRICT. If the statement could be executed, its results would again depend on the order in which rows were accessed. 2.4 Chapter 2-4. Using SPUFI: Executing SQL from Your Terminal

This chapter explains how to enter and execute SQL statements at a TSO

terminal using the SPUFI (SQL processor using file input) facility. You can execute most of the interactive SQL examples shown in "Section 2. Using SQL Queries" by following the instructions provided in this chapter and using the sample tables shown in Appendix A, "DB2 Sample Tables" in topic APPENDIX1.1. The instructions assume that ISPF is available to you. statement from your terminal. This chapter also describes how to use SPUFI to perform other tasks: _ _ _ _ Display the names of tables you can use Display the names of the columns in a table Allocate a partitioned data set to use as an input data set Change the SPUFI defaults. 1. 2. 3. 4. 5. SQL Invoke SPUFI and Allocate an Input Data Set Change SPUFI Defaults (Optional) Enter SQL Statements Process SQL Statements Browse the Output Examples

Subtopics 2.4.1 Step 2.4.2 Step 2.4.3 Step 2.4.4 Step 2.4.5 Step 2.4.6 More

2.4.1 Step 1.

Invoke SPUFI and Allocate an Input Data Set

To invoke SPUFI, select SPUFI from the DB2I Primary Option Menu as shown in Figure 6. +----------------------------------------------------------------------------------+ ¦ ¦ ¦ DSNEPRI DB2I PRIMARY OPTION MENU SSID: DSN ¦ ¦ COMMAND ===> 1 ¦ ¦ ¦ ¦ Select one of the following DB2 functions and press ENTER. ¦ ¦ ¦ ¦ 1 SPUFI (Process SQL statements) ¦ ¦ 2 DCLGEN (Generate SQL and source language declarations) ¦ ¦ 3 PROGRAM PREPARATION (Prepare a DB2 application program to run) ¦ ¦ 4 PRECOMPILE (Invoke DB2 precompiler) ¦ ¦ 5 BIND/REBIND/FREE (BIND, REBIND, or FREE plans or packages) ¦ ¦ 6 RUN (RUN an SQL program) ¦ ¦ 7 DB2 COMMANDS (Issue DB2 commands) ¦ ¦ 8 UTILITIES (Invoke DB2 utilities) ¦ ¦ D DB2I DEFAULTS (Set global parameters) ¦ ¦ X EXIT (Leave DB2I) ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ PRESS: END to exit HELP for more information ¦ ¦ ¦ ¦ ¦ +----------------------------------------------------------------------------------+ Figure 6. The DB2I Primary Option Menu with Option 1 Selected The SPUFI panel is then displayed as shown in Figure 7.

Data you enter on the SPUFI panel tells DB2 how to process your input data set, and where to send the output. When the SPUFI panel is first displayed, enter the name of an input data set (where you will put SQL statements you want DB2 to execute) and an output data set (where DB2 will put the results of your queries). You can also enter new processing option defaults to specify how you want the next SPUFI processing sequence to proceed. The next time (and subsequent times) the SPUFI panel is displayed, the data entry fields on the panel will contain the values that were set on the panel previously. You can specify data set names and processing options each time the SPUFI panel displays, as needed. Values you do not change will remain in effect. +----------------------------------------------------------------------------------+ ¦ ¦ ¦ DSNESP01 SPUFI SSID: DSN ¦ ¦ ===> ¦ ¦ Enter the input data set name: (Can be sequential or partitioned) ¦ ¦ 1 DATA SET NAME..... ===> EXAMPLES(XMP1) ¦ ¦ 2 VOLUME SERIAL..... ===> (Enter if not cataloged) ¦ ¦ 3 DATA SET PASSWORD. ===> (Enter if password protected) ¦ ¦ ¦ ¦ Enter the output data set name: (Must be a sequential data set) ¦ ¦ 4 DATA SET NAME..... ===> RESULT ¦ ¦ ¦ ¦ Specify processing options: ¦ ¦ 5 CHANGE DEFAULTS... ===> Y (Y/N - Display SPUFI defaults panel?) ¦ ¦ 6 EDIT INPUT........ ===> Y (Y/N - Enter SQL statements?) ¦ ¦ 7 EXECUTE........... ===> Y (Y/N - Execute SQL statements?) ¦ ¦ 8 AUTOCOMMIT........ ===> Y (Y/N - Commit after successful run?) ¦ ¦ 9 BROWSE OUTPUT..... ===> Y (Y/N - Browse output data set?) ¦ ¦ ¦ ¦ For remote SQL processing: ¦ ¦ 10 CONNECT LOCATION ===> ¦ ¦ ¦ ¦ ¦ ¦ PRESS: ENTER to process END to exit HELP for more information ¦ ¦ ¦ +----------------------------------------------------------------------------------+ Figure 7. The SPUFI Panel Filled In Fill out the SPUFI panel as follows: 1, 2, 3 INPUT DATA SET NAME Identify the input data set in fields 1 through 3. This data set contains one or more SQL statements that you want to execute. _ _ _ Allocate this data set before you invoke SPUFI. conform to standard TSO naming conventions. The name must

The data set can be empty before you begin the session. You can then add the SQL statements by editing the data set from SPUFI. The data set can be either sequential or partitioned, but it must have the following DCB characteristics:

-

A record format (RECFM) of either F or FB A logical record length (LRECL) of either 79 or 80. Use 80 for any data set that was not created by the EXPORT command of QMF

_

Data in the data set can begin in column 1. It can extend to column 71 if the logical record length is 79, and to column 72 if the logical record length is 80. The last 8 bytes of the records are assumed to be reserved for sequence numbers.

If you use this panel a second time, the name of the data set you used previously appears. To create a new member of an existing partitioned data set, change only the member name. If you need to allocate an input data set, refer to ISPF/PDF Version 3 for MVS Guide and Reference for information on ISPF and allocating data sets. 4 OUTPUT DATA SET NAME Enter the name of a data set to receive the output of the SQL statement. The data set need not be allocated previously. The simplest choice is shown in Figure 7. In this example, RESULT was entered. SPUFI allocates a data set named userid.RESULT and sends all output to that data set. If a data set named userid.RESULT already exists, SPUFI sends DB2 output to it, replacing all existing data. Look at the processing options: 5 CHANGE DEFAULTS The SPUFI defaults need not be changed for this example. However, if you specify Y(YES) you can look at the SPUFI defaults panel. See "Step 2. Change SPUFI Defaults (Optional)" in topic 2.4.2 for more information about the values you can specify and how they affect SPUFI processing and output characteristics. 6 EDIT INPUT To edit the input data set, leave Y(YES) on line 6. You can use the ISPF editor to create a new member of the input data set and enter SQL statements in it. (To process a data set that already contains a set of SQL statements you want to execute immediately, enter N(NO). Specifying N bypasses the step described in "Step 3. Enter SQL Statements" in topic 2.4.3.) 7 EXECUTE To execute SQL statements contained in the input data set, leave Y(YES) on line 7. 8 AUTOCOMMIT To make changes to the DB2 data permanent, leave Y(YES) on line 8. Specifying Y makes SPUFI issue COMMIT if all statements execute successfully. If all statements do not execute successfully, SPUFI issues a ROLLBACK statement, and changes already made to the file (back to the last commit point) are deleted. We suggest that you read about the COMMIT and the ROLLBACK functions in "The ISOLATION Option" in topic 4.1.2.7.2 or Chapter 6 of SQL Reference. 9 BROWSE OUTPUT To look at the results of your query, leave Y(YES) on line 9. The results are saved in the output data set. You can look at them at

any time, until you delete or write over the data set. 10 CONNECT LOCATION Specify the name of the application server, if applicable, to which you want to submit SQL statements for execution. SPUFI will then issue a type 1 CONNECT statement to this application server. An installation job is added to bind SPUFI remotely. SPUFI is bound locally as a package. Subsequent processing of SQL statements in the input data set is based upon successful execution of the CONNECT statement. If the connect request fails, SQL return codes and error messages are placed in the output data set.

When you finish with the SPUFI panel, press the ENTER key. Because you specified YES on line 5 of the SPUFI panel, the next panel you see is the SPUFI Defaults panel, as shown in Figure 8. +----------------------------------------------------------------------------------+ ¦ ¦ ¦ DSNESP02 CURRENT SPUFI DEFAULTS SSID: DSN ¦ ¦ ===> ¦ ¦ Enter the following to control your SPUFI session: ¦ ¦ 1 ISOLATION LEVEL ... ===> RR (RR=Repeatable Read, CS=Cursor Stability) ¦ ¦ 2 MAX SELECT LINES .. ===> 250 (Maximum number of lines to be ¦ ¦ returned from a SELECT) ¦ ¦ Output data set characteristics: ¦ ¦ 3 RECORD LENGTH...... ===> 4092 (LRECL= logical record length) ¦ ¦ 4 BLOCKSIZE ......... ===> 4096 (Size of one block) ¦ ¦ 5 RECORD FORMAT...... ===> VB (RECFM= F, FB, FBA, V, VB, or VB) ¦ ¦ 6 DEVICE TYPE........ ===> SYSDA (Must be a DASD unit name) ¦ ¦ ¦ ¦ Output format characteristics: ¦ ¦ 7 MAX NUMERIC FIELD . ===> 33 (Maximum width for numeric field) ¦ ¦ 8 MAX CHAR FIELD .... ===> 80 (Maximum width for character field) ¦ ¦ 9 COLUMN HEADING .... ===> NAMES (NAMES, LABELS, ANY, or BOTH) ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ PRESS: ENTER to process END to exit HELP for more information ¦ ¦ ¦ +----------------------------------------------------------------------------------+ Figure 8. The SPUFI Defaults Panel 2.4.2 Step 2. Change SPUFI Defaults (Optional)

Default values are provided for each user the first time SPUFI is used. Defaults are set for all options except the DB2 subsystem name. Any changes you make to these values remain in effect until the values are changed again. Initial default values are shown in Figure 8 in topic 2.4.1. Specify values for the following options on the CURRENT SPUFI DEFAULTS panel: 1 ISOLATION LEVEL

See "The ISOLATION Option" in topic 4.1.2.7.2 for more information. 2 MAX SELECT LINES The maximum number of output lines to be returned as a result of executing a SELECT statement. To limit the number of rows retrieved, enter another maximum number. 3 RECORD LENGTH The record length must be at least 80 bytes. allows a 4092-byte record. The default value

4 BLOCKSIZE Follow normal block size selection rules. For F, the block size is equal to record length. For FB and FBA, choose a block size that is an even multiple of LRECL. For VB and VBA only, the block size must be 4 bytes larger than the block size for FB or FBA. 5 RECORD FORMAT The record format default is VB (variable-length blocked). 6 DEVICE TYPE SYSDA specifies that MVS is to select an appropriate direct access storage device. 7 MAX NUMERIC FIELD The maximum width of a numeric value column in your output. 8 MAX CHAR FIELD The maximum width of a character value column in your output. DATETIME and GRAPHIC data strings are externally represented as characters, and so they fall under the default values for character fields. 9 COLUMN HEADING You can specify NAMES, LABELS, ANY or BOTH for column headings. _ _ _ _ NAME (default) uses column names only. LABEL uses column labels. Leave the title blank if there is no label. ANY uses existing column labels or column names. BOTH creates two title lines, one with names and one with labels.

When you have specified SPUFI options, press the ENTER key to continue. SPUFI continues by processing the next processing option for which YES has been specified. If all other processing options are NO, SPUFI continues by displaying the SPUFI panel. If you press the END key, you return to the SPUFI panel, but all changes made on the SPUFI Defaults panel are lost. If you press ENTER, your changes are saved. 2.4.3 Step 3. Enter SQL Statements

Next, SPUFI lets you edit the input data set. Initially, editing consists of entering an SQL statement into the input data set. You can also edit an input data set that contains SQL statements and you can change, delete, or insert SQL statements. The ISPF Editor shows you an empty EDIT panel.

On the panel, use the ISPF EDIT program to enter SQL statements that you want to execute, as shown in Figure 9. Move the cursor to the first input line and enter the first part of an SQL statement. You can enter the rest of the SQL statement on subsequent lines, as shown in Figure 9. Line indentation and entry on several lines are not necessary, although this format is easier to read. You can put more than one SQL statement in the input data set. You can put an SQL statement on one line of the input data set or on more than one line. When the data set is processed, DB2 executes the statements one after the other. Do not put more than one SQL statement on a single line. The first one is executed, but other SQL statements on the same line are ignored. When using SPUFI, end each SQL statement with a semicolon (;). SPUFI that the statement is complete. This tells

When you have entered the SQL statements that you want, press the END PF key to save the file and to begin execution. +----------------------------------------------------------------------------------+ ¦ ¦ ¦ EDIT --------userid.EXAMPLES(XMP1) --------------------- COLUMNS 001 072 ¦ ¦ COMMAND INPUT ===> SAVE SCROLL ===> PAGE ¦ ¦ ********************************** TOP OF DATA *********************** ¦ ¦ 000100 SELECT LASTNAME, FIRSTNME, PHONENO ¦ ¦ 000200 FROM DSN8310.EMP ¦ ¦ 000300 WHERE WORKDEPT= 'D11' ¦ ¦ 000400 ORDER BY LASTNAME; ¦ ¦ ********************************* BOTTOM OF DATA ********************* ¦ ¦ ¦ ¦ ¦ ¦ ¦ +----------------------------------------------------------------------------------+ Figure 9. The EDIT Panel: After Entering an SQL Statement

Pressing the END PF key saves the data set. You can save the data set and continue editing it by entering the SAVE command. In fact, it is a good practice to save the data set after every 10 minutes or so of editing. Figure 9 shows what the panel looks like if you enter the sample SQL statement, followed by a SAVE command. The editing step is bypassed when you reset the EDIT INPUT processing option by specifying: EDIT INPUT ... ===> NO Subtopics 2.4.3.1 Comments in Your Input Data Set 2.4.3.1 Comments in Your Input Data Set You can put comments about SQL statements either on separate lines or on the same line. In either case, two hyphens (--) are used to begin a comment. Everything to the right of the two hyphens is ignored by DB2.

2.4.4 Step 4.

Process SQL Statements

SPUFI passes the input data set to DB2 for processing. The SQL statement in the input data set EXAMPLES(XMP1) is executed. Output is sent to the output data set userid.RESULT. The DB2 processing step is bypassed when you specify the EXECUTE processing option: EXECUTE ..... ===> NO Subtopics 2.4.4.1 Quitting While the Statement Is Executing 2.4.4.1 Quitting While the Statement Is Executing Your SQL statement might take a long time to execute, depending on how large a table DB2 has to search, or on how many rows DB2 has to process. To interrupt DB2's execution, press the PA1 key and respond to the prompting message that asks you if you really want to stop processing. This cancels the executing SQL statement and returns you to the ISPF-PDF menu. What happens to the output data set? This depends on how far execution has progressed before you interrupted the execution of the input data set. DB2 might not have opened the output data set yet, or the output data set might contain all or part of the results data produced so far. 2.4.5 Step 5. Browse the Output

SPUFI formats and displays the output data set using the ISPF Browse program. The output from the sample program is shown in Figure 10. An output data set contains these items for each SQL statement executed by DB2: _ _ _ The SQL statement that was executed, copied from the input data set The results of executing the SQL statement A set of messages about the execution of the SQL statement When executing a SELECT statement using SPUFI, an error-free result is indicated by the message "SQLCODE IS 100." If the message SQLCODE IS 100 is the only result of a SELECT statement executed using SPUFI, DB2 was unable to find any rows that satisfied the condition specified by the statement. For all other types of SQL statements executed with SPUFI, an error-free result is indicated by the message "SQLCODE IS 0." _ _ Errors return an SQLSTATE code to the SPUFI panel At the end of the data set are summary statistics that describe the execution of the input data set as a whole.

+-----------------------------------------------------------------------------------------+

¦ ¦ ¦ ¦ ¦ ¦ ¦ BROWSE-- userid.RESULT COLUMNS 001 072 ¦ ¦ COMMAND INPUT ===> SCROLL ===> PAGE ¦ ¦ --------+---------+---------+---------+---------+---------+--------+---------+ ¦ ¦ SELECT LASTNAME, FIRSTNME, PHONENO 00010000 ¦ ¦ FROM DSN8310.EMP 00020000 ¦ ¦ WHERE WORKDEPT = 'D11' 00030000 ¦ ¦ ORDER BY LASTNAME; 00040000 ¦ ¦ ---------+---------+---------+---------+---------+---------+--------+---------+ ¦ ¦ LASTNAME FIRSTNME PHONENO ¦ ¦ ADAMSON BRUCE 4510 ¦ ¦ BROWN DAVID 4501 ¦ ¦ JOHN REBA 0672 ¦ ¦ JONES WILLIAM 0942 ¦ ¦ LUTZ JENNIFER 0672 ¦ ¦ PIANKA ELIZABETH 3782 ¦ ¦ SCOUTTEN MARILYN 1682 ¦ ¦ STERN IRVING 6423 ¦ ¦ WALKER JAMES 2986 ¦ ¦ YAMAMOTO KIYOSHI 2890 ¦ ¦ YOSHIMURA MASATOSHI 2890 ¦ ¦ DSNE610I NUMBER OF ROWS DISPLAYED IS 11 ¦ ¦ DSNE616I STATEMENT EXECUTION WAS SUCCESSFUL, SQLCODE IS 100 ¦ ¦ ---------+---------+---------+---------+---------+---------+---¦ ¦ ---------+---------+---------+---------+---------+---------+---¦ ¦ DSNE617I COMMIT PERFORMED, SQLCODE IS 0 ¦ ¦ DSNE616I STATEMENT EXECUTION WAS SUCCESSFUL, SQLCODE IS 0 ¦ ¦ ---------+---------+---------+---------+---------+---------+---¦

Figure 10. Result Data Set from the Sample Problem Subtopics 2.4.5.1 Format of SELECT Statement Results 2.4.5.2 Content of the Messages

3.0 Section 3.

Coding SQL in Your Host Application Program

Subtopics 3.1 Chapter 3-1. Basics of Coding SQL in an Application Program 3.2 Chapter 3-2. Using a Cursor to Retrieve a Set of Rows 3.3 Chapter 3-3. Using DCLGEN 3.4 Chapter 3-4. Embedding SQL Statements in Host Languages 3.1 Chapter 3-1. Basics of Coding SQL in an Application Program

Suppose you are coding a COBOL application program to access data in a DB2 database. When your program executes an SQL statement, the program needs to communicate with DB2. When DB2 completes processing an SQL statement, DB2 sends back a return code; your program should test the return code to examine the results of the operation. To communicate with DB2, you need to: _ _ _ Delimit SQL statements, as described in "Delimiting an SQL Statement" in topic 3.1.2. Declare the tables you use, as described in "Declaring Table and View Definitions" in topic 3.1.3. (This is optional.) Declare the data items used to pass data between DB2 and a host language, as described in "Accessing Data Using Host Variables and Host Structures" in topic 3.1.4. Code SQL statements to access DB2 data. See "Accessing Data Using Host Variables and Host Structures" in topic 3.1.4. The SQL language is described in "Section 2. Using SQL Queries" in topic 2.0 and in SQL Reference. Details about how to use SQL statements within an application program are described in "Chapter 3-4. Embedding SQL Statements in Host Languages" in topic 3.4. _ Declare a communications area (SQLCA) or handle exceptional conditions that are indicated with return codes from DB2, in the SQLCA. See "Checking the Execution of SQL Statements" in topic 3.1.5 for more information.

_

In addition to these basic requirements, you should also consider several special topics in "Chapter 3-2. Using a Cursor to Retrieve a Set of Rows" in topic 3.2 and "Chapter 3-3. Using DCLGEN" in topic 3.3. "Chapter 3-2. Using a Cursor to Retrieve a Set of Rows" discusses how you can use a cursor in your application program to select a set of rows and then process the set one row at a time. "Chapter 3-3. Using DCLGEN" discusses how to use DB2's declarations generator, DCLGEN, to obtain accurate SQL DECLARE statements for tables and views. This book includes information about using SQL in application programs

written in assembler, C, COBOL, FORTRAN, and PL/I. You can also use SQL in application programs written in the following languages: Ada. See IBM Ada/370 SQL Module Processor for DB2 Database Manager User's Guide for more information about writing applications in Ada. APL2 (*). See APL2 Programming: Using Structured Query Language (SQL) for more information about writing applications in APL2. BASIC. See IBM BASIC Language Reference for more information about writing applications in BASIC. IBM SAA AD/Cycle* Prolog/MVS & VM Version 1. See IBM SAA AD/Cycle Prolog/MVS & VM Programmer for more information about writing applications in Prolog/MVS & VM. (*) Trademark of the IBM Corporation. Subtopics 3.1.1 Conventions Used in Examples of Coding SQL Statements 3.1.2 Delimiting an SQL Statement 3.1.3 Declaring Table and View Definitions 3.1.4 Accessing Data Using Host Variables and Host Structures 3.1.5 Checking the Execution of SQL Statements 3.1.1 Conventions Used in Examples of Coding SQL Statements The SQL statements shown in this section use the following conventions: _ The SQL statement is coded as part of a COBOL application program. Each SQL example is shown on several lines, with each clause of the statement on a separate line. The APOST and APOSTSQL precompiler options are assumed (although they are not the defaults). Character string literals within SQL and host language statements are delimited by apostrophes ('). The SQL statements access data in the sample tables that are shipped as part of DB2. Those tables contain data that a manufacturing company might keep about its employees and its current projects. They are described in Appendix A, "DB2 Sample Tables" in topic APPENDIX1.1. An SQL example does not necessarily show the complete syntax of an SQL statement. For the complete description and syntax of any of the statements described in this book, see Chapter 6 of SQL Reference. Examples do not take referential constraints into account. For more information about how referential constraints affect SQL statements, and examples of SQL statements operating with referential constraints, see "Chapter 2-2. Creating Tables and Modifying Data" in topic 2.2. Exceptions are noted

_

_

_

_

Some of the examples vary from these conventions. where they occur. 3.1.2 Delimiting an SQL Statement

Bracket an SQL statement in your program between EXEC SQL and a statement terminator. The terminators for the languages described in this book are:

Language Assembler C COBOL FORTRAN PL/I

SQL Statement Terminator End of line or end of last continued line Semicolon (;) END-EXEC End of line or end of last continued line Semicolon (;)

For example, use EXEC SQL and END-EXEC to delimit an SQL statement in a COBOL program, like this: EXEC SQL an SQL statement END-EXEC.

Before your program issues SQL statements that retrieve, update, delete, or insert data, you can declare the tables and views your program accesses by including an SQL DECLARE statement in your program. You do not have to declare tables or views, but there are advantages if you do. One advantage is documentation; for example, the DECLARE statement specifies the structure of the table or view you are working with, and the data type of each column. You can refer to the DECLARE statement for the column names and data types in the table or view. Another advantage is that the DB2 precompiler uses your declarations to make sure you have used correct column names and data types in your SQL statements. The DB2 precompiler issues a warning message when the column names and data types do not correspond to the SQL DECLARE statements in your program. A way to declare a table or view is to code a DECLARE statement in the WORKING-STORAGE SECTION or LINKAGE SECTION within the DATA DIVISION of your COBOL program. Specify the name of the table and list each column and its data type. When you declare a table or view, you specify DECLARE table-name TABLE regardless of whether the table-name refers to a table or a view. For example, the DECLARE TABLE statement for the DSN8310.DEPT table looks like this: EXEC SQL DECLARE DSN8310.DEPT TABLE (DEPTNO CHAR(3) DEPTNAME VARCHAR(36) MGRNO CHAR(6) ADMRDEPT CHAR(3) LOCATION CHAR(16) END-EXEC. As an alternative to coding the DECLARE statement yourself, you can use DCLGEN, the declarations generator supplied with DB2. For more information about using DCLGEN, see "Chapter 3-3. Using DCLGEN" in

NOT NULL, NOT NULL, , NOT NULL, )

topic 3.3.

3.1.4 Accessing Data Using Host Variables and Host Structures You can access data using host variables and host structures. A host variable is a data item declared in the host language (in this case, COBOL) for use within an SQL statement. Using host variables, you can: _ _ _ Etc. A host structure is a group of host variables that is referred to by a single name in an SQL statement. Host structures are defined by statements of the host language. Subtopics 3.1.4.1 Using Host Variables 3.1.4.2 Using Host Structures 3.1.4.1 Using Host Variables Any valid host variable name can be used in an SQL statement. The name must be declared in the host program before it is used. (For more information see the appropriate language section in "Chapter 3-4. Embedding SQL Statements in Host Languages" in topic 3.4.) In your application program written in the language of your choice, the host variable declaration should as close as possible match the types of the associated data in the data base to get the best performance. For more performance suggestions, see "Section 5. Additional Programming Techniques" in topic 5.0 and Volume 3 of Administration Guide. A host variable can be used to represent a data value but cannot be used to represent a table, view, or column name. (Table, view, or column names can be specified at execution time using dynamic SQL. See "Chapter 5-1. Coding Dynamic SQL in Application Programs" in topic 5.1 for more information.) Host variables follow the naming conventions of the host language. (In this chapter, COBOL is assumed to be the host language.) Host variables used within SQL statements must be preceded by a colon (:) to tell DB2 that the variable is not a column name. (4) Host variables outside of SQL statements must not be preceded by a colon. For more information about declaring host variables, see the appropriate language section: Retrieve data and put it into the host variable for use by the application program Use the data in the host variable to insert into a table or to change the contents of a row Use the data in the host variable when evaluating a WHERE or HAVING clause.

_ _ _ _ _

Assembler: "Using Host Variables" in topic 3.4.1.4 C: "Using Host Variables" in topic 3.4.2.4 COBOL: "Using Host Variables" in topic 3.4.3.4 FORTRAN: "Using Host Variables" in topic 3.4.4.4 PL/I: "Using Host Variables" in topic 3.4.5.4. (4) The colon is required by the SQL standards and other implementations of SQL. (Although the colon is currently optional in certain contexts, this feature could be withdrawn in future releases of DB2. Therefore it is always best to use the colon. A message is issued when a colon does not precede the name of a host variable in an SQL statement.)

Subtopics 3.1.4.1.1 3.1.4.1.2 3.1.4.1.3 3.1.4.1.4

3.1.4.1.5

Retrieving Data into a Host Variable Inserting and Updating Data Searching Data Using Indicator Variables with Host Variables Considerations

3.1.4.1.1 Retrieving Data into a Host Variable You can use a host variable to specify a program data area that is to contain the column values of a retrieved row or rows. Retrieving a Single Row of Data: The INTO clause of the SELECT statement names one or more host variables to contain the column values returned. The elementary data items involved correspond one-for-one with the list of column names in the SELECT list. For example, suppose you are retrieving the EMPNO, LASTNAME, and WORKDEPT column values from rows in the DSN8310.EMP table. You can define a data area in your program to hold each column, then name the data areas with an INTO clause, as in the following example (where each host variable is preceded by a colon): EXEC SQL SELECT EMPNO, LASTNAME, WORKDEPT INTO :CBLEMPNO, :CBLNAME, :CBLDEPT FROM DSN8310.EMP WHERE EMPNO = :EMPID END-EXEC. In the DATA DIVISION of the program, the host variables CBLEMPNO, CBLNAME, and CBLDEPT must be declared such that each is compatible with the data type contained in the DSN8310.EMP table columns EMPNO, LASTNAME, and WORKDEPT. If the SELECT statement returns more than one row, this is an error, and any data returned is undefined and unpredictable. To avoid this error see "Retrieving Multiple Rows of Data." Specifying a List of Items in a SELECT Clause: When specifying a list of items in the SELECT clause, you are not restricted to the column names of tables and views. A set of column values intermixed with host variable values and constants can be returned. For example: MOVE 4476 TO RAISE. MOVE '000220' TO PERSON.

EXEC SQL SELECT EMPNO, LASTNAME, SALARY, :RAISE, SALARY + :RAISE INTO :EMP-NUM, :PERSON-NAME, :EMP-SAL, :EMP-RAISE, :EMP-TTL FROM DSN8310.EMP WHERE EMPNO = :PERSON END-EXEC. The results are shown below with column headings that represent the names of the host variables: EMP-NUM ======= 000220 PERSON-NAME =========== LUTZ EMP-SAL ======= 29840 EMP-RAISE ========= 4476 EMP-TTL ======= 34316

Retrieving Multiple Rows of Data: If you are unsure about the number of rows that will be returned, or if you expect that more than one row will be returned, then you must use an alternative to the SELECT ... INTO statement. DB2 has a mechanism called a cursor that enables an application to process a set of rows and retrieve one row at a time from the result table. This mechanism is described in "Chapter 3-2. Using a Cursor to Retrieve a Set of Rows" in topic 3.2. 3.1.4.1.2 Inserting and Updating Data You can set or change a value in a DB2 table to the value of a host variable. Use the host variable name in the SET clause of UPDATE or the VALUES clause of INSERT. This example changes an employee's phone number: EXEC SQL UPDATE DSN8310.EMP SET PHONENO = :NEWPHONE WHERE EMPNO = :EMPID END-EXEC. 3.1.4.1.3 Searching Data You can use a host variable to specify a value in the predicate of a search condition or to replace a constant in an expression. For example, if you have defined a field called EMPID that contains an employee number, you can retrieve the name of the employee whose number is 000110 with: MOVE '000110' TO EMPID. EXEC SQL SELECT LASTNAME INTO :PGM-LASTNAME FROM DSN8310.EMP WHERE EMPNO = :EMPID END-EXEC. 3.1.4.1.4 Using Indicator Variables with Host Variables Indicator variables are small integers that are used to: Indicate whether the values of associated host variables are null Verify that the value of a retrieved character string has not been truncated when retrieved

Insert null values from host variables into columns.

Retrieving Data into Host Variables: If the value for the column you are retrieving is null, DB2 puts a negative value in the indicator variable. If it is null because of a numeric or character conversion error, or an arithmetic expression error, DB2 sets the indicator variable to -2. See "Handling Arithmetic or Conversion Errors" in topic 3.1.5.3 for more information. If you do not use an indicator variable and DB2 retrieves a null value, an error results. When DB2 retrieves the value of a column, you can test the indicator variable. If the indicator variable's value is less than zero, the column value is null. When the column value is null, DB2 puts nothing into the host variable; its value is unchanged. You can also use an indicator variable to verify that a retrieved character string value has not been truncated. If the indicator variable contains a positive integer, it specifies the original length of the string. You specify an indicator variable, preceded by a colon, immediately after the host variable. Optionally, you can use the word INDICATOR between the host variable and its indicator variable. Thus, the following two examples are equivalent: +-------------------------------------------------------------------------------------------------+ ¦ EXEC SQL ¦ EXEC SQL ¦ ¦ SELECT PHONENO ¦ SELECT PHONENO ¦ ¦ INTO :CBLPHONE:INDNULL ¦ INTO :CBLPHONE INDICATOR :INDNULL ¦ ¦ FROM DSN8310.EMP ¦ FROM DSN8310.EMP ¦ ¦ WHERE EMPNO = :EMPID ¦ WHERE EMPNO = :EMPID ¦ ¦ END-EXEC. ¦ END-EXEC. ¦ +-------------------------------------------------------------------------------------------------+ You can then test INDNULL for a negative value. If it is negative, the corresponding value of PHONENO is null, and you can disregard the contents of CBLPHONE. When a column value is fetched using a cursor, you can use the same technique to determine whether the column value is null or not. Inserting Null Values into Columns Using Host Variables: You can use an indicator variable to insert a null value from a host variable into a column. When DB2 processes INSERT and UPDATE statements, it checks the indicator variable (if it exists). If the indicator variable is negative, the column value is set to null value. If the indicator variable is greater than -1, the associated host variable contains a value for the

column. For example, you could put a value in a column (using INSERT or UPDATE), but you are not sure whether the value is always specified with the input data. To allow the possibility that the column's value might be null, you can code: EXEC SQL UPDATE DSN8310.EMP SET PHONENO = :NEWPHONE:PHONEIND WHERE EMPNO = :EMPID END-EXEC. When NEWPHONE contains other than a null value, set PHONEIND to zero by preceding the statement with: MOVE 0 TO PHONEIND. Otherwise, to tell DB2 that NEWPHONE contains a null value, set PHONEIND to a negative value, as in the following statement: MOVE -1 TO PHONEIND. 3.1.4.1.5 Considerations If you transfer data between a DB2 column and a host variable, and the two do not have the same data type or length attribute, you can expect the data format to change. Values might be truncated, padded, or rounded somehow. If you transfer or compare data, see Chapter 3 of SQL Reference for the rules associated with these operations. 3.1.4.2 Using Host Structures A host structure can be substituted for one or more host variables and, like host variables, indicator variables (or structures) can be used with host structures. Subtopics 3.1.4.2.1 Example: Using a Host Structure 3.1.4.2.2 Using Indicator Variables with Host Structures 3.1.4.2.1 Example: Using a Host Structure

In the following example, assume that your COBOL program includes the following SQL statement: EXEC SQL SELECT EMPNO, FIRSTNME, MIDINIT, LASTNAME, WORKDEPT INTO :EMPNO, :FIRSTNME, :MIDINIT, :LASTNAME, :WORKDEPT FROM DSN8310.VEMP WHERE EMPNO = :EMPID END-EXEC. In this example, if you want to avoid listing host variables, you can substitute the name of a structure, say :PEMP, that contains :EMPNO, :FIRSTNME, :MIDINIT, :LASTNAME, and :WORKDEPT. The example then reads: EXEC SQL SELECT EMPNO, FIRSTNME, MIDINIT, LASTNAME, WORKDEPT INTO :PEMP

FROM DSN8310.VEMP WHERE EMPNO = :EMPID END-EXEC. You can declare a host structure yourself, or you can use DCLGEN to generate a COBOL record description, PL/I structure declaration, or C structure declaration that corresponds to the columns of a table For more details about coding a host structure in your program, see "Chapter 3-4. Embedding SQL Statements in Host Languages" in topic 3.4. For more information on using DCLGEN and the restrictions that apply to the C language, see "Chapter 3-3. Using DCLGEN" in topic 3.3. After the host structure is defined, you can refer to it in an SQL statement instead of listing several host variables (that is, the names of the data items that make up the host structure). 3.1.4.2.2 Using Indicator Variables with Host Structures You can define an indicator structure (an array of halfword integer variables) to support a host structure. Indicator structures are defined in the DATA DIVISION of your COBOL program. If the column values your program retrieves into a host structure can be null, you can attach an indicator structure name to the host structure name. This allows DB2 to notify your program about each null value returned to a host variable in the host structure. For example: 01 PEMP-ROW. 10 EMPNO PIC X(6). 10 FIRSTNME. 49 FIRSTNME-LEN PIC S9(4) USAGE COMP. 49 FIRSTNME-TEXT PIC X(12). 10 MIDINIT PIC X(1). 10 LASTNAME. 49 LASTNAME-LEN PIC S9(4) USAGE COMP. 49 LASTNAME-TEXT PIC X(15). 10 WORKDEPT PIC X(3). 10 EMP-BIRTHDATE PIC X(10). 01 INDICATOR-TABLE. 02 EMP-IND PIC S9(4) COMP OCCURS 6 TIMES. . . . MOVE '000230' TO EMPNO. . . . EXEC SQL SELECT EMPNO, FIRSTNME, MIDINIT, LASTNAME, WORKDEPT, BIRTHDATE INTO :PEMP-ROW:EMP-IND FROM DSN8310.EMP WHERE EMPNO = :EMPNO END-EXEC. In this example, EMP-IND is an array containing six values, each of which can be tested for a negative value. If, for example, EMP-IND(6) contains a negative value, the corresponding host variable in the host structure (EMP-BIRTHDATE) contains a null value. Because this example selects rows from the DSN8310.EMP table, some of the EMP-IND array values are always zero. The first five columns of each row are defined NOT NULL. In the above example, DB2 selects the values for a row of data into a host structure. Therefore, you must use a

corresponding structure for the indicator variables to determine which (if any) selected column values are null. For information on using the IS NULL keyword phrase in WHERE clauses, see "Chapter 2-1. Retrieving Data" in topic 2.1. 3.1.5 Checking the Execution of SQL Statements A program that includes SQL statements needs to have an area set apart for communication with DB2; this area is called the SQL communication area (SQLCA). When DB2 processes an SQL statement in your program, it places return codes in the SQLCODE (5) and SQLSTATE fields of the SQLCA. The return codes indicate whether the statement you executed succeeded or failed. Because the SQLCA is a valuable problem-diagnosis tool, it is a good idea to include the instructions necessary to display some of the information contained in the SQLCA in your application programs. For example, the contents of SQLERRD(3), which indicates the number of rows updated, inserted, or deleted by DB2, could be useful. If SQLWARN0 is set to W, at least one of the SQL warning flags (SQLWARN1 through SQLWARNA) is set. See Appendix C of SQL Reference for a description of all the fields in the SQLCA. The WHENEVER statement can be used to make a decision in your program based on a condition that is indicated in the SQLCA. +--- Batch, CICS, IMS, and TSO ------------------------------------------+ ¦ ¦ ¦ The -911 SQLCODE indicates that a unit of work has been rolled back. ¦ ¦ If the impact of this action is not taken into consideration when ¦ ¦ writing the code, the data integrity of your system can be ¦ ¦ compromised. ¦ ¦ ¦ +------------------------------------------------------------------------+ (5) SQLCODE can be a stand-alone integer variable. SQLCA is not used if SQLCODE is a stand-alone integer variable.

Subtopics 3.1.5.1 SQLCODE and SQLSTATE 3.1.5.2 The WHENEVER Statement 3.1.5.3 Handling Arithmetic or Conversion Errors 3.1.5.4 Handling SQL Error Return Codes 3.1.5.1 SQLCODE and SQLSTATE Whenever an SQL statement is executed, a return code is placed in the SQLCODE and SQLSTATE fields of the SQLCA. Although both fields serve basically the same purpose (indicating whether the statement execution was successful or not) there are some differences between the two fields. SQLCODE: _ _ _ DB2 returns the following codes in SQLCODE:

If SQLCODE = 0, execution was successful. If SQLCODE > 0, execution was successful with a warning. If SQLCODE < 0, execution was not successful.

SQLCODE 100 indicates "no data" was found.

The meaning of SQLCODEs other than 0 and 100 varies with the particular product implementing SQL. SQLSTATE: SQLSTATE allows an application program to check for errors in the same way for different IBM database management systems. See Appendix C of Messages and Codes for a complete list of possible SQLSTATE values.

3.1.5.2 The WHENEVER Statement The WHENEVER statement causes DB2 to check the SQLCA and continue processing your program, or branch to another area in your program if an error, exception, or warning exists as a result of executing an SQL statement. Your program can then examine the SQLCODE or SQLSTATE fields to take an action specific to the error or exception situation. The WHENEVER statement allows you to specify what should be done whenever a general condition is true. You can specify more than one WHENEVER statement in your program. When you do this, the first WHENEVER statement applies to all subsequent SQL statements in the source program until another WHENEVER statement is specified. The WHENEVER statement looks like this: EXEC SQL WHENEVER condition action END-EXEC There are three conditions you can specify: SQLWARNING Indicates what should be done when SQLWARN0 = W or SQLCODE contains a positive value other than 100. SQLWARN0 can be set for several different reasons. For example, if a column value was truncated when it was moved into a host variable; it is possible your program would not regard this as an error. Indicates what should be done when DB2 returns an error code as the result of an SQL statement (SQLCODE < 0). Indicates what should be done when DB2 cannot find a row to satisfy your SQL statement or when there are no more rows to fetch (SQLCODE = 100).

SQLERROR NOT FOUND

You can also specify the action you want taken: CONTINUE Specifies the next sequential statement of the source program. GOTO or GO TO host-label Specifies the statement identified by host-label. For host-label, substitute a single token, optionally preceded by a colon. The form of the token depends on the host language. In COBOL, for example, it can be section-name or an unqualified paragraph-name.

The WHENEVER statement must precede the first SQL statement it is to affect. However, if your program checks the SQLCODE directly, the check must be done after the SQL statement is executed. 3.1.5.3 Handling Arithmetic or Conversion Errors Numeric or character conversion errors or arithmetic expression errors can set an indicator variable to -2. For example, division by zero and arithmetic overflow does not necessarily halt the execution of a SELECT statement. If the error occurs in the SELECT list, the statement can continue to execute and return good data for rows in which the error does not occur, if indicator variables have been used. For rows in which the error does occur, one or more selected items have no meaningful value. This error is flagged by a -2 in the indicator variable for the affected host variable, and an SQLCODE of +802 (SQLSTATE '01519') in the SQLCA. 3.2 Chapter 3-2. Using a Cursor to Retrieve a Set of Rows

DB2 has a mechanism called a cursor to allow an application program to retrieve a set of rows. "Chapter 3-1. Basics of Coding SQL in an Application Program" in topic 3.1, showed how to use a SELECT INTO statement to retrieve a single row of data. This chapter explains how your application program can select a set of rows using a cursor, and then process the set one row at a time. Subtopics 3.2.1 Cursor Functions 3.2.2 How to Use a Cursor: An Example 3.2.3 Maintaining Cursor Position 3.2.1 Cursor Functions DB2 can be used to retrieve and process a set of rows. Each row in the set satisfies the criteria specified in the search conditions of an SQL statement. However, when a set of rows is selected by your program, the program cannot process all the rows at once. The program needs to process the rows one at a time. To help illustrate the concept of a cursor, assume that DB2 builds a result table (6) to hold all the rows retrieved by executing the SELECT statement. DB2 uses a cursor to make rows from the result table available to your program. A cursor identifies the current row of the result table specified by a SELECT statement. When you use a cursor, your program can retrieve each row sequentially from the result table until end-of-data (that is, the not found condition, SQLCODE=100 and SQLSTATE = '02000') is reached. The set of rows obtained as a result of executing the SELECT statement can consist of zero, one, or many rows, depending on the number of rows that satisfy the SELECT statement search condition. The SELECT statement referred to in this section must be within a DECLARE CURSOR statement and cannot include an INTO clause. The DECLARE CURSOR statement defines and names the cursor, identifying the set of rows to be retrieved with the SELECT statement of the cursor. The result table of a cursor is processed much like a sequential data set. The cursor must be opened (with an OPEN statement) before any rows are retrieved. A FETCH statement is used to retrieve the cursor's current

row. FETCH can be executed repeatedly until all rows have been retrieved. When the end-of-data condition occurs, you must close the cursor with a CLOSE statement (similar to end-of-file processing). Your program can have several cursors. _ _ _ Each cursor requires its own:

DECLARE CURSOR statement to define the cursor OPEN and CLOSE statements to open and close the cursor FETCH statement to retrieve rows from the cursor's result table.

Declarations for host variables that are referred to in a DECLARE CURSOR statement must precede the DECLARE CURSOR statement. Refer to Chapter 6 of SQL Reference for further information. You can use cursors to fetch, update, or delete a row of a table, but you cannot use them to insert a row into a table. (6) DB2 implements the concept of a result table in different ways, depending on the complexity of the SELECT statement. However, the concept is the same regardless of the implementation. 3.2.2 How to Use a Cursor: An Example Suppose your program examines data about people in department D11. The data is kept in the DSN8310.EMP table. The following shows the SQL statements you must include in a COBOL program to define and use a cursor. In this example, the cursor is used by the program to process a set of rows from the DSN8310.EMP table. +------------------------------------------------------------------------+ ¦ Table 5. SQL Statements Required to Define and Use a Cursor in a COBOL ¦ ¦ Program ¦ +------------------------------------------------------------------------¦ ¦ SQL Statement ¦ Described in Section ¦ +------------------------------------+-----------------------------------¦ ¦ EXEC SQL ¦ "Step 1: Define the Cursor" in ¦ ¦ DECLARE THISEMP CURSOR FOR ¦ topic 3.2.2.1 ¦ ¦ SELECT EMPNO, LASTNAME, ¦ ¦ ¦ WORKDEPT, JOB ¦ ¦ ¦ FROM DSN8310.EMP ¦ ¦ ¦ WHERE WORKDEPT = 'D11' ¦ ¦ ¦ FOR UPDATE OF JOB ¦ ¦ ¦ END-EXEC. ¦ ¦ +------------------------------------+-----------------------------------¦ ¦ EXEC SQL ¦ "Step 2: Open the Cursor" in ¦ ¦ OPEN THISEMP ¦ topic 3.2.2.2 ¦ ¦ END-EXEC. ¦ ¦ +------------------------------------+-----------------------------------¦ ¦ EXEC SQL ¦ "Step 3: Specify What to Do When ¦ ¦ WHENEVER NOT FOUND ¦ End-of-Data Is Reached" in ¦ ¦ GO TO CLOSE-THISEMP ¦ topic 3.2.2.3 ¦ ¦ END-EXEC. ¦ ¦ +------------------------------------+-----------------------------------¦ ¦ EXEC SQL ¦ "Step 4: Retrieve a Row Using ¦ ¦ FETCH THISEMP ¦ the Cursor" in topic 3.2.2.4 ¦ ¦ INTO :EMP-NUM, :NAME2, ¦ ¦ ¦ :DEPT, :JOB-NAME ¦ ¦ ¦ END-EXEC. ¦ ¦ +------------------------------------+-----------------------------------¦ ¦ ... for specific employees ¦ "Step 5a: Update the Current ¦ ¦ in Department D11, ¦ Row" in topic 3.2.2.5 ¦

¦ update the JOB value: ¦ ¦ ¦ ¦ ¦ ¦ EXEC SQL ¦ ¦ ¦ UPDATE DSN8310.EMP ¦ ¦ ¦ SET JOB = :NEW-JOB ¦ ¦ ¦ WHERE CURRENT OF THISEMP ¦ ¦ ¦ END-EXEC. ¦ ¦ ¦ ¦ ¦ ¦ ... then print the row. ¦ ¦ +------------------------------------+-----------------------------------¦ ¦ ... for other employees, ¦ "Step 5b: Delete the Current ¦ ¦ delete the row: ¦ Row" in topic 3.2.2.6 ¦ ¦ ¦ ¦ ¦ EXEC SQL ¦ ¦ ¦ DELETE FROM DSN8310.EMP ¦ ¦ ¦ WHERE CURRENT OF THISEMP ¦ ¦ ¦ END-EXEC. ¦ ¦ +------------------------------------+-----------------------------------¦ ¦ Branch back to fetch and ¦ ¦ ¦ process the next row. ¦ ¦ +------------------------------------+-----------------------------------¦ ¦ CLOSE-THISEMP. ¦ "Step 6: Close the Cursor" in ¦ ¦ EXEC SQL ¦ topic 3.2.2.7 ¦ ¦ CLOSE THISEMP ¦ ¦ ¦ END-EXEC. ¦ ¦ +------------------------------------------------------------------------+ Subtopics 3.2.2.1 Step 3.2.2.2 Step 3.2.2.3 Step 3.2.2.4 Step 3.2.2.5 Step 3.2.2.6 Step 3.2.2.7 Step 1: 2: 3: 4: 5a: 5b: 6: Define the Cursor Open the Cursor Specify What to Do When End-of-Data Is Reached Retrieve a Row Using the Cursor Update the Current Row Delete the Current Row Close the Cursor Define the Cursor

3.2.2.1 Step 1:

To define and identify a set of rows to be accessed with a cursor, issue a DECLARE CURSOR statement. The DECLARE CURSOR statement names a cursor and specifies a SELECT statement. The SELECT statement defines a set of rows that, conceptually, make up the result table. The DECLARE CURSOR statement looks like this: EXEC SQL DECLARE cursor-name CURSOR FOR SELECT column-name-list FROM table-name WHERE search-condition FOR UPDATE OF column-name END-EXEC. The SELECT statement shown here is rather simple. You can code several other types of clauses in a SELECT statement within a DECLARE CURSOR statement. Chapter 6 of SQL Reference illustrates several more clauses that can be used within a SELECT statement. Updating a Column: If you intend to update a column in any (or all) of the rows of the identified table, include the FOR UPDATE OF clause, which names each column you intend to update. The effects of the FOR UPDATE OF

clause are affected by the NOFOR and STDSQL precompiler options. These are described in Table 24 in topic 4.2.2.4. If you do not specify the names of columns you intend to update, and you do not specify the STDSQL(86) option or the NOFOR precompiler options, you receive error codes in the SQLCODE and SQLSTATE fields of the SQLCA. A column of the identified table can be updated even though it is not part of the result table. In this case, you do not need to name the column in the SELECT statement (but do not forget to name it in the FOR UPDATE OF clause). When the cursor retrieves a row (using FETCH) that contains a column value you want to update, you can use UPDATE ... WHERE CURRENT OF to update the row. For example, assume that each row of the result table includes the EMPNO, LASTNAME, and WORKDEPT columns from the DSN8310.EMP table. If you want to update the JOB column (one of the columns in the DSN8310.EMP table), the DECLARE CURSOR statement must include FOR UPDATE OF JOB even though JOB is omitted from the SELECT statement. Read-Only Result Table: Read-only result tables cannot be updated using a cursor. Read-only result table specifications are described in greater detail in SQL Reference. 3.2.2.2 Step 2: Open the Cursor

To tell DB2 you are ready to process the first row of the result table, have your program issue the OPEN statement. When this happens, DB2 processes the SELECT statement within the DECLARE CURSOR statement to identify a set of rows using the current value of any host variables specified in the SELECT statement. The result table can contain zero, one, or many rows, depending on the extent to which the search condition is satisfied. The OPEN statement looks like this: EXEC SQL OPEN cursor-name END-EXEC. When used with cursors, the CURRENT DATE, CURRENT TIME, and CURRENT TIMESTAMP special registers are evaluated once when the OPEN statement is executed; the value returned in the register is then used on all subsequent FETCH statements.

3.2.2.3 Step 3:

Specify What to Do When End-of-Data Is Reached

Test the SQLCODE field for a value of 100 or the SQLSTATE field for a value of '02000' to determine if the last row of data has been retrieved. These codes occur when a FETCH statement has retrieved the last row in the result table and your program issues a subsequent FETCH. For example: IF SQLCODE = 100 GO TO DATA-NOT-FOUND. An alternative to this technique is to code the WHENEVER NOT FOUND statement. The WHENEVER NOT FOUND statement can result in a branch to another part of your program, where a CLOSE statement is issued. The WHENEVER NOT FOUND statement looks like this: EXEC SQL WHENEVER NOT FOUND GO TO symbolic-address

END-EXEC. Your program must anticipate an end-of-data condition whenever a cursor is used to fetch a row, and it must be prepared to handle this situation when it occurs. For further information about the WHENEVER NOT FOUND statement, see "Checking the Execution of SQL Statements" in topic 3.1. 3.2.2.4 Step 4: Retrieve a Row Using the Cursor

To move the contents of a selected row into your program host variables, use the FETCH statement. The SELECT statement within the DECLARE CURSOR statement identifies rows that contain the column values your program wants (that is, the result table is defined), but DB2 does not retrieve any data for your application program until FETCH is issued. When your program issues the FETCH statement, DB2 uses the cursor to point to the next row in the result table, making it the current row. DB2 then moves the current row contents into your program host variables (specified with the INTO clause). This sequence is repeated each time FETCH is issued, until you have processed all rows in the result table. The FETCH statement looks like this: EXEC SQL FETCH cursor-name INTO :host variable1, :host variable2 END-EXEC. When querying a remote subsystem with FETCH, it is possible that you may experience reduced efficiency. To combat this problem, you can use block fetch. For more information see "How to Ensure Block Fetching" in topic 4.1.4.6.2. Block fetch processes rows ahead of the application's current row. Block fetch cannot be used when a cursor is used for update or delete. 3.2.2.5 Step 5a: Update the Current Row

When your program has retrieved the current row, you can update its data by using the UPDATE statement. To do this, issue an UPDATE...WHERE CURRENT OF statement; it is intended specifically for use with a cursor. The UPDATE ... WHERE CURRENT OF statement looks like this: EXEC SQL UPDATE table-name SET column1 = value, column2 = value WHERE CURRENT OF cursor-name END-EXEC. When used with a cursor, the UPDATE statement differs from the one described in "Chapter 2-2. Creating Tables and Modifying Data" in topic 2.2. _ _ _ You update only one row--the current row. The WHERE clause identifies the cursor that points to the row to be updated. Each column to be updated must have been named previously in the FOR UPDATE OF clause of the SELECT statement associated with the DECLARE CURSOR statement. (7)

After you have updated a row, the cursor points to the current row until you issue a FETCH statement for the next row. Remember that you cannot update a row if your update violates any referential constraints the table might have. Refer to "Updating Tables with Referential Constraints" in topic 2.2.2.3 for more information. "Updating Current Values: UPDATE" in topic 2.2.2.2 showed you how to use the UPDATE statement repeatedly when you update all rows that meet a specific search condition. Alternatively, you can use the UPDATE... WHERE CURRENT OF statement repeatedly when you want to obtain a copy of the row, examine it, and then update it. (7) If you do not specify the names of columns you intend to update, you receive an error code in the SQLCODE and SQLSTATE fields of the SQLCA when you try to update the columns. This is true only if you have not specified the STDSQL(86) option or the NOFOR precompile options.

3.2.2.6 Step 5b:

Delete the Current Row

When your program has retrieved the current row, you can delete the row by using the DELETE statement. To do this, you issue a DELETE...WHERE CURRENT OF statement; it is intended specifically for use with a cursor. The DELETE...WHERE CURRENT OF statement looks like this: EXEC SQL DELETE FROM table-name WHERE CURRENT OF cursor-name END-EXEC. When used with a cursor, the DELETE statement differs from the one you learned in "Chapter 2-2. Creating Tables and Modifying Data" in topic 2.2. _ _ You delete only one row--the current row. The WHERE clause identifies the cursor that points to the row to be deleted.

Deleting a row does not require any additional modifications to the DECLARE statement. After you have deleted a row, you cannot update or delete another row using that cursor until you issue a FETCH statement to position the cursor on the next row. "Deleting Rows: DELETE" in topic 2.2.2.4 showed you how to use the DELETE statement to delete all rows that meet a specific search condition. Alternatively, you can use the DELETE...WHERE CURRENT OF statement repeatedly when you want to obtain a copy of the row, examine it, and then delete it. Remember that you cannot delete a row if doing so will result in the violation of any referential constraints the table might have. In the example on page 3.2.2, the employee cannot be deleted from the employee table unless the employee has already been deleted from the project table and the project activity table. This is because of the way the referential constraints have been defined on these tables. Refer to "Updating Tables with Referential Constraints" in topic 2.2.2.3 for more

information. 3.2.2.7 Step 6: Close the Cursor

When you are finished processing the rows of the result table and you want to use the cursor again, issue a CLOSE statement to close the cursor: EXEC SQL CLOSE cursor-name END-EXEC. If you are finished processing the rows of the "result table" and you do not want to use the cursor, you can let DB2 automatically close the cursor when your program terminates. When a cursor is closed, locks can be freed. To release page locks as soon as possible, issue a CLOSE statement as soon as you are finished with the cursor. For further information see "Planning for Concurrency" in topic 4.1.2. 3.2.3 Maintaining Cursor Position If your program completes a unit of work (that is, it either commits or rolls back the changes made so far), DB2 automatically closes all open cursors that were not declared with the WITH HOLD option. You can reopen the cursor after it has been closed, but you begin processing at the beginning of the result table. To maintain a cursor and its position across commit points, use the WITH HOLD option of the DECLARE CURSOR statement. The commit process releases only locks that are not required to maintain cursor position. After the commit process, open cursors are not closed. A cursor is positioned after the last row retrieved and before the next logical row of the result table to be returned. The following example shows how to use a cursor to fetch data without writing code to reposition the cursor after a commit point: EXEC SQL DECLARE EMPLUPDT CURSOR WITH HOLD FOR SELECT EMPNO, LASTNAME, PHONENO, JOB, SALARY, WORKDEPT FROM DSN8310.EMP WHERE WORKDEPT < 'D11' ORDER BY EMPNO END-EXEC. A cursor declared in this way can be closed when: _ _ _ A CLOSE cursor, ROLLBACK, or CONNECT statement is issued A CAF CLOSE function is issued The application program terminates.

If the program abends, the cursor position is lost; to prepare for restart, your program must reposition the cursor. The following restrictions apply for declaring WITH HOLD cursors: DECLARE CURSOR WITH HOLD should not be used with the new user signon from a DB2 attachment facility, because all open cursors are closed. Do not declare a WITH HOLD cursor in a thread that could become inactive. If you do, its locks will be held indefinitely.

+--- IMS ------------------------------------------------------------+ ¦ ¦ ¦ You cannot make use of DECLARE CURSOR...WITH HOLD for message ¦ ¦ processing programs (MPP) and message-driven batch message ¦ ¦ processing (BMP). Each message is a new user for DB2, and no ¦ ¦ cursors, whether declared using WITH HOLD or not, are continued ¦ ¦ for a new user. You can use WITH HOLD for non-message-driven BMP ¦ ¦ and DL/I batch programs. ¦ ¦ ¦ +--------------------------------------------------------------------+ +--- CICS -----------------------------------------------------------+ ¦ ¦ ¦ In CICS applications, you can use DECLARE CURSOR...WITH HOLD to ¦ ¦ indicate that a cursor should not close at a commit or sync point. ¦ ¦ However, SYNCPOINT ROLLBACK closes all cursors, and end-of-task ¦ ¦ (EOT) closes all cursors before DB2 reuses or terminates the ¦ ¦ thread. Because pseudo-conversational transactions usually have ¦ ¦ multiple EXEC CICS RETURN statements and thus span multiple EOTs, ¦ ¦ the scope of a held cursor is limited. Across EOTs, a cursor ¦ ¦ declared WITH HOLD must be reopened and repositioned just as if ¦ ¦ the WITH HOLD option had not been specified. ¦ ¦ ¦ ¦ You should always close cursors that you no longer need. If you ¦ ¦ let DB2 close a CICS attachment cursor, the cursor might not close ¦ ¦ until DB2 reuses or terminates the thread. ¦ ¦ ¦ +------------------------------------------------------------3.3 Chapter 3-3. Using DCLGEN

DCLGEN, the declarations generator supplied with DB2, produces a DECLARE statement you can use in a PL/I, C, or COBOL program, so that you do not need to code the statement yourself. DCLGEN generates a table declaration and puts it into a member of a partitioned data set that you can include in your program. When you use DCLGEN to generate a table's declaration, DB2 gets the relevant information from the DB2 catalog, which contains information about the table's definition, and the definition of each column within the table. DCLGEN uses this information to produce a complete SQL DECLARE statement for the table or view and a matching PL/I or C structure declaration or COBOL record description. You can use DCLGEN for table declarations only if the table you are declaring already exists. DCLGEN must be used before the program is precompiled. Supply DCLGEN with the table or view name before you precompile your program. To use the declarations in your program, use the SQL INCLUDE statement. DB2 must be active before you can use DCLGEN. several different ways: _ You can invoke DCLGEN in

From ISPF through DB2I. Select the DCLGEN option on the DB2I Primary Option Menu panel. Next, fill in the DCLGEN panel with the information it needs to build the declarations and press ENTER. Directly from TSO. To do this, sign on to TSO, issue the TSO command "DSN", and then issue the subcommand "DCLGEN". From a CLIST, running in TSO foreground or background, that issues DSN

_ _

then DCLGEN. _ With JCL. in batch. Supply the required information, using JCL, and run DCLGEN

If you wish to invoke DCLGEN in the foreground, and your table names include DBCS characters, you need to use a terminal that can input and display double-byte characters. If you do not have such a terminal, you can enter DBCS character using the hex mode of ISPF edit. Subtopics 3.3.1 Invoking DCLGEN through DB2I 3.3.2 Including the Data Declarations in Your Program 3.3.3 DCLGEN Support of C, COBOL, and PL/I Languages 3.3.4 Example: Adding a Table Declaration and Host-Variable Structure to a Library 3.3.1 Invoking DCLGEN through DB2I The easiest way to invoke DCLGEN is through DB2I. Figure 14 shows the DCLGEN panel you reach by selecting option 2, DCLGEN, on the DB2I Primary Options Menu. For more instructions on using DB2I, see "Using ISPF and DB2 Interactive (DB2I)" in topic 4.2.7.1. +----------------------------------------------------------------------------------+ ¦ ¦ ¦ DSNEDP01 DCLGEN SSID: DSN ¦ ¦ ===> ¦ ¦ ¦ ¦ Enter table name for which declarations are required: ¦ ¦ 1 SOURCE TABLE NAME ===> (Unqualified table name) ¦ ¦ 2 TABLE OWNER ===> ¦ ¦ 3 AT LOCATION ..... ===> (Optional) ¦ ¦ ¦ ¦ Enter destination data set: (Can be sequential or partitioned) ¦ ¦ 4 DATA SET NAME ... ===> ¦ ¦ 5 DATA SET PASSWORD ===> (If password protected) ¦ ¦ ¦ ¦ Enter options as desired: ¦ ¦ 6 ACTION .......... ===> (ADD new or REPLACE old declaration) ¦ ¦ 7 COLUMN LABEL .... ===> (Enter YES for column label) ¦ ¦ 8 STRUCTURE NAME .. ===> (Optional) ¦ ¦ 9 FIELD NAME PREFIX ===> (Optional) ¦ ¦ 10 DELIMIT DBCS .... ===> (Enter YES to delimit DBCS identifiers) ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ PRESS: ENTER to process END to exit HELP for more information ¦ ¦ ¦ ¦ ¦ +----------------------------------------------------------------------------------+ Figure 14. DCLGEN Panel The following information explains the options on the DCGLEN panel and how to fill in the necessary fields in order to invoke the declarations generator. SOURCE TABLE NAME

Is the unqualified table name for which you want DCLGEN to produce SQL data declarations. The table can be stored at your DB2 location or at another DB2 location. To specify a table name at another DB2 location, enter the table qualifier in the TABLE OWNER field and the location name in the AT LOCATION field. DCLGEN generates a three-part table name from the SOURCE TABLE NAME, TABLE OWNER, and AT LOCATION fields. You can also use an alias for a table name. To specify a table name that contains special characters or blanks, enclose the name in apostrophes. For example, to specify a table named DON'S TABLE, enter the following: 'DON''S TABLE' DBCS table names do not have to be enclosed in apostrophes. The underscore is not considered a special character. named JUNE_PROFITS does not have to be enclosed in apostrophes. TABLE OWNER Is the table name qualifier. If you do not specify this value, and the table is a local table, DB2 assumes that the table qualifier is your TSO logon ID. If the table is at a remote location, you must specify this value. AT LOCATION Is the location of the table in an interconnected network. The AT keyword option is used to prefix the table name on the SQL DECLARE statement as follows: location_name.owner_id.table_name for example, PLAINS_GA.CARTER.CROP_YIELD_89 If no location is specified, then this option defaults to the local location name. This option applies to system-directed access only (that is, the location named must be another DB2 system). DATA SET NAME Is the data set name you allocated to contain the declarations that DCLGEN produces. It can be either sequential or partitioned. If this data set is password protected, you must supply the password in field 4. DATA SET PASSWORD Is the data set password for the data set specified in field 3 if the data set is password protected. ACTION Tells what to do with the output when it is sent to a partitioned data set. (The option is ignored if the data set you specified in field 3 is sequential.) ADD indicates that an old version of the output does not exist. A new member is created with the specified data set name. A table

REPLACE indicates that an old version is replaced if it already exists. COLUMN LABEL Includes labels declared on any columns of the table or view DCLGEN is operating on. If you enter YES in this field, and if the table or view for which you are creating declarations includes column labels, those labels is included as comments in the data declarations DCLGEN produces. Specifying NO causes DCLGEN to ignore any column labels it encounters. STRUCTURE NAME Names the generated data structure. The name can be up to 31 bytes in length. If the name is not a DBCS string, and the first character is not alphabetic, then the name must be enclosed in apostrophes. If you leave this field blank, DCLGEN generates a name that contains the table or view name with a prefix of DCL. If the language is COBOL or PL/I, and the table or view name consists of a DBCS string, the prefix consists of DBCS characters. If you use the C language, the letters you enter will not be folded to uppercase letters. FIELD NAME PREFIX Prefixes names that are generated for fields in the DCLGEN output. The value you choose can be up to 28 bytes in length and is used as the prefix for the field name. For example, if you choose ABCDE, the field names generated are ABCDE1, ABCDE2, and so on. If the name is a DBCS string, DBCS equivalents of the suffix numbers are generated. If you leave this field blank, the field names are the same as the column names in the table or view. If you use the C language, the letters you enter will not be folded to uppercase letters. DELIMIT DBCS Specifies whether to delimit DBCS table names and column names in the DCLGEN table declaration. If you enter YES, DBCS table and column names are surrounded by SQL delimiters. YES is the default. A table or column name in the DECLARE statement is generated as a non-delimited identifier unless at least one of the following is true: _ _ The name contains special characters and is not a DBCS string. The name is a DBCS string, and you have requested that DBCS names be delimited.

If you are using an SQL reserved word as an identifier, you must edit the DCLGEN output in order to add the appropriate SQL delimiters. 3.3.2 Including the Data Declarations in Your Program Use the following SQL INCLUDE statement to insert the table declaration

and COBOL record description produced through the DCLGEN process into your source program: EXEC SQL INCLUDE member name END-EXEC. For example, to include a description for the DSN8310.EMP table, code: EXEC SQL INCLUDE DECEMP END-EXEC. In this example, DECEMP is a name of a member of a partitioned data set that contains the table declaration and a corresponding COBOL record description of the DSN8310.EMP table. (A COBOL record description is a two-level host structure that corresponds to the columns of a table's row. Host structures are described in "Chapter 3-4. Embedding SQL Statements in Host Languages" in topic 3.4.) To get a current description of the table, use DCLGEN to generate the table's declaration and store it as member DECEMP in a library (usually a partitioned data set) just before you precompile the program. For various reasons, there are times when DCLGEN does not produce the results you expect. You might need to edit the results, tailoring the output to your specific needs. For example, DCLGEN does not generate columns that are named NOT NULL WITH DEFAULT. 3.3.3 DCLGEN Support of C, COBOL, and PL/I Languages Variable names provided by DCLGEN are derived from the source in the database. In Table 6, var represents variable names that are provided by DCLGEN when it is necessary to clarify the host language declaration. +--------------------------------------------------------------------------------------------------------------------+ ¦ Table 6. Declarations Generated by DCLGEN ¦ +--------------------------------------------------------------------------------------------------------------------¦ ¦ SQL Data Type ¦ C ¦ COBOL ¦ PL/I ¦ +---------------------+-----------------------+-------------------------------------------------+--------------------¦ ¦ SMALLINT ¦ short int ¦ PIC S9(4) ¦ BIN FIXED(15) ¦ ¦ ¦ ¦ USAGE COMP ¦ ¦ +---------------------+-----------------------+-------------------------------------------------+--------------------¦ ¦ INTEGER ¦ long int ¦ PIC S9(9) ¦ BIN FIXED(31) ¦ ¦ ¦ ¦ USAGE COMP ¦ ¦ +---------------------+-----------------------+-------------------------------------------------+--------------------¦ ¦ DECIMAL(p,s) or ¦ Not generated (no ¦ PIC S9(p-s)V9(s) ¦ DEC FIXED(p,s) ¦ ¦ NUMERIC(p,s) ¦ exact equivalent); ¦ USAGE COMP-3 ¦ If p>15, a warning ¦

¦ ¦ comment replaces ¦ If p>18, a warning ¦ is generated. ¦ ¦ ¦ the declaration. ¦ is generated. ¦ ¦ +---------------------+-----------------------+-------------------------------------------------+--------------------¦ ¦ REAL or ¦ float ¦ USAGE COMP-1 ¦ BIN FLOAT(n) ¦ ¦ FLOAT(n) ¦ ¦ ¦ ¦ ¦ 1 <= n <= 21 ¦ ¦ ¦ ¦ +---------------------+-----------------------+-------------------------------------------------+--------------------¦ ¦ DOUBLE PRECISION ¦ double ¦ USAGE COMP-2 ¦ BIN FLOAT(n) ¦ ¦ or FLOAT(n) ¦ ¦ ¦ ¦ +---------------------+-----------------------+-------------------------------------------------+--------------------¦ ¦ CHAR(1) ¦ char ¦ PIC X(1) ¦ CHAR(1) ¦ +---------------------+-----------------------+-------------------------------------------------+--------------------¦ ¦ CHAR(n) ¦ char var [n+1] ¦ PIC X(n) ¦ CHAR(n) ¦ +---------------------+-----------------------+-------------------------------------------------+--------------------¦ ¦ VARCHAR(n) ¦ struct ¦ 10 var. ¦ CHAR(n) VAR ¦ ¦ ¦ {short int var_len; ¦ 49 var_LEN PIC 9(4) USAGE COMP. ¦ ¦ ¦ ¦ char var_data[n]¦ 49 var_TEXT PIC X(n). ¦ ¦ ¦ ¦ } var; ¦ ¦ ¦ +---------------------+-----------------------+-------------------------------------------------+--------------------¦ ¦ GRAPHIC(n) ¦ Not generated (no ¦ PIC G(n) USAGE DISPLAY-1.(1) ¦ GRAPHIC(n) ¦ ¦ ¦ exact equivalent); ¦ or ¦ ¦ ¦ ¦ comment replaces ¦ PIC N(n).(1) ¦ ¦ ¦ ¦ declaration. ¦ ¦ ¦ +---------------------+-----------------------+-------------------------------------------------+--------------------¦ ¦ VARGRAPHIC(n) ¦ Not generated (no ¦ 10 var. ¦ GRAPHIC(n) VAR ¦ ¦ ¦ exact equivalent); ¦ 49 var_LEN PIC 9(4) USAGE COMP. ¦ ¦ ¦ ¦ comment replaces ¦ 49 var_TEXT PIC G(n) USAGE DISPLAY-1.(1) ¦ ¦ ¦ ¦ declaration. ¦ or ¦ ¦ ¦ ¦ ¦ 10 var. ¦ ¦ ¦ ¦ ¦ 49 var_LEN PIC 9(4) USAGE COMP. ¦ ¦ ¦ ¦ ¦ 49 var_TEXT PIC N(n).(1) ¦ ¦

+---------------------+-----------------------+-------------------------------------------------+--------------------¦ ¦ DATE ¦ char var[11](2) ¦ PIC X(10)(2) ¦ CHAR(10)(2) ¦ +---------------------+-----------------------+-------------------------------------------------+--------------------¦ ¦ TIME ¦ char var[9](3) ¦ PIC X(8)(3) ¦ CHAR(8)(3) ¦ +---------------------+-----------------------+-------------------------------------------------+--------------------¦ ¦ TIMESTAMP ¦ char var[27] ¦ PIC X(26) ¦ CHAR(26) ¦ +--------------------------------------------------------------------------------------------------------------------¦ ¦ Notes: ¦ ¦ ¦ ¦ 1. DCLGEN chooses the format based on the character you specify as the DBCS symbol on the COBOL Defaults panel. ¦ ¦ ¦ ¦ 2. This declaration is used unless there is a date installation exit for formatting dates, in which case the ¦ ¦ length is that specified for the LOCAL DATE LENGTH installation option. ¦ ¦ ¦ ¦ 3. This declaration is used unless there is a time installation exit for formatting times, in which case the ¦ ¦ length is that specified for the LOCAL TIME LENGTH installation option. ¦ +--------------------------------------------------------------------------------------------------------------------+ For further details about the DCLGEN subcommand, see Chapter 2 of Command and Utility Reference. 3.3.4 Example: Adding a Table Declaration and Host-Variable Structure to a Library

This example adds an SQL table declaration and a corresponding host-variable structure to a library. This example is based on the following scenario: _ _ _ _ _ The The The The The library name is prefix.TEMP.COBOL. member will be a new member named VPHONE. table is a local table named DSN8310.VPHONE. host-variable structure is for VS COBOL II. structure receives the default name DCLVPHONE.

Information you must enter is shown in boldface type. Subtopics 3.3.4.1 Step 1. Specify VS COBOL II as the Host Language 3.3.4.2 Step 2. Create the Table Declaration and Host Structure 3.3.4.3 Step 3. Examine the Results 3.3.4.1 Step 1. Specify VS COBOL II as the Host Language Select option D on the ISPF/PDF menu to display the DB2I Defaults panel. Specify COB2 as the application language as shown in Figure 15, and then

press Enter. Figure 16.

The COBOL Defaults panel is then displayed as shown in

Fill in the COBOL defaults panel as necessary and press Enter to save the new defaults, if any, and return to the DB2I Primary Option menu. +----------------------------------------------------------------------------------+ ¦ ¦ ¦ ¦ ¦ ¦ ¦ DSNEOP01 DB2I DEFAULTS ¦ ¦ COMMAND ===>_ ¦ ¦ ¦ ¦ Change defaults as desired: ¦ ¦ ¦ ¦ 1 DB2 NAME ............. ===> DSN (Subsystem identifier) ¦ ¦ 2 DB2 CONNECTION RETRIES ===> 0 (How many retries for DB2 connection¦ ¦ 3 APPLICATION LANGUAGE ===> COB2 (ASM/ASMH,C,COBOL/COB2,FORTRAN,PLI) ¦ ¦ 4 LINES/PAGE OF LISTING ===> 80 (A number from 5 to 999) ¦ ¦ 5 MESSAGE LEVEL ........ ===> I (Information, Warning, Error, Severe¦ ¦ 6 SQL STRING DELIMITER ===> DEFAULT (DEFAULT, ' or ") ¦ ¦ 7 DECIMAL POINT ........ ===> . (. or ,) ¦ ¦ 8 STOP IF RETURN CODE >= ===> 8 (Lowest terminating return code) ¦ ¦ 9 NUMBER OF ROWS ===> 20 (For ISPF Tables) ¦ ¦ ¦ ¦ 10 DB2I JOB STATEMENT: (Optional if your site has a SUBMIT exit) ¦ ¦ ===> //USRT001A JOB (ACCOUNT),'NAME' ¦ ¦ ===> //* ¦ ¦ ===> //* ¦ ¦ ===> //* ¦ ¦ ¦ ¦ ¦ ¦ PRESS: ENTER to process END to cancel HELP for more information ¦ ¦ ¦ ¦ ¦ +----------------------------------------------------------------------------------+ Figure 15. DB2I Defaults Panel--Changing the Application Language +----------------------------------------------------------------------------------+ ¦ ¦ ¦ ¦ ¦ ¦ ¦ DSNEOP02 COBOL DEFAULTS ¦ ¦ COMMAND ===>_ ¦ ¦ ¦ ¦ Change defaults as desired: ¦ ¦ ¦ ¦ 1 COBOL STRING DELIMITER ===> (DEFAULT, ' or ") ¦ ¦ 2 DBCS SYMBOL FOR DCLGEN ===> (G/N - Character in PIC clause) ¦ ¦ ¦ ¦ ¦ Figure 16. The COBOL Defaults Panel. Shown only if the application language is COBOL or COB2. 3.3.4.2 Step 2. Create the Table Declaration and Host Structure

Select option 2 on the DB2I Primary Option menu, and press Enter to display the DCLGEN panel. Fill in the fields as shown in Figure 17, and then press Enter. +----------------------------------------------------------------------------------+ ¦ ¦ ¦ DSNEDP01 DCLGEN SSID: DSN ¦ ¦ ===> ¦ ¦ Enter table name for which declarations are required: ¦ ¦ ¦ ¦ 1 SOURCE TABLE NAME ===> DSN8310.VPHONE ¦ ¦ 2 TABLE OWNER ===> ¦ ¦ 3 AT LOCATION ..... ===> (Location of table, optional) ¦ ¦ ¦ ¦ Enter destination data set: (Can be sequential or partitioned) ¦ ¦ 4 DATA SET NAME ... ===> TEMP(VPHONEC) ¦ ¦ 5 DATA SET PASSWORD ===> (If password protected) ¦ ¦ ¦ ¦ Enter options as desired: ¦ ¦ 6 ACTION .......... ===> ADD (ADD new or REPLACE old declaration) ¦ ¦ 7 COLUMN LABEL .... ===> NO (Enter YES for column label) ¦ ¦ 8 STRUCTURE NAME .. ===> (Optional) ¦ ¦ 9 FIELD NAME PREFIX ===> (Optional) ¦ ¦ 10 DELIMIT DBCS ===> YES (Enter YES to delimit DBCS identifiers) ¦ ¦ ¦ ¦ ¦ ¦ PRESS: ENTER to process END to exit HELP for more information ¦ ¦ ¦ +----------------------------------------------------------------------------------+ Figure 17. DCLGEN Panel--Selecting Source Table and Destination Data Set If the operation is successful, a message is displayed at the top of your screen as shown in Figure 18. +----------------------------------------------------------------------------------+ ¦ ¦ ¦ EXECUTION COMPLETE, MEMBER VPHONEC ADDED ¦ ¦ *** ¦ ¦ ¦ Figure 18. Successful Completion Message DB2 then displays the screen as shown in Figure 19. to the DB2I Primary Option menu. Press Enter to return

+----------------------------------------------------------------------------------+ ¦ ¦ ¦ DSNEDP01 DCLGEN SSID: DSN ¦ ¦ ===> ¦ ¦ DSNE294I SYSTEM RETCODE=000 USER OR DSN RETCODE=0 ¦ ¦ Enter table name for which declarations are required: ¦ ¦ 1 SOURCE TABLE NAME ===> DSN8310.VPHONE ¦ ¦ 2 TABLE OWNER ===> ¦

¦ 3 AT LOCATION ..... ===> (Location of table, optional) ¦ ¦ ¦ ¦ Enter destination data set: (Can be sequential or partitioned) ¦ ¦ 4 DATA SET NAME ... ===> TEMP(VPHONEC) ¦ ¦ 5 DATA SET PASSWORD ===> (If password protected) ¦ ¦ ¦ ¦ Enter options as desired: ¦ ¦ 6 ACTION .......... ===> ADD (ADD new or REPLACE old declaration) ¦ ¦ 7 COLUMN LABEL .... ===> NO (Enter YES for column label) ¦ ¦ 8 STRUCTURE NAME .. ===> (Optional) ¦ ¦ 9 FIELD NAME PREFIX ===> (Optional) ¦ ¦ 10 DELIMIT DBCS ===> (Enter YES to delimit DBCS identifiers) ¦ ¦ ¦ ¦ ¦ ¦ PRESS: ENTER to process END to exit HELP for more information ¦ ¦ ¦ +----------------------------------------------------------------------------------+ Figure 19. DCLGEN Panel--Displaying System and User Return Codes

3.3.4.3 Step 3. Examine the Results To browse or edit the results, first exit from DB2I by entering X on the command line of the DB2I Primary Option menu. The ISPF/PDF menu is then displayed, and you can select either the browse or the edit option to view the results. For this example, the data set to edit is prefix.TEMP.COBOL(VPHONEC), which is shown in Figure 20. ***** DCLGEN TABLE(DSN8310.VPHONE) *** ***** LIBRARY(SYSADM.TEMP.COBOL(VPHONEC)) *** ***** QUOTE *** ***** ... IS THE DCLGEN COMMAND THAT MADE THE FOLLOWING STATEMENTS *** EXEC SQL DECLARE DSN8310.VPHONE TABLE ( LASTNAME VARCHAR(15) NOT NULL, FIRSTNAME VARCHAR(12) NOT NULL, MIDDLEINITIAL CHAR(1) NOT NULL, PHONENUMBER VARCHAR(4) NOT NULL, EMPLOYEENUMBER CHAR(6) NOT NULL, DEPTNUMBER CHAR(3) NOT NULL, DEPTNAME VARCHAR(36) NOT NULL ) END-EXEC. ***** COBOL DECLARATION FOR TABLE DSN8310.VPHONE ****** 01 DCLVPHONE. 10 LASTNAME. 49 LASTNAME-LEN PIC S9(4) USAGE COMP. 49 LASTNAME-TEXT PIC X(15). 10 FIRSTNAME. 49 FIRSTNAME-LEN PIC S9(4) USAGE COMP. 49 FIRSTNAME-TEXT PIC X(12). 10 MIDDLEINITIAL PIC X(1). 10 PHONENUMBER. 49 PHONENUMBER-LEN PIC S9(4) USAGE COMP. 49 PHONENUMBER-TEXT PIC X(4). 10 EMPLOYEENUMBER PIC X(6). 10 DEPTNUMBER PIC X(3). 10 DEPTNAME.

49 DEPTNAME-LEN PIC S9(4) USAGE COMP. 49 DEPTNAME-TEXT PIC X(36). ***** THE NUMBER OF COLUMNS DESCRIBED BY THIS DECLARATION IS 7 Figure 20. DCLGEN Results Displayed in Edit Mode

******

REFER TO SLIDES FOR EGS EGS FROM CBT 4.1 Chapter 4-1. Designing a DB2 Database Application

As with any application, you must plan and design programs to meet the application's requirements. Designing a DB2 database application requires that you also plan for binding, locking, recovery, and perhaps for using distributed data. This chapter includes the following subjects: _ "Planning to Precompile and Bind" in topic 4.1.1 describes the DB2 bind process, the program preparation process, and methods of using packages within a plan. "Chapter 4-2. Preparing an Application Program to Run" in topic 4.2 provides specific details about controlling those processes. "Planning for Concurrency" in topic 4.1.2 describes what you might want your application to do about DB2 locks to make best use of the resources in an environment where other programs run concurrently. "Planning for Recovery" in topic 4.1.3 describes considerations for designing an application to recover from an interruption as quickly as possible. "Planning to Access Distributed Data" in topic 4.1.4 describes how to use DB2's system-directed access and application-directed access to access distributed data; it describes how each one might affect your program design. to Precompile and Bind for Concurrency for Recovery to Access Distributed Data

_

_

_

Subtopics 4.1.1 Planning 4.1.2 Planning 4.1.3 Planning 4.1.4 Planning

4.1.1 Planning to Precompile and Bind DB2 application programs include SQL statements. You cannot compile these programs until you change the SQL statements into language recognized by your compiler or assembler. To do this, you must communicate the SQL requests to DB2 by some other means. The DB2 precompiler does the following: _ _ Replaces the SQL statements in your source programs with compilable code. Creates a database request module (DBRM), which communicates your SQL

requests to DB2 during the bind process. The entire program preparation process is illustrated in Figure 21. "Chapter 4-2. Preparing an Application Program to Run" in topic 4.2 supplies specific details about accomplishing these steps. After you have precompiled your source program, you create a load module, possibly one or more packages, and an application plan. It does not matter which you do first. Creating a load module is similar to compiling and link-editing an application containing no SQL statements. Creating a package or an application plan, a process unique to DB2, involves binding one or more DBRMs. PICTURE 2 Figure 21. Program Preparation Overview. Two processes are performed: The compile and link-edit process, and the bind process. Subtopics 4.1.1.1 Planning to Precompile 4.1.1.2 Planning to Bind 4.1.1.3 Automatic Rebinding 4.1.1.1 Planning to Precompile The DB2 precompiler provides many options. Most of the options do not affect the way you design or code the program. They allow you to tell the precompiler what you have already done--for example, what host language you use or what value you depend on for the maximum precision of a decimal number. Or, they tell the precompiler what you want it to do--how many lines per page in the listing or whether you want a cross-reference report. In many cases, you may want to accept the default value provided. A few options, however, can affect the way you code. For example, you need to know if you are using NOFOR or STDSQL(86) before you begin coding. Before you begin coding, please review the list of options in Table 24 in topic 4.2.2.4. 4.1.1.2 Planning to Bind To access DB2 data, an SQL statement requires an access path. For dynamic SQL, such as statements issued through SPUFI, DB2 determines the access path when the statement executes. For statements that are not executed often, this method is usually acceptable. However, an application typically runs the same SQL statements repeatedly. In this case, determining the access path at execution time wastes system resources, because the same path must be determined repeatedly. To reduce use of system resources, the access paths used each time the statements execute can be established once through binding. Depending upon how you design your DB2 application, you might bind all your DBRMs in one operation, creating only a single application plan. Or, you might bind some or all of your DBRMs into separate packages in separate operations. After that, you must still perform a bind process for the entire application, listing the packages that are included and binding any DBRMs that are not bound into packages. Regardless of what the plan contains, you must bind a plan before the application can run.

Subtopics 4.1.1.2.1 4.1.1.2.2 4.1.1.2.3 4.1.1.2.4

Deciding How to Use Packages Deciding What to Put into a Plan Rebinding a Package Rebinding a Plan

4.1.1.2.1 Deciding How to Use Packages The question of how to use packages affects your application design from the beginning. For example, you might decide to put certain SQL statements together in the same program in order to precompile them into the same DBRM and then bind them into the same package. At its simplest, you can bind each DBRM into its own package. A one-to-one correspondence between programs and packages might easily allow you to keep track of each. However, your application could consist of too many packages to track easily. At the other extreme, you can bind all your DBRMs to a single plan. This approach has the disadvantage that, whenever your plan is rebound, the operation includes all of the DBRMs, even though not all of them have changed. For more information about rebinding, see"Automatic Rebinding" in topic 4.1.1.3 . You must decide how to use packages based on your application design and your operational objectives. Keep in mind the following: _ When using packages, the entire plan need not be rebound when a change is made to one SQL statement. Only the package associated with the changed statement has to be rebound. Binding packages allows you to add programs to an existing application plan without having to rebind the entire plan. Packages can be bound into a collection, which is a group of associated packages. When you bind the plan, you can associate an entire package collection with it; later, you can add other packages to the collection, or replace existing packages, without rebinding the plan. Binding DBRMs as packages helps you to support multiple versions of the same program. To maintain multiple versions in a plan without packages requires separate plans for each version, and therefore separate plan names and RUN commands. Isolating versions of a program by using packages allows you to maintain only one plan and helps to make program migration and fallback easier. For example, you could concurrently maintain development, test, and production levels of a program by binding each level of each program as a separate package, all within a single plan. Most options specified when binding the plan apply only to the DBRMs bound directly to the plan. You can use different options when you bind a package that is included in the plan. By using packages, you can use different qualifiers for SQL statements in different parts of your application. When you bind a package, you can name a qualifier for the unqualified object names in its DBRM. When you bind the plan, you can name a different qualifier for unqualified object names in DBRMs bound directly to the plan. Hence, by rebinding, you can redirect your SQL statements from, say, a test table to a production table.

_

_

_

_

+--- CICS -----------------------------------------------------------+ ¦ ¦ ¦ With packages, you probably do not need a dynamic plan selection ¦ ¦ and its accompanying exit routine. A package listed within a plan ¦ ¦ is not accessed until it is executed. However, it is possible to ¦ ¦ use dynamic plan selection and packages together. Doing so can ¦ ¦ reduce the number of plans in an application, and fewer plans can ¦ ¦ mean that less effort is required to maintain the dynamic plan ¦ ¦ exit). See "Using Packages with Dynamic Plan Selection" in ¦ ¦ topic 4.2.4.4 for information on using packages with dynamic plan ¦ ¦ selection. ¦ ¦ ¦ +--------------------------------------------------------------------+ Before you bind packages, consider the following: _ _ Develop a naming convention and strategy for the most effective and efficient use of your packages. Determine whether your application requires that all resources be acquired when the plan is allocated or when your program first uses the resources. DBRMs bound directly to a plan can acquire resources at either allocation or first use, depending on what you specify in the ACQUIRE option. Packages, by default, always acquire resources when they are first used.

4.1.1.2.2 Deciding What to Put into a Plan Input to binding the plan can include DBRMs only, a package list only, or a combination of the two. Binding All DBRMs to a Plan: Binding all DBRMs to a plan is suitable for small applications that are unlikely to change or that require all resources to be acquired when the plan is allocated rather than when your program first uses them. Binding with a Package List Only: Binding a plan that includes only a package list makes maintenance easier when the application will change significantly over time. Because different BIND options can be specified when binding each package, you can control such things as qualifiers and isolation level. Binding with Both DBRMs and a Package List: Binding DBRMs directly to the plan and specifying a package list is suitable for maintaining existing applications. You can add a package list when rebinding an existing plan. To migrate gradually to using packages, bind DBRMs as packages when some change is needed. 4.1.1.2.3 Rebinding a Package The following example shows the options for rebinding a package in the collection GROUP1. In the example below, the location ID is SNTERSA, the package ID is PROGA, and the version ID is V1. The connection types shown in the REBIND subcommand replace connection types that might have been specified during the original BIND operation. REBIND PACKAGE(SNTRESA.GROUP1.PROGA.(V1)) ENABLE(CICS,REMOTE)

The asterisk can also be used on the REBIND subcommand. In the following example, all packages in all collections at the local DB2 system are rebound. The asterisk does not apply to packages at remote sites. REBIND PACKAGE(*) 4.1.1.2.4 Rebinding a Plan If DBRMs for an application are bound directly to a plan, then the entire plan must be recreated using BIND with the REPLACE option. 4.1.2 Planning for Concurrency DB2 allows more than one application process to access the same data at essentially the same time. This is known as concurrency, and it cannot be provided without cost. To control such undesirable effects as lost updates, access to uncommitted data, and unrepeatable reads, concurrency must be controlled. Lost updates. Without concurrency control, two processes, A and B, might both read the same row from the database, and both calculate new values for one of its columns, based on what they read. If A updates the row with its new value, and then B updates the same row, A's update is lost. Access to uncommitted data. Additionally, without concurrency control, process A might update a value in the database, and process B might read that value before it was committed. Then, if A's value is not later committed, but backed out, B's calculations are based on uncommitted (and presumably incorrect) data. Unrepeatable reads. Some processes require the following sequence of events: A reads a row from the database and then goes on to process other SQL requests. Later, A reads the first row again and must find the same values it read the first time. Without control, process B could have changed the row between the two reads. To prevent those situations from occurring, DB2 uses locks to control concurrency. A lock associates a DB2 resource with an application process in a way that affects how other processes can access the same resource. The process associated with the resource is said to "hold" or "own" the lock. DB2 uses locks to ensure that no process accesses data that has been changed, but not yet committed, by another process. To preserve data integrity, a process acquires locks implicitly, that is, under DB2 control. It is never necessary for a process to request a lock explicitly to conceal uncommitted data. Therefore, sometimes you need not do anything about DB2 locks. Nevertheless, processes acquire locks on the basis of certain general parameters; you can make better use of your resources and improve concurrency by understanding the effects of those parameters. There are two major classes of locks in DB2. Transaction locks are used primarily to control access by SQL statements. Those locks are the ones over which you have the most control, and the ones we describe in most detail. Hereafter, lock (with no qualifier) refers to transaction lock.

Drain locks are used to A means called draining partial or full control concurrent access. For utilities and commands, Guide.

control access by DB2 utilities and commands. allows utilities and commands to acquire of a needed object with least interruption to information on concurrency control by see Section 7 (Volume 3) of Administration

DB2 transaction locks can be compared in four basic aspects: size, duration, and mode. Subtopics 4.1.2.1 The Object of a Lock 4.1.2.2 The Size of a Lock 4.1.2.3 The Duration of a Lock 4.1.2.4 The Mode of a Lock 4.1.2.5 Lock Compatibility 4.1.2.6 Effects of DB2 Locks 4.1.2.7 Specifying the Bind Options that Affect Locking 4.1.2.8 Explicitly Locking a Table 4.1.2.9 Access Paths

object,

4.1.2.1 The Object of a Lock The object of a lock is the resource being locked. You have most control over locks on user data in tables. But there are also locks on DB2 internal objects. Most of those you are never aware of, but sometimes you have to consider locks on: _ _ The skeleton cursor table (SKCT) representing an application plan and the skeleton package table (SKPT) representing a package The database descriptor (DBD) representing each DB2 database.

Locks on data include some items occasionally overlooked. A lock on user data also can control indexes on that data. Some operations that lock user data also lock portions of the DB2 catalog. Also, operations subject to referential constraints can require locks on related tables. For example, if you try to delete a row from a parent table, DB2 might have to delete rows from the dependent table as well. If you try to update a primary key column of a parent row, DB2 has to check whether a dependent row exists. In both those cases, DB2 must lock and read data in the dependent table as well as the parent table. (For more information on referential constraints, see Section 4 (Volume 2) of Administration The maximum size of a Tablespace can be 64Gb. It is the storage unit for Recovery and Reorganisation. A large Tablespace can be partitioned. In that case, the unit of Recovery and Reorg. is each individual partition. There are three types of Tablespaces : Simple Tablespace Partitioned Tablespace Segmented Tablespace Simple Tablespaces Generally it contains a single table. However it can contain multiple tables. It is possible to store the records from related tables together, in an interleaved fashion. This particularly improves the performance of table join queries. However the queries involving sequential tablespace scans will suffer.

Partitioned Tablespaces A Partitioned Tablespace contains exactly 1 table. Very large tables are generally partitioned. The partitioning is done on the value ranges of a column or combination of columns. Each individual partition can be individually recovered or reorganised. Each partition can be associated to independent storage groups and thus stored on different DASD volumes to reduce the I/O load. Segmented Tablespaces A Segmented Tablespace can contain multiple tables. However the cross clustering of tables is not allowed. A Table can span across multiple Segments. One Segment can contain records from a single Table only. Segment size can be defined while defining the table using SEGSIZE parameter. It has to be a multiple of 4 pages ranging from 4 to 64 pages. 4.1.2.2 The Size of a Lock Size (sometimes scope or level) applies only to locks on data in tables. It refers to the amount of data controlled. As Figure 22 shows, the same piece of data can be controlled by locks of different sizes. A table space lock (the largest size) controls the most data, a page lock (the smallest) controls the least. Simple table space or Segmented table space partitioned table space +---------------------------+ +---------------------------+ ¦ ¦ ¦ ¦ ¦ Table space lock ¦ ¦ Table space lock ¦ +---------------------------+ +---------------------------+ ¦ ¦ +---------------+ ¦ ¦ ¦ ¦ ¦ Table lock ¦ ¦ +---------------+ ¦ ¦ ¦ +---------+ +---------+ ¦ Page ¦ ¦ Page ¦ ¦ lock ¦ ¦ lock ¦ +---------+ +---------+ Figure 22. Sizes of Objects Locked Locking larger or smaller amounts of data allows you to trade performance for concurrency. When page locks are used: _ _ Concurrency improves, meaning better response times and higher throughput rates for many users. Processing time and use of storage increases. That is especially evident in batch processes that scan or update a large number of pages.

When only table or table space locks are used: _ _ Processing time and storage usage is reduced. Concurrency can be reduced, meaning longer response times for some users but better throughput for one user.

The principal means of controlling the size of locks are:

_ _ _

Installation options. For guidance on using the applicable options, see Section 7 (Volume 3) of Administration Guide. The LOCKSIZE clause of the CREATE and ALTER TABLESPACE statements. Its use is also described in Section 7 (Volume 3) of Administration Guide. The SQL LOCK TABLE statement, described in "Explicitly Locking a Table" in topic 4.1.2.8.

More Than One Table in a Table Space: In a segmented table space, a lock can apply to the entire table space, to a single table, or to a single page. In a simple table space or a partitioned table space, a lock can apply only to the entire table space or to a single page. In a simple table space, a single page can contain rows from more than one table. A lock on the page or on the entire table space locks all the data in the page or table space, no matter what table the data belongs to. A lock needed to access data from one table can make data from other tables temporarily unavailable. In a segmented table space, however, rows from different tables are contained in different pages. Hence, locking a page in a segmented table space does not lock data from more than one table. Partitioned Table Spaces: A partitioned table space is locked by locking each of the partitions. Utility jobs can control separate partitions of a table space or index space.

4.1.2.3 The Duration of a Lock The duration of a lock is the length of time the lock is held. It varies according to when the lock is acquired and when it is released. Duration of Table and Table Space Locks: Table and table space locks can be acquired when a plan is first allocated, or you can delay acquiring them until the resource they lock is first used. For the principal means of controlling the duration of these locks, see: "The ACQUIRE and RELEASE Options" in topic 4.1.2.7.1. Duration of Page Locks: Page locks are acquired when the page is first accessed. When they are released depends on many factors. For details on controlling the duration of page locks, see "The ISOLATION Option" in topic 4.1.2.7.2. 4.1.2.4 The Mode of a Lock The mode (sometimes state) of a lock tells what access to the locked object is permitted to the lock owner and to any concurrent application processes. Figure 23 shows the possible modes for page locks; Figure 24 shows the modes for table and table space locks. When a page is locked, the table or table space containing it is also locked. In that case, the table or table space lock has one of the intent modes: IS, IX, or SIX. (In contrast, the modes S, U, and X of table and table space locks are

sometimes called gross modes.) Locks on Indexes: The transaction lock acquired on a table space applies equally to all indexes on all tables in the table space. The lock acquired on a table in a segmented table space applies to all indexes on the table. Thus, if an application process has an S or X lock on a table space, the indexes are also locked in S or X mode and index pages or subpages are not locked separately. If the process has an IS, IX, or SIX lock on the table space, particular index pages or subpages can be locked separately. Utilities do not typically require transaction locks. They can acquire control on a table space without locking its indexes, or on an index without locking the table space, through the claim and drain mechanisms. Thus, some utilities can operate concurrently with other utilities, or with SQL access (possibly read-only). For more specific information, see Section 7 (Volume 3) of Administration Guide and the descriptions of particular utilities in Chapter 3 of Command and Utility Reference. +------------------------------------------------------------------------+ ¦ ¦ ¦ Page Lock Modes ¦ ¦ ¦ ¦ Modes and their effects are listed in the order of increasing control ¦ ¦ over resources. ¦ ¦ ¦ ¦ S (SHARE) ¦ ¦ The lock owner and any concurrent processes can read, but not ¦ ¦ change, the locked page. Concurrent processes can acquire S or ¦ ¦ U locks on the page or might read data without acquiring a page ¦ ¦ lock. ¦ ¦ ¦ ¦ U (UPDATE) ¦ ¦ The lock owner can read, but not change, the locked page; ¦ ¦ however, the owner can promote the lock to an X lock and then ¦ ¦ can change the page. Processes concurrent with the U lock can ¦ ¦ acquire S locks and read the page, but no concurrent process can ¦ ¦ acquire a U lock. ¦ ¦ ¦ ¦ U locks reduce the chance of deadlocks when the lock owner is ¦ ¦ reading a page to determine whether to change it. If the lock ¦ ¦ is promoted to mode X, that lock is held at least until a commit ¦ ¦ point. But if the lock is not promoted, it might be released ¦ ¦ before the commit point. ¦ ¦ ¦ ¦ X (EXCLUSIVE) ¦ ¦ The lock owner can read or change the locked page. Concurrent ¦ ¦ processes cannot acquire any lock on the page nor can they ¦ ¦ access the locked page. ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ +------------------------------------------------------------------------+ Figure 23. Modes of Page Locks +------------------------------------------------------------------------+ ¦ ¦ ¦ Table and Table Space Lock Modes ¦

¦ ¦ ¦ Modes and their effects are listed in the order of increasing control ¦ ¦ over resources. ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ IS (INTENT SHARE) ¦ ¦ The lock owner can read data in the table or table space, but ¦ ¦ not change it. Concurrent processes can both read and change ¦ ¦ the data. The lock owner might acquire a page lock on any data ¦ ¦ it reads. ¦ ¦ ¦ ¦ IX (INTENT EXCLUSIVE) ¦ ¦ The lock owner and concurrent processes can read and change data ¦ ¦ in the table or table space. The lock owner must acquire a page ¦ ¦ lock on any data it reads or changes. ¦ ¦ ¦ ¦ S (SHARE) ¦ ¦ The lock owner and any concurrent processes can read, but not ¦ ¦ change, data in the table or table space. The lock owner does ¦ ¦ not need page locks on data it reads. ¦ ¦ ¦ ¦ U (UPDATE) ¦ ¦ The lock owner can read, but not change, the locked data; ¦ ¦ however, the owner can promote the lock to an X lock and then ¦ ¦ can change the data. Processes concurrent with the U lock can ¦ ¦ acquire S locks and read the data, but no concurrent process can ¦ ¦ acquire a U lock. The lock owner does not need page locks. ¦ ¦ ¦ ¦ U locks reduce the chance of deadlocks when the lock owner is ¦ ¦ reading data to determine whether to change it. ¦ ¦ ¦ ¦ SIX (SHARE with INTENT EXCLUSIVE) ¦ ¦ The lock owner can read and change data in the table or table ¦ ¦ space. Concurrent processes can read data in the table or table ¦ ¦ space, but not change it. Only when the lock owner changes ¦ ¦ data, does it acquire page locks. ¦ ¦ ¦ ¦ X (EXCLUSIVE) ¦ ¦ The lock owner can read or change data in the table or table ¦ ¦ space. No concurrent process can access the data. The lock ¦ ¦ owner does not need page locks. ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ +------------------------------------------------------------------------+ Figure 24. Modes of Table and Table Space Locks

4.1.2.5 Lock Compatibility As Figure 23 in topic 4.1.2.4 and Figure 24 in topic 4.1.2.4 show, locks of some modes do not shut out all other users. Assume that application process A holds a lock on a table space that process B also wants to access. DB2 requests, on behalf of B, a lock of some particular mode. If the mode of A's lock permits B's request, the two locks (or modes) are said to be compatible.

If the two locks are not compatible, B cannot proceed. It must wait until A releases its lock. (And, in fact, it must wait until all existing incompatible locks are released.) Compatibility for page locks is easy to define: Table 19 shows whether locks of any two modes are compatible (Yes) or not (No). +------------------------------+ ¦ Table 19. Compatibility of ¦ ¦ Page Lock Modes ¦ +------------------------------¦ ¦ Page ¦ ¦ ¦ ¦ ¦ Lock Mode¦ S ¦ U ¦ X ¦ +------------+-----+-----+-----¦ ¦ S ¦ Yes ¦ Yes ¦ No ¦ +------------+-----+-----+-----¦ ¦ U ¦ Yes ¦ No ¦ No ¦ +------------+-----+-----+-----¦ ¦ X ¦ No ¦ No ¦ No ¦ +------------------------------+ REFER NOTES PROGRAM PREPARATION (C.J.DATE)

Sign up to vote on this title
UsefulNot useful