Previous Topic

Next Topic

Data Manipulation with Cursors

Cursors enable embedded SQL programs to process, one at a time, the result rows returned by a select statement. After a cursor has been opened, it can be advanced through the result rows. When the cursor is positioned to a row, the data in the row can be transferred to host language variables and processed according to the requirements of the application. The row to which the cursor is positioned is referred to as the current row.

A typical cursor application uses SQL statements to perform the following steps:

  1. Declare a cursor that selects a set of rows for processing.
  2. Open the cursor, thereby selecting the data.
  3. Fetch each row from the result set and move the data from the row into host language variables.
  4. Update or delete the current row.
  5. Close the cursor and terminate processing.

Previous Topic

Next Topic

Example: Cursor Processing

The following is an example of cursor processing:

exec sql include sqlca;

exec sql begin declare section;
     name          character_string(15);
     salary        float;
exec sql end declare section;

exec sql whenever sqlerror stop;

exec sql connect personnel;

exec sql declare c1 cursor for
          select ename, sal
          from employee
          for update of sal;

exec sql open c1;

exec sql whenever not found goto closec1;
loop while more rows
/* The WHENEVER NOT FOUND statement causes
     the loop to be broken as soon as a row
     is not fetched. */

exec sql fetch c1 into :name, :salary;

print name, salary;

if salary less than 10000 then
          exec sql update employee
               set salary = 10000
               where current of c1;
end if;
end loop;

closec1:

exec sql close c1;

exec sql disconnect;

Previous Topic

Next Topic

Cursor Declarations

Before using a cursor in an application, the cursor must be declared. The syntax for declaring a cursor is:

exec sql declare cursor_name cursor for

select_statement;

The declare cursor statement assigns a name to the cursor and associates the cursor with a select statement to be used to retrieve data. A cursor is always associated with a select statement. The select is executed when the cursor is opened. Updates can be performed only if the cursor select statement refers to a single table (or updateable view) and does not include any of the following elements:

These elements can be present in subselects within the select statement, but must not occur in the outermost select statement.

The cursor_name can be specified using a string literal or a host language string variable. The cursor name cannot exceed 32 characters, and can be assigned dynamically. For details, see Summary of Cursor Positioning.

Previous Topic

Next Topic

Open Cursors

To open a cursor, use the open statement:

exec sql open cursor_name [for readonly];

Opening a cursor executes the associated select statement. The rows returned by the select statement are stored in a temporary result set. The cursor is positioned before the first row in the result table.

Note: If a cursor is closed and reopened, the cursor is repositioned to the beginning of the result table, and does not resume the position it had before it was closed.

Previous Topic

Next Topic

Readonly Cursors

Readonly cursors specify that the data does not intend to be updated. The for readonly clause can be specified even if the cursor was declared for update; if this is done, updates on the data cannot be performed.

To improve performance, the DBMS Server pre-fetches (buffers) rows for readonly cursors. Use the set_sql(prefetchrows) statement to disable prefetching or to specify the number of rows to prefetch. To determine the number of rows that is prefetched for a particular readonly cursor, open the cursor, issue the inquire_sql(prefetchrows) statement.

By default the DBMS Server calculates the number of rows it can prefetch, taking into consideration the size of the row being fetched and the dimensions of internal buffers. If, using set_sql(prefetchrows), a value larger than the maximum number of rows the DBMS Server can prefetch is specified, prefetchrows is reset to its calculated maximum.

Note: Prefetchrows cannot be set for readonly cursors that return long varchar or long byte columns.

Previous Topic

Next Topic

Open Cursors and Transaction Processing

Cursors affect transaction processing as follows:

Previous Topic

Next Topic

Fetch Data From Cursor

The fetch statement advances the position of the cursor through the result rows returned by the select. Using the fetch statement, your application can process the rows one at a time. The syntax of the fetch statement is:

exec sql fetch cursor_name

into variable{, variable};

The fetch statement advances the cursor to the first or next row in the set, and loads the values indicated in the select clause of the declare cursor statement into host language variables.

To illustrate, the example of cursor processing shown previously contains the following declare cursor statement:

exec sql declare c1 cursor for
     select ename, sal
     from employee
     for update of sal;

