sign in | join about | help | sitemap
Connecting Tech Pros Worldwide
Frank Swarbrick's Avatar

UPDATE CURRENT OF CURSOR


Question posted by: Frank Swarbrick (Guest) on August 14th, 2008 01:05 AM
New to SQL here...

We have a CURSOR declared thusly:

EXEC SQL
DECLARE ALL-ADJSTMTS-CSR CURSOR FOR
SELECT ACCT.ACCOUNT_ID
, ACCT.APPL_ID
, ACCT.BRANCH_NUMBER
, ACCT.CATEGORY_CODE
, ACCT.OPEN_DATE
, ADJ.STATUS_IND
, ADJ.PRICE_GROUP_DETAIL_ID
, ADJ.ADJUSTMENT_AMT
, ADJ.INVOICE_ID
FROM ACCOUNTS.ACCOUNT_ADJUSTMENTS ADJ
JOIN ACCOUNTS.ACCOUNT_MASTER ACCT
ON ACCT.ACCOUNT_ID = ADJ.ACCOUNT_ID
WHERE ACCT.APPL_CODE = :ACCT-APPL-CODE
AND ACCT.BRANCH_NUMBER = :BCH-BRANCH-NBR
AND ADJ.STATUS_IND = 'O'
END-EXEC

After fetching a row we may need to update the following columns:
ADJ.STATUS_IND
ADJ.INVOICE_ID
ADJ.LAST_ID

The first thought was to use WHERE CURRENT OF <cursor-name>, ie:
EXEC SQL
UPDATE ACCOUNTS.ACCOUNT_ADJUSTMENTS
SET STATUS_IND = :ACCT-ADJ-STATUS-IND
, INVOICE_ID = :ACCT-ADJ-INVOICE-ID
, LAST_ID = :ACCT-ADJ-LAST-ID
WHERE CURRENT OF ALL-ADJSTMTS-CSR
END-EXEC

You can't use the FOR UPDATE clause in the DECLARE, however, if the SELECT
joins two tables (and other reasons, of course). This makes sense. So
instead we just do a read-only cursor and update, when necessary, like
this:

EXEC SQL
UPDATE ACCOUNTS.ACCOUNT_ADJUSTMENTS
SET STATUS_IND = :ACCT-ADJ-STATUS-IND
, INVOICE_ID = :ACCT-ADJ-INVOICE-ID
, LAST_ID = :ACCT-ADJ-LAST-ID
WHERE ADJUSTMENT_ID = :ACCT-ADJ-ADJUSTMENT-ID
END-EXEC

Is this standard practice, or am I missing an (obvious) other way to do it?

Thanks,
Frank

9 Answers Posted
Serge Rielau's Avatar
Guest - n/a Posts
#2: Re: UPDATE CURRENT OF CURSOR

Frank Swarbrick wrote:
Quote:
Originally Posted by
New to SQL here...
>
We have a CURSOR declared thusly:
>
EXEC SQL
DECLARE ALL-ADJSTMTS-CSR CURSOR FOR
SELECT ACCT.ACCOUNT_ID
, ACCT.APPL_ID
, ACCT.BRANCH_NUMBER
, ACCT.CATEGORY_CODE
, ACCT.OPEN_DATE
, ADJ.STATUS_IND
, ADJ.PRICE_GROUP_DETAIL_ID
, ADJ.ADJUSTMENT_AMT
, ADJ.INVOICE_ID
FROM ACCOUNTS.ACCOUNT_ADJUSTMENTS ADJ
JOIN ACCOUNTS.ACCOUNT_MASTER ACCT
ON ACCT.ACCOUNT_ID = ADJ.ACCOUNT_ID
WHERE ACCT.APPL_CODE = :ACCT-APPL-CODE
AND ACCT.BRANCH_NUMBER = :BCH-BRANCH-NBR
AND ADJ.STATUS_IND = 'O'
END-EXEC
>
After fetching a row we may need to update the following columns:
ADJ.STATUS_IND
ADJ.INVOICE_ID
ADJ.LAST_ID
>
The first thought was to use WHERE CURRENT OF <cursor-name>, ie:
EXEC SQL
UPDATE ACCOUNTS.ACCOUNT_ADJUSTMENTS
SET STATUS_IND = :ACCT-ADJ-STATUS-IND
, INVOICE_ID = :ACCT-ADJ-INVOICE-ID
, LAST_ID = :ACCT-ADJ-LAST-ID
WHERE CURRENT OF ALL-ADJSTMTS-CSR
END-EXEC
>
You can't use the FOR UPDATE clause in the DECLARE, however, if the SELECT
joins two tables (and other reasons, of course). This makes sense. So
instead we just do a read-only cursor and update, when necessary, like
this:
>
EXEC SQL
UPDATE ACCOUNTS.ACCOUNT_ADJUSTMENTS
SET STATUS_IND = :ACCT-ADJ-STATUS-IND
, INVOICE_ID = :ACCT-ADJ-INVOICE-ID
, LAST_ID = :ACCT-ADJ-LAST-ID
WHERE ADJUSTMENT_ID = :ACCT-ADJ-ADJUSTMENT-ID
END-EXEC
>
Is this standard practice, or am I missing an (obvious) other way to do it?

