Skip to main content

Programmers Only: Reducing Conversations with DB2 for z/OS: Part 1

As Prem Mehra said, "There is no better-performing SQL than the SQL that is not executed."

Bonnie Baker specializes in teaching on-site classes for corporations, agencies and DB2 user groups. She is an IBM DB2 Gold Consultant, an IBM Information Champion, a five-time winner of the IDUG Best Speaker award, and a member of the IDUG Speakers' Hall of Fame. She is best known for her ability to demystify complex concepts through analogies and war stories.

Summary:  A look at SQL that's unnecessary and should be eliminated, SQL that is executed more times than necessary, and SQL that should be replaced

Date:  19 Oct 2009
Level:  Introductory
Activity:  448 views

Long ago, a learned colleague (Prem Mehra) introduced me to the concept of unnecessary SQL and his oft-quoted truism: "There is no better-performing SQL than the SQL that is not executed." In this three-part series, we are going to look at SQL that is totally unnecessary and should be eliminated, SQL that is executed far more times than necessary, and SQL that should be replaced with newer, better-performing SQL. The goal? To reduce connects to DB2 and, if possible, to eliminate some connections completely. The other goal? To learn about the latest solutions in IBM DB2 8 and DB2 9 for these old problems. So with that in mind, welcome to part 1 of my series on eliminating or reducing connects to DB2.

COUNT(*): Problem 1

There are many fundamental performance rules that should be obeyed when writing programs. One is to eliminate all unnecessary SELECT COUNTs in a program. Another is to reduce the number of executions of COUNTs if they cannot be totally eliminated. Let's look at a few examples:



Select count(*) into :hvcount
from employee_master
Where status_flag = 'T'
and hiredate <= current date - 90 days

Followed by:



Update employee_master
Set status_flag = 'P'
Where status_flag = 'T'
and hiredate <= current date - 90 days

Whenever I see SQL like this, my internal warning buzzer goes off. Why is there a COUNT before the UPDATE? I check further. Is the host variable ever examined? If it's 0, what is the action? What if the COUNT is greater than 0? Is the next action dependent upon the content of :hvcount?

Often the COUNT is totally unnecessary and can be completely eliminated. In this case, the programmer may want to know how many temporary (T) employees will be updated to permanent (P) status when/if the UPDATE statement is executed.

Two things are wrong with this approach. First, unless you are using ISOLATION RR or have exclusive maintenance access to the table, the COUNT of the number of qualified rows read and the actual number of rows that will be updated might differ. Rows could be deleted, inserted, or updated between the COUNT and your actual UPDATE.

However, the other-and far more important- problem is that the COUNT is unnecessary because DB2 counts the rows as they are updated and returns the count in SQLERRD(3) of the SQL Communications Area (SQLCA). As you test your programs, check to see if this SQLCA field contains the COUNT information that you need.

As far as I know, DB2 counts your rows as you do maintenance whenever it can. However, I do know of at least two exceptions:

  1. When you DELETE all of the rows from a table (i.e., no WHERE clause) in a segmented tablespace
  2. When you DELETE all of the rows from a table in a universal tablespace (new in DB2 9)

When doing a mass DELETE from a table that is in either of these two types of tablespaces, DB2 has no need to individually address the rows and therefore does not count them- with three exceptions:

  1. When DATA CAPTURE is on
  2. When a VALIDPROC is involved
  3. When row-level security is being used

However, mass DELETEs are most commonly used for work tables or temporary tables, which are highly unlikely to employ any of these three exception categories.

A new feature of DB2 9 allows you to truncate a table, thereby "reinitializing" it (either by resetting the High-Used Relative Byte Address [HURBA] or by deleting and redefining the underlying VSAM data set- much like a LOAD with an empty INPUT data set). This, like the mass DELETE, does not count the rows for you.


COUNT(*): Problem 2

Let's look at another real-life example of an improper/inadvisable/inappropriate use of