Later in the program, the following fetch statement appears:

exec sql fetch c1 into :name, :salary;

This fetch statement puts the values from the columns, ename and sal, of the current row into the host language variables, name and salary.

Because the fetch statement operates on a single row at a time, it is ordinarily placed inside a host language loop.

You can detect when you have fetched the last row in the result table in the following ways:

Cursors can only move forward through a set of rows. To refetch a row, close and reopen a cursor.

Previous Topic

Next Topic

Fetch Rows Inserted by Other Queries

While a cursor is open, your application can append rows using non-cursor insert statements. If rows are inserted after the current cursor position, the rows are or are not be visible to the cursor, depending on the following criteria:

Previous Topic

Next Topic

Using Cursors to Update Data

To use a cursor to update rows, specify the for update clause when declaring the cursor:

exec sql declare cursor_name cursor for

select_statement

for [deferred | direct] update from column1 {,column};

The for update of clause must list any columns in the selected database table that are intended to be updated. Columns cannot be updated unless they have been declared for update. To delete rows, the cursor does not need to be declared for update.

The cursor update statement has the following syntax:

exec sql update tablename

set column= expression {, column = expression}

where current of cursor_name;

Previous Topic

Next Topic

Cursor Modes

There are two cursor modes: direct and deferred. The default cursor mode is specified when the DBMS Server is started. The default for ANSI/ISO Entry SQL-92 compliance is direct mode.

Previous Topic

Next Topic

Direct Mode for Update

Direct mode allows changes to be seen by the program before the cursor is closed. In direct mode, if a row is updated with a value that causes the row to move forward with respect to the current position of the cursor (for example, a key column is updated), the program sees this row again and takes appropriate steps to avoid reprocessing that row.

Previous Topic

Next Topic

Deferred Mode for Update

In a Deferred Mode, changes made to the current row of a cursor are not visible to the program that opened the cursor until the cursor is closed. Transaction behavior, such as the release of locks and external visibility to other programs, is not affected if deferred update is used. There can be only one cursor open for update in deferred mode at any given time.

Previous Topic

Next Topic

Cursor Position for Updates

The where clause of the cursor version specifies the row to which the cursor currently points. The update affects only data in that row. Each column referenced in the set clause must have been previously declared for updating in the declare cursor statement.

The cursor must be pointing to a row (a fetch has been executed) before a cursor update is performed. The update statement does not advance the cursor; a fetch is required to move the cursor forward one row. Two cursor updates not separated by a fetch generally cause the same row to be updated twice if the cursor was opened in direct mode, or cause an error if the cursor was opened in deferred mode.

Previous Topic

Next Topic

Delete Data Using Cursors

The cursor version of the delete statement has the following syntax:

exec sql delete from tablename where current of cursor_name;

The delete statement deletes the current row. The cursor must be positioned on a row (as the result of a fetch statement) before a cursor delete can be performed. After the row is deleted, the cursor points to the position after the row (and before the next row) in the set. To advance the cursor to the next row, issue the fetch statement.

A cursor does not have to be declared for update to perform a cursor delete.

Previous Topic

Next Topic

Example: Updating and Deleting with Cursors

The following example illustrates updating and deleting with a cursor:

exec sql include sqlca;

exec sql begin declare section;
     name          character_string(15);
     salary        float;
exec sql end declare section;

exec sql whenever sqlerror stop;

exec sql connect personnel;

exec sql declare c1 cursor for
     select ename, sal
     from employee
     for update of sal;

exec sql open c1;

exec sql whenever not found goto closec1;

loop while more rows

exec sql fetch c1 into :name, :salary;
          print name, salary;

/* Increase salaries of all employees earning
          less than 60,000. */

if salary < 60,000 then

          print 'Updating ', name;
          exec sql update employee
               set sal = sal * 1.1
                    where current of c1;

/* Fire all employees earning more than
          300,000. */

else if salary > 300,000 then

          print 'Terminating ', name;
          exec sql delete from employee
               where current of c1;
end if;

end loop;

closec1:

     exec sql close c1;

     exec sql disconnect;

Previous Topic

Next Topic

Closing Cursors

To close a cursor, issue the close cursor statement:

exec sql close cursor_name;