Depending on your isolation level one of two things can happen:
* The row may change under your feet and you wipe someone elses updates.
* You get a deadlock when the read lock get upgraded

You can right that by adding WITH RS USE AND KEEP UPDATE UPDATE LOCKS to
the end of the SELECT query.

Cheers
Serge

--
Serge Rielau
DB2 Solutions Development
IBM Toronto Lab
Mark A's Avatar
Guest - n/a Posts
#3: Re: UPDATE CURRENT OF CURSOR

"Frank Swarbrick" <Frank.Swarbrick@efirstbank.comwrote in message
news:48A32175.6F0F.0085.0@efirstbank.com...
Quote:
Originally Posted by
New to SQL here...
>
We have a CURSOR declared thusly:
>
EXEC SQL
DECLARE ALL-ADJSTMTS-CSR CURSOR FOR
SELECT ACCT.ACCOUNT_ID
, ACCT.APPL_ID
, ACCT.BRANCH_NUMBER
, ACCT.CATEGORY_CODE
, ACCT.OPEN_DATE
, ADJ.STATUS_IND
, ADJ.PRICE_GROUP_DETAIL_ID
, ADJ.ADJUSTMENT_AMT
, ADJ.INVOICE_ID
FROM ACCOUNTS.ACCOUNT_ADJUSTMENTS ADJ
JOIN ACCOUNTS.ACCOUNT_MASTER ACCT
ON ACCT.ACCOUNT_ID = ADJ.ACCOUNT_ID
WHERE ACCT.APPL_CODE = :ACCT-APPL-CODE
AND ACCT.BRANCH_NUMBER = :BCH-BRANCH-NBR
AND ADJ.STATUS_IND = 'O'
END-EXEC
>
After fetching a row we may need to update the following columns:
ADJ.STATUS_IND
ADJ.INVOICE_ID
ADJ.LAST_ID
>
The first thought was to use WHERE CURRENT OF <cursor-name>, ie:
EXEC SQL
UPDATE ACCOUNTS.ACCOUNT_ADJUSTMENTS
SET STATUS_IND = :ACCT-ADJ-STATUS-IND
, INVOICE_ID = :ACCT-ADJ-INVOICE-ID
, LAST_ID = :ACCT-ADJ-LAST-ID
WHERE CURRENT OF ALL-ADJSTMTS-CSR
END-EXEC
>
You can't use the FOR UPDATE clause in the DECLARE, however, if the SELECT
joins two tables (and other reasons, of course). This makes sense. So
instead we just do a read-only cursor and update, when necessary, like
this:
>
EXEC SQL
UPDATE ACCOUNTS.ACCOUNT_ADJUSTMENTS
SET STATUS_IND = :ACCT-ADJ-STATUS-IND
, INVOICE_ID = :ACCT-ADJ-INVOICE-ID
, LAST_ID = :ACCT-ADJ-LAST-ID
WHERE ADJUSTMENT_ID = :ACCT-ADJ-ADJUSTMENT-ID
END-EXEC
>
Is this standard practice, or am I missing an (obvious) other way to do
it?
>
Thanks,
Frank


Yes this is common practice, but whether it is "standard" depends on what
you are trying accomplish and what level of locking and concurrent access
you need.

