Sybase
Cursors
SQL
is a set based language. This is excellent for many things but
sometimes you want to step through a table processing one row at a time. An
example would be if you were updating two or more tables,
conditionally, from a single table. It might be possible to come up
with acceptable set queries to perform the task but doing it in a
linear manner gives far more control, albeit at the cost of greater
processing time for the job.
Cursors
provide a mechanism for dealing with data one row at a time.
In essence, you must declare the cursor and memory variables in which
to store the table's columns; create some form of control loop and use
the fetch
command to populate the memory variables. Here's a very simplistic
example...
DECLARE
@Salesman CHAR(20),
@SalesCount INT,
@LastUpdate DATETIME
DECLARE SalesRead CURSOR
FOR
SELECT employee,
running_total,
update
FROM sales_control sc1
WHERE update = (SELECT max(update)
FROM
sales_control sc2
WHERE sc2.employee = sc1.employee)
OPEN SalesRead
FETCH SalesRead INTO @Salesman,
@SalesCount INT,
@LastUpdate
WHILE (@@sqlstatus=0)
BEGIN
-- Do whatever processing is required
:
:
:
-- last thing in the loop is to fetch next row...
FETCH SalesRead INTO @Salesman,
@SalesCount INT,
@LastUpdate
END
CLOSE SalesRead
So,
What's Happening
Here?
First we
declared the variables we would use to hold the output from the cursor
and then we declared the cursor itself. Note that a cursor may be based
on practically any legal read query and it's when there's a need to
process data based on very complex queries that cursors come into their
own.
Then we opened the cursor and read the first result row into the
variables. At this point, Sybase sets @@sqlstatus to report what
happened when we did the read. 0 means that the read succeeded.
We use this condition as the test for the loop which we now enter.
Within the loop, we can carry out any legal Sybase processing. The
important thing to note is that the final task within the loop is to
read the next row from the cursor.
When there are no more rows to process, @@sqlstatus will be non-zero so
we'll fall out of the loop. Logically, the thing to do here is to close
the cursor and then proceed with any further processing.
Wheels Within Wheels.
Cursors and loops may be nested so you can legitimately do
something like the following:
DECLARE
variables
DECLARE cursor1
DECLARE cursor2
OPEN cursor1
FETCH cursor1 INTO variables
WHILE (@@sqlstatus=0)
BEGIN
OPEN cursor2
FETCH cursor2 into variables
WHILE (@@sqlstatus=0)
BEGIN
-- Do whatever processing is required
:
:
:
FETCH
cursor2 INTO variables
END
CLOSE cursor2
-- Do processing for main cursor
:
:
:
FETCH
cursor1 INTO variables
END
CLOSE cursor1
Provided you keep
firmly in mind what cursor is performing which action you may nest as
deeply as the permitted level of loop nesting for your version of
Sybase. The problems arise when you lose track of what's going on,
which is easily done if you are not fanatical about indentation and
comments!
Advantages and
Disadvantages.
Cursors are sometimes the only practical way of translating business
requirements into code and when they are useful they are invaluable.
Cursors are slow in Sybase and they can massively extend the run time
for large batches. Also, they add more than one level of complexity,
which is often made worse by the fact that cursors are, generally, at
their most useful when dealing with exceptionally complex business
logic.
Used with care, cursors can be a useful weapon in your armoury but used
carelessly they can bite you hard!
Find out more
by searching Google here...
|