After the cursor is closed, no more processing can be performed with it unless another open statement is issued. The same cursor can be opened and closed any number of times in a single program. A cursor must be closed before it can be reopened.

Previous Topic

Next Topic

Summary of Cursor Positioning

The following table summarizes the effects of cursor statements on cursor positioning:

Statement

Effect on Cursor Position

Open

Cursor positioned before first row in set.

Fetch

Cursor moves to next row in set. If it is already on the last row, the cursor moves beyond the set and its position becomes undefined.

Update(cursor)

Cursor remains on current row.

Delete(cursor)

Cursor moves to a position after the deleted row (but before the following row).

Close

Cursor and set of rows become undefined.

For extended examples of the use of cursors in embedded SQL, see the Embedded SQL Companion Guide.

Previous Topic

Next Topic

Dynamically Specifying Cursor Names

A dynamically specified cursor name (a cursor name specified using a host string variable) can be used to scan a table that contains rows that are related hierarchically, such as a table of employees and managers.

In a relational database, this tree structure is represented as a relationship between two columns. In an employee table, employees are assigned an ID number. One of the columns in the employee table contains the ID number of each employee's manager. The ID number column establishes the relationships between employees and managers.

To use dynamically specified cursor names to scan this kind of table, do the following:

The following example retrieves rows from the employee table, which has the following format:

exec sql declare employee table

     (ename          varchar(32),

     title           varchar(20),

     manager         varchar(32));

This program scans the employee table and prints out all employees and the employees that they manage.

/* This program will print out, starting with

** the top manager, each manager and who they

** manage for the entire company. */

exec sql include sqlca;

/* main program */

exec sql begin declare section;

     manager character_string(32)

exec sql end declare section;

exec sql connect dbname;

exec sql whenever not found goto closedb;

exec sql whenever sqlerror call sqlprint;

/* Retrieve top manager */

exec sql select ename into :topmanager

     from employee

     where title = 'President';

/* start with top manager */

print "President", topmanager

call printorg(1, "President");

closedb:

exec sql disconnect;

/* This subroutine retrieves and displays employees who report to a given manager. This subroutine is called recursively to determine if a given employee is also a manager and if so, it will display who reports to them.

*/

subroutine printorg(level, manager)

level integer;

exec sql begin declare section;

     manager character_string(32)

     ename character_string(32)

     title character_string(20);

exec sql end declare section;

/* set cursor name to 'c1', 'c2', … */

cname = 'c' + level

exec sql declare :cname cursor for

     select ename, title, manager from employee

          where manager = :manager

          order by ename;

exec sql whenever not found goto closec;

exec sql open :cname;

loop

     exec sql fetch :cname into :ename, :title,

          :manager;

/* Print employee's name and title */

          print title, ename

/* Find out who (if anyone) reports to this

     employee */

          printorg(level+1, ename);

end loop

closec:

exec sql close :cname;

Previous Topic

Next Topic

Cursors versus Select Loops

A select loop is a block of code associated with an embedded select statement; the select loop processes the rows returned by the select. Select loops are typically used when the select returns more than one row.

The select loop is an enhancement of standard SQL. ANSI SQL does not allow more than one row to be retrieved by a select statement. If multiple rows are to be retrieved, the ANSI standard requires the use of a cursor even if the rows are not updated.

Cursors enable an application to retrieve and manipulate individual rows without the restriction of the select loop. Within a select loop, statements cannot be issued that access the database. Use cursors in the following situations:

The following two examples do the same thing. The first example uses a select loop and the second uses a cursor. Because there are no nested updates and only one result table is being processed, the select method is preferred.

//Select Loop Version

exec sql select ename, eno, sal
    into :name, :empnum, :salary
    from employee
    order by ename;

    exec sql begin;
    print name, salary, empnum;
    exec sql end;

//Cursor Version

exec sql declare c1 cursor for
    select ename, eno, sal/* No INTO clause */
    from employee
    order by ename;

    exec sql open c1;
    exec sql whenever not found goto closec1;

    loop while more rows
    exec sql fetch c1 into :name, :salary, :empnum;
    print name, salary, empnum;
    end loop;

    closec1:
    exec sql close c1;


© 2007 Ingres Corporation. All rights reserved.