Select count(*) into :hvcount
from employee_master
Where status_flag = 'T'
and hiredate <= current date - 90 days
Followed by coded logic:
If :hvcount = 0, then get next, else if :hvcount =
1, then
Select col1, col2… into :hvcol1, :hvcol2
from employee_master
where status_flag = 'T'
and hiredate <= current date - 90 days
Else if :hvcount > 1, then
Declare mycursor cursor for
Select col1, col2… into :hvcol1, :hvcol2…
from employee_master
where status_flag = 'T'
and hiredate <= current date - 90 days
Open mycursor …
Fetch mycursor into :hvcol1, :hvcol2…

Let me reassure you: I am not manufacturing these examples. Here the programmer has very honorable intentions: to use a lower- CPU, shorter path-length singleton Searched SELECT if only one row qualifies, and to use a higher-CPU, longer path-length CURSOR if and only if >1 row qualifies.

But reading the row(s) to count them and then (again) reading the row(s) to process them is not the answer. Double dipping is not good, even if the pages are already in the buffer pool. How many times have I heard that? Just because the pages may be in the buffer pool does not mean the second read is free. Now that DB2 is one serious, flying, crunching maintenance machine, eliminating connects is one of the few places we can see huge reductions in CPU and GET PAGE overhead.

So, what is a better approach for this problem? The programmer is trying to avoid a higher overhead cursor. Therefore, think: How often is there only one row? Ninety-eight percent of the time, you say? Then do the singleton SELECT first. Then, if and only if you receive an -811 ("multiple rows returned to a Singleton Select") error message, open a cursor. Conversely, if the majority of the time more than one row will be found, then open a cursor immediately without worrying about the small payback from a singleton SELECT. In other words, do what is most common first, and don't do the COUNT.


COUNT(*): Problem 3


Listing 1. Sample code listing at maximum width

<!-- Please limit code lines to 90 characters max. -->
<!-- The following line is the max length: -->
|-------10--------20--------30--------40--------50--------60--------70--------80--------90
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Select count(*) into :hvcount
from employee_master
Where status_flag = 'T'
and hiredate <= current date - 90 days

This COUNT is followed by this logic: divide :hvcount by 10 (the number of rows that fit on a screen) to see how many screens of data there are. Why? So that each screen, including the initial one, can display "Page 1 of n" for the user. Here we have an "uh-oh, can't be eliminated" requirement. But is there a better way to find out how many 10-row screens will qualify if our user actually, and maybe unrealistically, hits PF8 until there are no more rows/screens?

Yes. First of all, ask, "How many screens are there usually?" If the answer is one screen or less than a full screen, then don't count the rows before you read them. Instead, open the cursor, fetch the rows, and count the rows as you read them. If you reach an End of File before you hit row 11, you know your answer without connecting to do the COUNT. And you have eliminated all of the GET PAGEs (maybe READ I/Os) incurred when you made DB2 do the COUNT.

What if there are usually between 11 and 30 rows that qualify? Then read those rows, too, counting as you FETCH. If you hit +100 before or as you read row 31, then save those rows in some area that hangs around between screen displays (e.g., in CICS, the COMMAREA). Put "Page 1 of 3" on page one and eliminate the instructions that would have been needed for the PF8s to display "Page 2 of 3" and "Page 3 of 3".

If more than three pages of rows qualify, then store what the user will realistically look at in the COMMAREA (or wherever). Then, and only then, do the COUNT. You have deferred the COUNT until the last possible moment. You have avoided the double read in most situations. Most important, you have a good program with great logic that others can replicate to write their programs.


We get it. What's next?

For those of you who work with DB2 daily and know a thousand examples and exceptions to my suggestions, I thank you for being in the trenches of the "It depends!" world of reality- and for sharing your insights and wisdom. For the rest of you, the next column will include more examples of unnecessary SQL and the whys, along with better and newer solutions for replacing the common code that is out there. Stay tuned for part 2.


About the author

Bonnie Baker specializes in teaching on-site classes for corporations, agencies and DB2 user groups. She is an IBM DB2 Gold Consultant, an IBM Information Champion, a five-time winner of the IDUG Best Speaker award, and a member of the IDUG Speakers' Hall of Fame. She is best known for her ability to demystify complex concepts through analogies and war stories.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Information Management
ArticleID=438242
ArticleTitle=Programmers Only: Reducing Conversations with DB2 for z/OS: Part 1
publish-date=10192009
author1-email=bkbaker@bonniebaker.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers