After preparing this class, I saw you had a good class on cursors a year ago, so this will be review for some of you.
Often an application program allows a user to browse through a table of data row by row, or look at several detail records at once, such as all the orders placed by a particular customer. The user may want to update or delete one or more of the rows. Say a customer calls and says “I placed 4 orders last week and I’d like to change one of them”. ‘”Do you have the order number?” “No.” The user can select all the orders for the customer, find the right one, and update it. You might also have a batch program that needs to process through a cursor and update some or all of the rows, after some processing or calculations. That is done through by a positioned update statement.
With a positioned update, you already have an existing row, and want to update or delete that row. Instead of giving where conditions, you say where current of cursor-name. DB2 already knows which row it is pointing to. After updating, that is still the current row. After deleting, you cannot refer to that row again. There is no current row. A “positioned” insert doesn’t make sense, as an already-existing row is the current row of the cursor. You can’t say “insert after this one” as rows in a table have no intrinsic order. You specify an order on retrieval if important. However, if your program inserts a row in the middle of cursor processing, that fits the select conditions of the cursor, it may or may not show up as you continue through the cursor. If the results table hasn’t been materialized yet, and if the row fits the select conditions that haven’t been realized yet, it will be selected.
Cursors that can use this are limited. SQL1 Standard says… IBM added for DB2… Standards ANSI (Am. Natl Stds Institute) and ISO (Internatl Stds Org) started on SQL stds in 1982. Adopted by ANSI in 86 and ISO in 87. Revised in 89 and now called SQL1 or SQL-89. This was a good start, but left a lot unspecified, especially areas where the individual DB vendors had gone separate ways. A second standard SQL2 was adopted in 1992 and specified much more, borrowed from IBM, also Oracle, MS SQL Server, Informix. Vendors now aim toward coming closer in new releases. (ie Coalesce). Good to know your particular brand of db. Most of these are obvious – if you are using GROUP BY, HAVING or DISTINCT you’re usually not referring to a single row. Order by and Join are not so obvious, and can cut out a lot of possibilities for updating within a cursor. You may have to choose between having rows sorted in a logical order or getting data from more than one table at a time and being able to update as you go along.
In most of our programs we say FOR READ ONLY. It is a great advantage for the database to know in advance whether a cursor will be used for updates or whether its data will be read only, because read-only processing is much simpler. Locks are held longer when the cursor may be updated. A block fetch may be used for read-only rows, but updateable rows must be fetched one at a time. This is important when transmitting data over a network. In DB2, leaving off the “FOR UPDATE” clause and having code in the program that does update where current of cursor gives an SQL code –510 on bind. “Table or view cannot be modified as requested” Other dbms’s assume for update if the cursor is not specified as for read only. This wastes resources. In the standard and in some databases the Of Column-name is optional, but not DB2. Bind error –503 “Column cannot be updated because it was not specified in For Update Of clause of the cursor from which it was fetched.
So, we have alerted DB2 we might update some or all of the rows selected. What happens when we do this? 2) If you look at the bind options at the end of a DB2 compile here in our shop, one of the options is ISOLATION CS. What is CS? Cursor stability. What is isolation? How protected this program is from concurrent users. A high level of isolation is nice and safe, but it cuts down concurrency. Your program has many locks and hangs onto them. Cursor stability sounds stable. Actually it is one of the less restrictive isolation levels. Cursor stability allows for the unlocking of data more quickly than DB2’s default method. Its purpose is to avoid large amounts of data being locked for long periods of time, to allow better concurrency for multiple users. However, it doesn’t unlock data for update or that has been updated as quickly. In another class we will talk about locking, a very important concept in any database management system. 3)I understand our shop doesn’t use restart procedures, but that can cut down on data locks by allowing more frequent commits. There is another possible topic for class discussion.
This example is how updating within a cursor was handled on a project I worked on. The environment was cobol, DB2 on the server and Visual Age Smalltalk on the client, communicating with TCP/IP. User can take time with the data without holding anyone else up. If he goes to lunch it’s not hurting anyone. When the user makes a change and clicks Save, we are not dependent on his actions, now nothing the user does will delay the update. If the data has been updated, we wouldn’t want to continue our update, because we would overlay the prior one. We don’t want to use the new data, after our changes have already been edited using the old values.
That is how one team designed their system. I am not suggesting we need to do that here, or that we should not use FOR UPDATE on a cursor. What is important is that you HAVE a strategy and have thought about your program’s effect on other users.
1. Especially important for online processes. Want to keep the user’s coffee break out of the transaction. 2.When updating using an independent where clause, every row that fits the criteria will be updated. 4.We do this here, with cursor stability isolation level. 5. A DB2 LAW at CCSD!
Updates within a Cursor in DB2
Updates within a Cursor in DB2 Pam Odden
Objectives <ul><li>Learn how to declare a cursor for update </li></ul><ul><li>Learn how to specify which row to update </li></ul><ul><li>What types of cursors can be updated? </li></ul><ul><li>Consider performance issues </li></ul>
Positioned Update and Delete Statements <ul><li>UPDATE table-name SET column-name = expression (, column-name = expression …) </li></ul><ul><ul><li>WHERE CURRENT OF cursor-name </li></ul></ul><ul><ul><li>Updates the single row that is the current row of the cursor </li></ul></ul><ul><ul><li>After updating, the row remains the current row of the cursor </li></ul></ul><ul><li>DELETE from table-name </li></ul><ul><ul><li>WHERE CURRENT OF cursor-name </li></ul></ul><ul><ul><li>Deletes the single row that is the current row of the cursor </li></ul></ul><ul><ul><li>After deletion, the cursor has no current row. It is positioned in the “empty space” left by the deleted row, and will be advanced to the next row by a subsequent FETCH statement </li></ul></ul><ul><li>There is no “positioned” insert statement </li></ul>
Strict criteria for positioned update and deletes <ul><li>According to the SQL1 Standard, the query associated with the cursor </li></ul><ul><li>must draw its data from a single source table, not from a join. </li></ul><ul><li>cannot specify an ORDER BY clause. </li></ul><ul><li>cannot specify the DISTINCT keyword. </li></ul><ul><li>must not specify a GROUP BY or HAVING clause. </li></ul><ul><li>cannot use UNION or UNION ALL </li></ul><ul><li>cannot be a subquery in which the same table is specified in the FROM clauses of both the outer query and the subquery </li></ul><ul><li>The user must have the UPDATE or DELETE privilege on the base table. </li></ul><ul><li>In DB2, the cursor must be specified “FOR UPDATE OF column-name ” at the time it is declared </li></ul><ul><li>These rules can change with versions of DB2. </li></ul>
Declare Cursor statement with For Update clause <ul><li>DECLARE CURSOR cursor-name FOR </li></ul><ul><ul><li>select statement </li></ul></ul><ul><ul><li>FOR UPDATE OF column-name (, column-name…) </li></ul></ul><ul><ul><li>Declares the cursor will be updateable </li></ul></ul><ul><ul><li>Specifies which columns will be updated </li></ul></ul><ul><li>Sometimes the database management system (DBMS) cannot determine whether a cursor is updateable or not. The cursor qualifies according to the criteria and the cursor select statement does not specify FOR UPDATE or FOR READ ONLY. This is called an ambiguous cursor . </li></ul><ul><li>DB2 does not permit updates within an ambiguous cursor. You get SQL bind error -510 “Table or view cannot be modified as requested” . DB2 does allow deletes within an ambiguous cursor, but the row is not locked before the delete. </li></ul><ul><li>Other Database Management Systems (DBMS) allow an ambiguous cursor and assume it may be updated. This causes extra work and extra locking. </li></ul><ul><li>Get in the habit of specifying FOR UPDATE or FOR READ ONLY. Programs that explicitly declare the intention to update are more easily maintained. </li></ul>
So what happens when we do this? <ul><li>When the results table is materialized (we learned last week this may be at open time or fetch time), DB2 obtains a lock on a page as it is accessed. </li></ul><ul><li>Since our TSO compile option 5.7 specifies an isolation level of CURSOR STABILITY, read-only page locks are released as soon as a different page is accessed. </li></ul><ul><li>Page locks on data that is specified for update are not released until the updating program issues a Commit or Rollback. </li></ul><ul><li>Consequently, depending on the structure of the program, a lot of data can wind up being locked. </li></ul><ul><ul><li>A batch program opening a large cursor may not issue a commit until it is finished. This won’t cause a problem if the program runs at night and no other processes are accessing the same data, but if other processes do access the data, they will have to wait. </li></ul></ul><ul><ul><li>An online program may open a small cursor with only a few rows. However, what if the user gets a phone call or goes to lunch before a commit is issued? Other users are meanwhile prevented from reading or updating data on those pages. </li></ul></ul>
One Team’s Strategy for Online Updates <ul><li>Isolation level is cursor stability </li></ul><ul><li>Cursor is originally opened for read-only, allowing locks to be released when the next page is accessed </li></ul><ul><li>Data is displayed to the user, who can scroll as desired </li></ul><ul><li>When the user makes a change and clicks SAVE, the updated row is selected again, this time for update </li></ul><ul><li>The timestamp column, LAST_UPDATE, is compared with the timestamp column of the row originally selected in the cursor. </li></ul><ul><li>If they match, we know the data has not been updated by someone else since our original select. The update is issued, followed by a commit. </li></ul><ul><li>If they don’t match, we know the data has been updated. A message is displayed to the user trying to do the update that the data has changed, and the new values are displayed. The user then can decide whether to proceed with the update. </li></ul>
One Team’s Strategy, cont. <ul><li>The strategy on the previous slide is not a solution for every application, just an example of how one team did it. </li></ul><ul><li>Advantages – </li></ul><ul><ul><li>Data not locked until ready for update </li></ul></ul><ul><ul><li>Restriction against order by clause or join in cursor is avoided </li></ul></ul><ul><li>Disadvantages – </li></ul><ul><ul><li>Rows are selected multiple times </li></ul></ul><ul><li>It worked well for a busy call-center application transmitting over a network, keeping locking to a minimum and transactions short. </li></ul><ul><li>The important thing is to have a strategy and to consider your program’s effect on other users. </li></ul>
Guidelines <ul><li>Keep transactions as short as possible </li></ul><ul><li>Use WHERE CURRENT OF cursor-name rather than doing independent updates using a WHERE clause. It is faster and protects you from inadvertently updating more than the one current row you are processing </li></ul><ul><li>Issue a commit as soon as possible after your program completes an update </li></ul><ul><li>If you know the program will not refetch a row of data after the cursor has moved past it, use a less restrictive isolation level. This allows DB2 to unlock the row sooner. </li></ul><ul><li>Explicitly specify whether your cursor is for read only or for update. </li></ul><ul><li> </li></ul>
Summary <ul><li>Use update or delete where current of cursor-name to update the current row of a cursor. </li></ul><ul><li>Declare a cursor as FOR READ ONLY or FOR UPDATE OF column-name . </li></ul><ul><li>Consider program performance and the need for concurrency when designing an update strategy. </li></ul>