I often do a separate update/delete instead of "where current of cursor"
even when there is only one table involved, simply because I want to have
maximum concurrency (minimize locking issues). I declare the cursor as READ
ONLY, and use the WITH HOLD option (so that the cursor does not close upon
commit, and then issue a separate update or delete statement (followed by
frequent commits). Sometimes I may even select the cursor WITH UR if I know
it will not create a data integrity problem (I know that no other programs
are updating the columns in the predicate).


Frank Swarbrick's Avatar
Frank Swarbrick August 14th, 2008 05:35 PM
Guest - n/a Posts
#4: Re: UPDATE CURRENT OF CURSOR

>>On 8/13/2008 at 7:00 PM, in message
<6ghedkFfvbfnU1@mid.individual.net>,
Serge Rielau<srielau@ca.ibm.comwrote:
Quote:
Originally Posted by
Depending on your isolation level one of two things can happen:
* The row may change under your feet and you wipe someone elses updates.
* You get a deadlock when the read lock get upgraded
>
You can right that by adding WITH RS USE AND KEEP UPDATE UPDATE LOCKS to
the end of the SELECT query.


Great! Thanks for the tip. Makes sense.

Can you clarify the meaning of the following?

"Share: Limits concurrent application processes to read-only operations on
the data."

How is WITH RS USE AND KEEP SHARED LOCKS different than just WITH RS and no
lock-request-clause and how is it different then an UPDATE lock?

I guess I am wondering, if another process attempt to access a table that I
have declared a cursor on, and that other process wants to update it, what
will the other process see if I have SHARED locks on my cursor, and what
will he see if I have UPDATE locks on my cursor? I assume if I have UPDATE
locks then the other process will simply wait until my cursor is closed.
How is SHARED different from this?

Also, does it make sense to specify "FOR READ ONLY"? I guess in my
particular case it is superflulous, since that particular SELECT is already
a read only select. But I'm guessing it's probably a good idea to specify
FOR READ ONLY any time that one is not doing positioned updates/deletes. Is
this a fair statement?

Thanks!
Frank

Serge Rielau's Avatar
Guest - n/a Posts
#5: Re: UPDATE CURRENT OF CURSOR

Frank Swarbrick wrote:
Quote:
Originally Posted by
Quote:
Originally Posted by
Quote:
Originally Posted by
>>>On 8/13/2008 at 7:00 PM, in message

<6ghedkFfvbfnU1@mid.individual.net>,
Serge Rielau<srielau@ca.ibm.comwrote:
Quote:
Originally Posted by
>Depending on your isolation level one of two things can happen:
>* The row may change under your feet and you wipe someone elses updates.
>* You get a deadlock when the read lock get upgraded
>>
>You can right that by adding WITH RS USE AND KEEP UPDATE UPDATE LOCKS to
>the end of the SELECT query.

>
Great! Thanks for the tip. Makes sense.
>
Can you clarify the meaning of the following?
>
"Share: Limits concurrent application processes to read-only operations on
the data."
>
How is WITH RS USE AND KEEP SHARED LOCKS different than just WITH RS and no
lock-request-clause and how is it different then an UPDATE lock?
>
I guess I am wondering, if another process attempt to access a table that I
have declared a cursor on, and that other process wants to update it, what
will the other process see if I have SHARED locks on my cursor, and what
will he see if I have UPDATE locks on my cursor? I assume if I have UPDATE
locks then the other process will simply wait until my cursor is closed.
How is SHARED different from this?
>
Also, does it make sense to specify "FOR READ ONLY"? I guess in my
particular case it is superflulous, since that particular SELECT is already
a read only select. But I'm guessing it's probably a good idea to specify
FOR READ ONLY any time that one is not doing positioned updates/deletes. Is
this a fair statement?

Specifying FOR READ ONLY is generally good practice since it assures
that DB2 can use "blocking" of rows (send more than one row to the
consumer). As you note: In this case it is irrelevant.
I don't think the KEEP SHARED LOCK Sis useful for anything but
completeness of the options.
What you want is USE AND KEEP UPDATE LOCKS

Cheers
Serge


--
Serge Rielau
DB2 Solutions Development
IBM Toronto Lab
Frank Swarbrick's Avatar
Frank Swarbrick August 14th, 2008 09:15 PM
Guest - n/a Posts
#6: Re: UPDATE CURRENT OF CURSOR

>>On 8/13/2008 at 11:55 PM, in message
<lJPok.10220$Ep1.4961@bignews2.bellsouth.net>, Mark A<xxxxxx@xxxxx.com>
wrote:
Quote:
Originally Posted by
>
Yes this is common practice, but whether it is "standard" depends on
what
you are trying accomplish and what level of locking and concurrent
access
you need.
>
I often do a separate update/delete instead of "where current of cursor"
>
even when there is only one table involved, simply because I want to
have
maximum concurrency (minimize locking issues). I declare the cursor as
READ
ONLY, and use the WITH HOLD option (so that the cursor does not close
upon
commit, and then issue a separate update or delete statement (followed
by
frequent commits). Sometimes I may even select the cursor WITH UR if I
know
it will not create a data integrity problem (I know that no other
programs
are updating the columns in the predicate).


This information is very helpful. Thank you!
Frank

Frank Swarbrick's Avatar
Frank Swarbrick August 14th, 2008 10:05 PM
Guest - n/a Posts
#7: Re: UPDATE CURRENT OF CURSOR

>>On 8/14/2008 at 11:43 AM, in message
<6gj96pFg5gjqU1@mid.individual.net>,
Serge Rielau<srielau@ca.ibm.comwrote:
Quote:
Originally Posted by
Frank Swarbrick wrote:
Quote:
Originally Posted by
Quote:
Originally Posted by
>>>>On 8/13/2008 at 7:00 PM, in message

><6ghedkFfvbfnU1@mid.individual.net>,
>Serge Rielau<srielau@ca.ibm.comwrote:
Quote:
Originally Posted by
>>Depending on your isolation level one of two things can happen:
>>* The row may change under your feet and you wipe someone elses

updates.
Quote:
Originally Posted by
Quote:
Originally Posted by
Quote:
Originally Posted by
>>* You get a deadlock when the read lock get upgraded
>>>
>>You can right that by adding WITH RS USE AND KEEP UPDATE UPDATE LOCKS to

Quote:
Originally Posted by
>
Quote:
Originally Posted by
Quote:
Originally Posted by
>>the end of the SELECT query.

>>
>Great! Thanks for the tip. Makes sense.
>>
>Can you clarify the meaning of the following?
>>
>"Share: Limits concurrent application processes to read-only operations

on
Quote:
Originally Posted by
>the data."
>>
>How is WITH RS USE AND KEEP SHARED LOCKS different than just WITH RS and

Quote:
Originally Posted by
no
Quote:
Originally Posted by
>lock-request-clause and how is it different then an UPDATE lock?
>>
>I guess I am wondering, if another process attempt to access a table

that I
Quote:
Originally Posted by
>have declared a cursor on, and that other process wants to update it,

what
Quote:
Originally Posted by
>will the other process see if I have SHARED locks on my cursor, and what
>will he see if I have UPDATE locks on my cursor? I assume if I have

UPDATE
Quote:
Originally Posted by
>locks then the other process will simply wait until my cursor is closed.

Quote:
Originally Posted by
>
Quote:
Originally Posted by
>How is SHARED different from this?
>>
>Also, does it make sense to specify "FOR READ ONLY"? I guess in my
>particular case it is superflulous, since that particular SELECT is

already
Quote:
Originally Posted by
>a read only select. But I'm guessing it's probably a good idea to

specify
Quote:
Originally Posted by
>FOR READ ONLY any time that one is not doing positioned updates/deletes.

Quote:
Originally Posted by
Is
Quote:
Originally Posted by
>this a fair statement?

Specifying FOR READ ONLY is generally good practice since it assures
that DB2 can use "blocking" of rows (send more than one row to the
consumer). As you note: In this case it is irrelevant.
I don't think the KEEP SHARED LOCK Sis useful for anything but
completeness of the options.
What you want is USE AND KEEP UPDATE LOCKS


Great! Thanks again.
Frank Swarbrick's Avatar
Frank Swarbrick August 14th, 2008 11:35 PM
Guest - n/a Posts
#8: Re: UPDATE CURRENT OF CURSOR

>>On 8/14/2008 at 11:43 AM, in message
<6gj96pFg5gjqU1@mid.individual.net>,
Serge Rielau<srielau@ca.ibm.comwrote:
Quote:
Originally Posted by
Specifying FOR READ ONLY is generally good practice since it assures
that DB2 can use "blocking" of rows (send more than one row to the
consumer). As you note: In this case it is irrelevant.
I don't think the KEEP SHARED LOCK Sis useful for anything but
completeness of the options.
What you want is USE AND KEEP UPDATE LOCKS



A further question... Below are two explain plans for an UPDATE. The first
does a regular update using the primary key in the predicate. The second
uses the RID_BIT() function to do a 'direct read' of the record.

---------------------------------------------------------------
Statement:

UPDATE ACCOUNTS.ACCOUNT_ADJUSTMENTS AS ADJ SET STATUS_IND =:H9_0,
INVOICE_ID =:H9_1, LAST_ID =:H9_2
WHERE ADJUSTMENT_ID =:H9_3


Section Code Page = 1252

Estimated Cost = 21.316250
Estimated Cardinality = 1.000000

Access Table Name = ACCOUNTS.ACCOUNT_ADJUSTMENTS ID = 9,775
| Index Scan: Name = ACCOUNTS.ADJUSTMENTS_PK ID = 1
| | Regular Index (Not Clustered)
| | Index Columns:
| | | 1: ADJUSTMENT_ID (Ascending)
| #Columns = 0
| Single Record
| Fully Qualified Unique Key
| #Key Columns = 1
| | Start Key: Inclusive Value
| | | | 1: ?
| | Stop Key: Inclusive Value
| | | | 1: ?
| Data Prefetch: Eligible 0
| Index Prefetch: None
| Lock Intents
| | Table: Intent Exclusive
| | Row : Exclusive
Update: Table Name = ACCOUNTS.ACCOUNT_ADJUSTMENTS ID = 9,775
---------------------------------------------------------------
Statement:

UPDATE ACCOUNTS.ACCOUNT_ADJUSTMENTS AS ADJ SET STATUS_IND =:H9_0,
INVOICE_ID =:H9_1, LAST_ID =:H9_2
WHERE RID_BIT(ADJ)=:H9_3


Section Code Page = 1252

Estimated Cost = 10.647691
Estimated Cardinality = 1.000000

Access Table Name = ACCOUNTS.ACCOUNT_ADJUSTMENTS ID = 9,775
| #Columns = 3
| Single Record
| Fetch Direct Using Row IDs
| Lock Intents
| | Table: Intent Exclusive
| | Row : Exclusive
Update: Table Name = ACCOUNTS.ACCOUNT_ADJUSTMENTS ID = 9,775
---------------------------------------------------------------

The second is obviously "twice as efficient" as the first. Seems to me the
second is the way to go in all situations where you have already done a
SELECT to retrieve information about the row you end up updating. This is a
slightly different example, I think, than is described in the article
"Improve concurrency with DB2 9.5 optimistic locking: New optimistic locking
feature to avoid maintaining long-lived locks"
(http://www.ibm.com/developerworks/d...-0801schuetz/in
dex.html) Technically I am not using this to avoid a long-lived lock, in
the sense that the article intends. I'm just using it to fetch the record
quicker during a batch update process.

Seems almost too good to be true. Am I missing some reason that I should
not do this?

Thanks,
Frank

Serge Rielau's Avatar
Guest - n/a Posts
#9: Re: UPDATE CURRENT OF CURSOR

Frank Swarbrick wrote:
Quote:
Originally Posted by
Seems almost too good to be true. Am I missing some reason that I should
not do this?

It's not relational. Also, in theory the rid of a row can change under
some circumstances (e.g. a reorg).



--
Serge Rielau
DB2 Solutions Development
IBM Toronto Lab
Frank Swarbrick's Avatar
Frank Swarbrick August 15th, 2008 03:55 PM
Guest - n/a Posts
#10: Re: UPDATE CURRENT OF CURSOR

>>On 8/14/2008 at 8:49 PM, in message
<6gk95gFfs65jU1@mid.individual.net>,
Serge Rielau<srielau@ca.ibm.comwrote:
Quote:
Originally Posted by
Frank Swarbrick wrote:
Quote:
Originally Posted by
>Seems almost too good to be true. Am I missing some reason that I

should
Quote:
Originally Posted by
>not do this?

It's not relational. Also, in theory the rid of a row can change under
some circumstances (e.g. a reorg).


Yes, it's "not relational". Not sure if I care, though, if it's more
efficient.

As for reorg, I am pretty sure I would not be doing a reorg during a batch
processing window (hopefully!).

:-)

Frank
 
Not the answer you were looking for? Post your question . . .
197,039 members ready to help you find a solution.
Join Bytes.com

What is Bytes?

We are a network of experts and professionals in IT and software development that help one another with answers to tough questions and share insights. Get the best answers to your questions from over 197,039 network members.
Post your question now . . .
It's fast and it's free

Popular Articles

Top Community Contributors