Optimizing scrollable cursors
A programming pattern to get fresh data from scrollable cursors.
Generally, when using scrollable cursors, the database server or
the database client software (i.e. the application) will
make a static copy of the result set produced by the SELECT
statement.
For example, when using an IBM®
Informix® database
engine, each scrollable cursor will create a temporary table
to hold the result set. Thus, if the SELECT
statement returns all columns of the table(s) in the FROM
clause,
the database software will make a copy of all these values.
This practice has two disadvantages: A lot of resources are
consumed, and the data is static.
A good programming pattern to save resources and always get fresh
data from the database server is to declare two cursors based
on the primary key usage, if the underlying database table
has a primary key (or unique index constraint): The first cursor
must be a scrollable cursor that executes the SELECT
statement,
but returns only the primary keys. The SELECT
statement
of this first cursor is typically assembled at runtime with
the where-part produced by a CONSTRUCT
interactive
instruction, to give a subset of the rows stored in the database.
The second cursor (actually, a PREPARE
/EXECUTE
statement
handle) performs a single-row SELECT
statement
listing all columns to be fetched for a given record, based
on the primary key value of the current row in the scrollable cursor
list. The second statement must use a ? question mark place
holder to execute the single-row SELECT
with
the current primary key as SQL parameter.
If the primary key SELECT
statement needs to be
ordered, check that the database engine allows that columns used in
the ORDER BY
clause do not need to appear in the SELECT
list.
For example, this was the case with IBM Informix servers prior to version
9.4. If needed, the SELECT
list can be completed
with the columns used in ORDER BY
, you can then just
list the variable that holds the primary key in the INTO
clause
of FETCH
.
Note also that the primary key result set is static. That
is, if new rows are inserted in the database or if rows referenced
by the scroll cursor are deleted after the scroll cursor was opened,
the result set will be outdated. In this case, you can refresh the
primary key result set by re-executing the scroll cursor with CLOSE
/OPEN
commands.
MAIN
DEFINE wp VARCHAR(500)
DATABASE test1
-- OPEN FORM / DISPLAY FORM with c_id and c_name fields
...
-- CONSTRUCT generates wp string...
...
LET wp = "c_name LIKE 'J%'"
DECLARE clist SCROLL CURSOR FROM "SELECT c_id FROM customer WHERE " || wp
PREPARE crec FROM "SELECT * FROM customer WHERE c_id = ?"
OPEN clist
MENU "Test"
COMMAND "First" CALL disp_cust("F")
COMMAND "Next" CALL disp_cust("N")
COMMAND "Previous" CALL disp_cust("P")
COMMAND "Last" CALL disp_cust("L")
COMMAND "Refresh" CLOSE clist OPEN clist
COMMAND "Quit" EXIT MENU
END MENU
FREE crec
FREE clist
END MAIN
FUNCTION disp_cust(m)
DEFINE m CHAR(1)
DEFINE rec RECORD
c_id INTEGER,
c_name VARCHAR(50)
END RECORD
CASE m
WHEN "F" FETCH FIRST clist INTO rec.c_id
WHEN "N" FETCH NEXT clist INTO rec.c_id
WHEN "P" FETCH PREVIOUS clist INTO rec.c_id
WHEN "L" FETCH LAST clist INTO rec.c_id
END CASE
INITIALIZE rec.* TO NULL
IF SQLCA.SQLCODE == NOTFOUND THEN
ERROR "You reached to top or bottom of the result set."
ELSE
EXECUTE crec USING rec.c_id INTO rec.*
IF SQLCA.SQLCODE == NOTFOUND THEN
ERROR "Row was not found in the database, refresh the result set."
END IF
END IF
DISPLAY BY NAME rec.*
END FUNCTION