DB2 V7 And Beyond"


Published on

  • Be the first to comment

  • Be the first to like this

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

DB2 V7 And Beyond"

  1. 1. DB2 V7 and Beyond: How newer DB2 capabilities can simplify age-old coding challenges on Z/os machines
  2. 2. IBM focused on the applications developers this time, not just the administrators! <ul><li>Newer features of DB2 can really help: </li></ul><ul><ul><li>Simplify programs </li></ul></ul><ul><ul><li>Enable granting of previously avoided user requests </li></ul></ul><ul><ul><li>Improve reliability </li></ul></ul><ul><ul><li>Increase table availability </li></ul></ul><ul><ul><li>Allow program features on the mainframe that users have become accustomed to on distributed platforms </li></ul></ul>
  3. 3. DB2 V7 and Beyond: Mismatched Joins – Now actually possible
  4. 4. Joining Tables with Mismatched Data Types <ul><li>In prior versions of DB2 this was a severe index killer </li></ul><ul><li>Example: </li></ul><ul><li>Table A has CUSTOMER_NBR stored as CHAR(8) but it really only contains numeric data </li></ul><ul><li>Table B has the same column but stores it as an INTEGER </li></ul><ul><li>You want to join ‘em </li></ul>
  5. 5. Joining Tables with Mismatched Data Types <ul><li>The old ways: </li></ul><ul><ul><li>Create a cross-reference table with both columns </li></ul></ul><ul><ul><ul><li>Maintenance problem and wastes space </li></ul></ul></ul><ul><ul><li>Put columns on both tables with both datatypes </li></ul></ul><ul><ul><ul><li>Confusing and wastes space </li></ul></ul></ul><ul><ul><li>Open a cursor against A, fetch through it, cycling a second cursor against B for each row </li></ul></ul><ul><ul><ul><li>Very inefficient and messy to code </li></ul></ul></ul>
  6. 6. Joining Tables with Mismatched Data Types <ul><li>Newer ways: </li></ul><ul><ul><li>DB2 is much more tolerant now of what is on the right side of the operator (=, <, > etc) </li></ul></ul><ul><ul><ul><li>Most Scalar functions are allowed </li></ul></ul></ul><ul><ul><ul><li>Concatenation is allowed </li></ul></ul></ul><ul><ul><ul><li>Mismatched lengths are allowed in many cases </li></ul></ul></ul>
  7. 7. Joining Tables with Mismatched Data Types <ul><li>In our example of joining two tables with mismatched datatypes, we can do some conversions </li></ul><ul><li>WHERE A.CUST_NBR = SUBSTR(CHAR(DIGITS(B.CUST_NBR))),3,8) </li></ul><ul><ul><li>DIGITS lets us extract the Integer as characters </li></ul></ul><ul><li>WHERE B.CUST_NBR = INTEGER(A.CUST_NBR) </li></ul>
  8. 8. Joining Tables with Mismatched Lengths <ul><li>This also was an index killer in prior versions of DB2 </li></ul><ul><li>Example: </li></ul><ul><li>Table A has CUSTOMER_NBR stored as CHAR(8) and CUSTOMER_LOC stored as CHAR(4) </li></ul><ul><li>Table B has one column with the two combined CUST_LOC_NBR CHAR(12) </li></ul><ul><li>You want to join ‘em </li></ul>
  9. 9. Joining Tables with Mismatched Lengths <ul><li>Again, we can use concatenations or substrings </li></ul><ul><li>WHERE A.CUST_NBR = </li></ul><ul><li>SUBSTR(CUST_LOC_NBR,5,8) </li></ul><ul><li> AND A.CUST_LOC = </li></ul><ul><li>SUBSTR(CUST_LOC_NBR,1,4) </li></ul><ul><ul><li>This will split out the two components </li></ul></ul><ul><li>WHERE B.CUST_LOC_NBR = </li></ul><ul><li>A.CUST_LOC||A.CUST_NBR </li></ul><ul><ul><li>This will put the two components together </li></ul></ul>
  10. 10. Joining Tables with Mismatched Data Types <ul><li>Remember a few things: </li></ul><ul><ul><li>Always have the column you want to index on to the left of the operator </li></ul></ul><ul><ul><li>NEVER put a function on the left of the operator if you want indexes to be used </li></ul></ul><ul><ul><li>ALWAYS do an EXPLAIN, these tricks don’t work in every scenario </li></ul></ul>
  11. 11. DB2 V7 and Beyond: Nested Table Expressions- Cool toy but are they useful
  12. 12. Real use for NTEs Nested Table Expressions <ul><li>What is an NTE? </li></ul><ul><ul><li>Treating a Select as if it were a table </li></ul></ul><ul><ul><li>SELECT * </li></ul></ul><ul><ul><li> FROM TABLE ( </li></ul></ul><ul><ul><li>SELECT * FROM </li></ul></ul><ul><ul><li>TABLE_A A, </li></ul></ul><ul><ul><li>TABLE_B B) </li></ul></ul>
  13. 13. Real use for NTEs Nested Table Expressions <ul><li>What good are they? </li></ul><ul><ul><li>You can do GROUP BYs on the result of another GROUP BY </li></ul></ul><ul><ul><li>You can do incompatible JOINS on multiple tables </li></ul></ul><ul><ul><li>You can make the DBAs REALLY angry if you are not careful – we’ll get to that </li></ul></ul>
  14. 14. Real use for NTEs Nested Table Expressions <ul><li>Let’s do it - Compounded Group By </li></ul><ul><ul><li>Get me the number of customers and total big bill amount at each terminal that has at least 1000 bills > 500.00 each </li></ul></ul><ul><li>SELECT X.TERM, COUNT(*), SUM(X.TOTAL_AMT) </li></ul><ul><li>FROM TABLE ( </li></ul><ul><li>SELECT </li></ul><ul><li>TERMINAL, CUSTOMER, SUM(BILL_AMT), COUNT(*) </li></ul><ul><li> FROM TBL_BILLS </li></ul><ul><li>WHERE BILL_AMT > 500.00 </li></ul><ul><li>GROUP BY TERMINAL, CUSTOMER </li></ul><ul><li>HAVING COUNT(*) > 1000 </li></ul><ul><li>) AS X(TERM, CUST, TOTAL_AMT, BILL_CNT) </li></ul><ul><li>GROUP BY TERMINAL </li></ul>
  15. 15. Real use for NTEs Nested Table Expressions <ul><li>Let’s do it - Incompatible Joins </li></ul><ul><li>You need to join multiple tables, </li></ul><ul><ul><li>Two of the tables must be LEFT OUTER JOINED and then you want to LEFT OUTER JOIN a third table to the results of this </li></ul></ul><ul><ul><li>How do you do this???? </li></ul></ul><ul><ul><ul><li>Nested Table Expressions </li></ul></ul></ul>
  16. 16. Real use for NTEs Nested Table Expressions <ul><li>Select * </li></ul><ul><li>FROM TBL_A a </li></ul><ul><li>left outer join table </li></ul><ul><li>( </li></ul><ul><li> select * </li></ul><ul><li>from tbl_b b </li></ul><ul><li>left outer join tbl_c c </li></ul><ul><li>on b.customer_nbr = c.customer_nbr </li></ul><ul><li>) As x </li></ul><ul><li>on a.customer_loc = x.customer_loc </li></ul>
  17. 17. Real use for NTEs Nested Table Expressions <ul><li>Lets make a DBA angry! </li></ul><ul><ul><li>Actually, that’s not such a good idea, so lets look at a few things to keep in mind to avoid this </li></ul></ul><ul><li>Be aware that NTEs use an area of DB2 called DSNDB07 </li></ul><ul><ul><li>This is a shared area used by sorts, NTEs, materialized views and a lot of other things </li></ul></ul><ul><ul><li>This can become a real problem if you are not careful, this area is NOT limitless! </li></ul></ul>
  18. 18. Real use for NTEs Nested Table Expressions <ul><li>So, how Do we avoid causing problems? </li></ul><ul><ul><li>Use Indexes, both inside and outside NTE </li></ul></ul><ul><ul><li>Use FETCH FIRST nnn ROWS ONLY </li></ul></ul><ul><ul><ul><li>This limits the total rows returned, but not the rows on the inside SQL statement </li></ul></ul></ul><ul><ul><li>Carefully craft your select inside the NTE to return a controlled result </li></ul></ul><ul><ul><ul><li>Don’t just join both tables and get an NTE with 100000 rows then select off two with the outer select </li></ul></ul></ul><ul><ul><li>If you are not sure you can control it, don’t use it </li></ul></ul><ul><ul><ul><li>Your scenario might be one to do the old way with a couple of cursors </li></ul></ul></ul><ul><ul><li>Use EXPLAIN! </li></ul></ul>
  19. 19. DB2 V7 and Beyond: Taming RIDLIST indexing
  20. 20. Taming RIDLIST processing <ul><li>What is it? </li></ul><ul><ul><li>Every row in a table has a Row Identifier (RID) which is used internally by DB2 </li></ul></ul><ul><ul><li>Some SQL really could benefit from two different Indexes, but DB2 can only use one at a time… Until NOW! </li></ul></ul><ul><ul><li>At the simplest level, a RIDLIST index process is simply a join between two index results </li></ul></ul>
  21. 21. Taming RIDLIST processing <ul><li>How does it work? </li></ul><ul><ul><li>DB2 scans one or more indexes and creates a temporary result which consists of the RIDS of all rows qualifying for each index </li></ul></ul><ul><ul><li>It uses these results to rescan looking for the LEFT JOIN of all of the results (i.e.: rows from each that match ALL criteria) </li></ul></ul><ul><ul><li>You then get the desired results </li></ul></ul>
  22. 22. Taming RIDLIST processing <ul><li>Sounds good, how can this help? </li></ul><ul><ul><li>Can undo the harm caused by having ‘OR’s mixed in with ‘AND’s on a table </li></ul></ul><ul><ul><li>Allows a column in one table to be matched to multiple columns in a second table </li></ul></ul><ul><ul><li>Can make previously unworkable SQL work decently </li></ul></ul>
  23. 23. Taming RIDLIST processing <ul><li>Here’s an example </li></ul><ul><ul><li>SELECT * </li></ul></ul><ul><ul><ul><li>FROM TBL_CUST A, TBL_BILL B </li></ul></ul></ul><ul><li> WHERE A.CUST_NBR = :WS-CUST </li></ul><ul><li>AND B.CUST_NBR = A.CUST_NBR </li></ul><ul><li>AND (A.CUST_LOC = B.LOC_1 OR </li></ul><ul><li> A.CUST_LOC = B.LOC_2 OR </li></ul><ul><li> A.CUST_LOC = B.LOC_3 OR </li></ul><ul><li> A.CUST_LOC = B.LOC_4 ) </li></ul><ul><li>This can create 4 RIDLISTS against TBL_BILL if each “loc” column has an index on TBL B and avoid a scan </li></ul><ul><ul><li>Previously this would be a tablespace scan against TBL_BILL </li></ul></ul>
  24. 24. Taming RIDLIST processing <ul><li>That’s cool, how can this ever be bad? </li></ul><ul><ul><li>DB2 may choose two indexes that on their own would return a majority of the table, then JOIN them </li></ul></ul><ul><ul><li>This may result in the equivalent of two or more full table scans, far worse than using no indexes at all </li></ul></ul>
  25. 25. Taming RIDLIST processing <ul><li>Example: </li></ul><ul><ul><li>Table has 1250000 rows </li></ul></ul><ul><ul><ul><li>Two separate indexes </li></ul></ul></ul><ul><ul><ul><ul><li>CUST_NBR and BILL_DATE </li></ul></ul></ul></ul><ul><ul><ul><li>I ask for everything for a customer on a date </li></ul></ul></ul><ul><ul><ul><ul><li>The customer has 5000 total entries on the table </li></ul></ul></ul></ul><ul><ul><ul><ul><li>There are 60000 total rows for this date </li></ul></ul></ul></ul><ul><ul><ul><ul><li>This customer has 10 rows on the chosen date </li></ul></ul></ul></ul><ul><ul><ul><ul><li>RIDLIST would get one result of 5k, another of 60k and join them to get just 10 rows </li></ul></ul></ul></ul>
  26. 26. Taming RIDLIST processing <ul><li>How do I know this has happened? </li></ul><ul><ul><li>The only way to know for sure is EXPLAIN </li></ul></ul><ul><ul><li>Watch for is unexpected performance degradation after changes </li></ul></ul><ul><ul><li>If you change and/or rebind a program that was bound on an earlier version of DB2 it may change access path, even on unchanged SQL </li></ul></ul><ul><ul><ul><li>You change a report heading and now the program runs 20 minutes instead of 2… </li></ul></ul></ul><ul><ul><ul><li>The SQL looks good (no improper functions, has index columns referenced etc) but it really runs bad … </li></ul></ul></ul>
  27. 27. Taming RIDLIST processing <ul><li>What do I do about it? </li></ul><ul><ul><li>See if you can add a column to one of the indexes to eliminate the need for RIDLIST </li></ul></ul><ul><ul><ul><li>Check to see if you can add DATE to the CUSTOMER index, if you can then do it </li></ul></ul></ul><ul><ul><li>Rewrite the SQL to use a different index or different criteria </li></ul></ul><ul><ul><li>Okay, none of that was possible, what now? </li></ul></ul><ul><ul><ul><li>Put a Scalar function on the left of the = sign </li></ul></ul></ul><ul><ul><ul><ul><li>Remember that this kills indexing </li></ul></ul></ul></ul><ul><ul><ul><ul><li>AND DATE(BILL_DATE) = :WS-BILL-DATE </li></ul></ul></ul></ul>
  28. 28. DB2 V7 and Beyond: Difficult CICS Paging
  29. 29. Difficult CICS Paging: Descending keys mixed in with Ascending keys <ul><li>Users functional request: </li></ul><ul><ul><li>A screen to view customer account invoice status – by selected Customer Account </li></ul></ul><ul><ul><li>Have the ability to view “ALL” or just the “UNPAID” accounts </li></ul></ul><ul><ul><li>Ignore the ENTRY DATE in the sort if the screen is in “UNPAID” mode </li></ul></ul><ul><ul><li>Smooth paging (i.e.: Page-Up goes to the previous page not the top of list) </li></ul></ul><ul><ul><li>Dynamic (i.e.: paging keeps up with database changes) </li></ul></ul><ul><ul><li>15 lines of detail per page </li></ul></ul>
  30. 30. Difficult CICS Paging: Descending keys mixed in with Ascending keys <ul><li>Users Want it Sorted by … </li></ul><ul><ul><li>Customer Loc Ascending </li></ul></ul><ul><ul><li>Paid Date Descending !!! </li></ul></ul><ul><ul><li>Entry Date Ascending </li></ul></ul><ul><li>> Ignore in “UNPAID” mode </li></ul><ul><ul><li>Invoice Number Ascending </li></ul></ul>
  31. 31. Difficult CICS Paging: The Table <ul><li>CUSTOMER_ACCT CHAR(8) </li></ul><ul><li>CUSTOMER_LOC CHAR(4) </li></ul><ul><li>PAID_DATE DATE </li></ul><ul><li>ENTRY_DATE DATE </li></ul><ul><li>INVOICE_NUMBER CHAR(10) </li></ul>
  32. 32. Difficult CICS Paging: Descending keys mixed in with Ascending keys <ul><li>Older ways of solving this: </li></ul><ul><ul><li>Build a TSQ and use external Sort </li></ul></ul><ul><ul><ul><li>Very inefficient if result set is large </li></ul></ul></ul><ul><ul><ul><li>Not dynamic unless you always rebuild TSQ </li></ul></ul></ul><ul><ul><li>Have lots of ‘OR’s in parenthesis with ‘AND’s </li></ul></ul><ul><ul><ul><li>Most likely not indexable at all </li></ul></ul></ul><ul><ul><li>Have multiple cursors with slightly different WHERE clause </li></ul></ul><ul><ul><ul><li>Not much fun to maintain </li></ul></ul></ul><ul><ul><li>Tell ‘em no </li></ul></ul><ul><ul><ul><li>Yeah right….. </li></ul></ul></ul>
  33. 33. Difficult CICS Paging: Descending keys mixed in with Ascending keys <ul><li>Newer ways to solve this: </li></ul><ul><ul><li>“Derived Columns” </li></ul></ul><ul><ul><ul><li>Use Case Statements and Scalar Functions </li></ul></ul></ul><ul><ul><li>“Compound Keys” </li></ul></ul><ul><ul><ul><li>Build Derived Columns and keys to work with them </li></ul></ul></ul><ul><ul><li>Switches in the WHERE clause </li></ul></ul>
  34. 34. Derived Columns <ul><li>Can reverse a value to make an ascending sort result in descending order </li></ul><ul><ul><li>Select </li></ul></ul><ul><ul><li>Digits(Days(‘9999-12-31’) – Days (Paid_Date)) </li></ul></ul><ul><ul><li>More current dates will have lower values, sorting this ascending actually results in a descending sort </li></ul></ul>
  35. 35. Derived Columns Reversing Sort Order <ul><li>Paid Date Derived Value Order </li></ul><ul><li>2006-07-18 0002919541 1 </li></ul><ul><li>2006-01-15 0002919733 2 </li></ul><ul><li>2005-02-01 0002920081 3 </li></ul><ul><li>2000-08-02 0002921725 4 </li></ul><ul><li>We sort Ascending by Derived Value and the dates come out in Descending order !!! </li></ul>
  36. 36. Derived Columns Selectively ignore <ul><li>In “UNPAID” mode we want to ignore Entry Date in sort order </li></ul><ul><ul><li>CASE </li></ul></ul><ul><ul><li> WHEN :WS-SCREEN-MODE = ‘U' </li></ul></ul><ul><ul><li> THEN '0001-01-01' </li></ul></ul><ul><ul><li>ELSE ENTRY_DATE </li></ul></ul><ul><ul><li>END CASE </li></ul></ul><ul><ul><li>This causes the column always being ‘0001-01-01’ in “UNPAID” mode, effectively ignoring it </li></ul></ul>
  37. 37. Difficult CICS Paging The Select Clause <ul><li>Select </li></ul><ul><li>Digits(Days(‘9999-12-31’) – Days (Paid_Date)) </li></ul><ul><li>,CASE </li></ul><ul><ul><li>WHEN :WS-SCREEN-MODE = 'A' </li></ul></ul><ul><ul><li>THEN '0001-01-01' </li></ul></ul><ul><ul><li>ELSE Entry_Date </li></ul></ul><ul><ul><li>END CASE </li></ul></ul><ul><li>,Customer_Acct </li></ul><ul><li>,Customer_Loc </li></ul><ul><li>,Paid_Date </li></ul><ul><li>,Entry_Date </li></ul><ul><li>,Invoice_Number </li></ul>
  38. 38. Difficult CICS Paging The Order By <ul><li>Order by </li></ul><ul><li>Customer Loc </li></ul><ul><li>,1 </li></ul><ul><li>,2 </li></ul><ul><li>,Invoice Number </li></ul><ul><li>FETCH FIRST 16 ROWS ONLY </li></ul><ul><li>The 1 and 2 refer to the value in that position of the result set (i.e.: the first and second columns). </li></ul><ul><li>We will save off the 16 th row as the forward paging key. If we get +100 before then we are at EOF </li></ul>
  39. 39. Difficult CICS Paging The WHERE clause <ul><li>WHERE CUSTOMER_ACCT = :WS-CUST-ACCT </li></ul><ul><li>AND CUSTOMER_LOC >= :WS-CUST-LOC </li></ul><ul><li>AND CUSTOMER_LOC|| </li></ul><ul><li>DIGITS(DAYS(‘9999-12-31’) – DAYS(PAID_DATE))|| </li></ul><ul><li>CHAR(DATE('0001-01-01') + </li></ul><ul><li>((DAYS(ENTRY_DATE) - </li></ul><ul><li>DAYS(DATE('0001-01-01'))) * </li></ul><ul><li>:WS-SCREEN-MODE-NUM) DAYS)|| </li></ul><ul><li>INVOICE_NBR >= :WS-PAGING-KEY </li></ul><ul><li>AND (:WS-SCREEN-MODE = 'A' OR </li></ul><ul><li>DATE_PAID = '0001-01-01') </li></ul>
  40. 40. Difficult CICS Paging The Where clause – Explained! <ul><li>First lets start with the table’s index: </li></ul><ul><ul><li>We do want this to run efficiently </li></ul></ul><ul><ul><ul><li>The table has 750000 rows </li></ul></ul></ul><ul><ul><ul><li>There are have 5000 customers across 150 locations </li></ul></ul></ul><ul><li>Lets look at the index on the table that will make this SQL work </li></ul>
  41. 41. Difficult CICS Paging The WHERE clause - Explained! <ul><li>Column Cardinality </li></ul><ul><li>CUSTOMER_ACCT 5000 </li></ul><ul><li>CUSTOMER_LOC 150 </li></ul><ul><li>INVOICE_NBR 750000 </li></ul><ul><li>Invoice number would be a perfect key… </li></ul><ul><ul><li>But we don’t have it, we were given CUSTOMER_ACCT </li></ul></ul><ul><ul><li>It’s the third column in this index, indexes must be used from the top down to work efficiently </li></ul></ul><ul><li>Remember that Cardinality assumes even distribution (A perfect world) </li></ul><ul><ul><li>This is not a perfect world so it is only a good approximation to gauge efficiency </li></ul></ul><ul><ul><li>Cardinality becomes less significant further down an index. </li></ul></ul>
  42. 42. Difficult CICS Paging The Where Clause – Explained! <ul><li>Lets break it down … </li></ul><ul><li>CUSTOMER_ACCT = :WS-CUST-ACCT </li></ul><ul><li>This is our big performance helper, without it our SQL would be horrifying </li></ul><ul><li>It cuts our 750000 row table into around 5000 eligible rows </li></ul>
  43. 43. Difficult CICS Paging The Where Clause – Explained! <ul><li>AND CUSTOMER_LOC >= :WS-CUST-LOC </li></ul><ul><li>This cuts the 5000 rows down to 33, well not really </li></ul><ul><ul><li>No one customer uses all 150 locations (they probably use around 4 at most) </li></ul></ul><ul><ul><li>More realistically we are left with 125 or so rows </li></ul></ul><ul><li>If you remember back to the WHERE clause, this also appears in the big concatenation </li></ul><ul><li>This is not redundant, once you start using || and scalar functions, DB2 cannot use that criteria for indexing </li></ul>
  44. 44. Difficult CICS Paging The Where Clause – Explained! <ul><li>AND CUSTOMER_LOC|| </li></ul><ul><li>DIGITS(DAYS(‘9999-12-31’) – DAYS(PAID_DATE))|| </li></ul><ul><li>This puts together the customer_loc with the inverted paid date </li></ul><ul><li>CHAR(DATE('0001-01-01') + </li></ul><ul><li>((DAYS(ENTRY_DATE) - </li></ul><ul><li>DAYS(DATE('0001-01-01'))) * </li></ul><ul><li>:WS-SCREEN-MODE-NUM) DAYS)|| </li></ul><ul><li>This adds in the Entry_date in ‘ALL’ Mode (‘0001-01-01’ in “U”) </li></ul><ul><li>INVOICE_NBR >= :WS-PAGING-KEY </li></ul><ul><li>and Finally tacks on the invoice number </li></ul>
  45. 45. Difficult CICS Paging The Where Clause – Explained! <ul><li>The Working Storage Key </li></ul><ul><li>01 DATABASE-KEYS </li></ul><ul><ul><li>05 WS-PAGING-KEY PIC X(34) </li></ul></ul><ul><ul><li>05 FILLER REDEFINES WS-PAGING-KEY </li></ul></ul><ul><ul><li>10 WS-KEY-LOC PIC X(04). </li></ul></ul><ul><ul><li>10 WS-KEY-PDATE PIC X(10). </li></ul></ul><ul><ul><li>10 WS-KEY-EDATE PIC X(10). </li></ul></ul><ul><ul><li>10 WS-KEY-INVOICE PIC X(10). </li></ul></ul><ul><li>* “A”LL MODE OR “U”NPAID MODE </li></ul><ul><ul><li>05 WS-SCREEN-MODE PIC X(01). </li></ul></ul><ul><li>* 0 IF IN “U”PAID MODE, 1 IN “A”LL MODE </li></ul><ul><ul><li>05 WS-SCREEN-MODE-NUM ` PIC S9(04) COMP. </li></ul></ul>
  46. 46. Difficult CICS Paging Backing up <ul><li>The page up cursor is the same thing with 2 tweaks </li></ul><ul><ul><li>Order by DESCENDING </li></ul></ul><ul><ul><li><= and < instead of >= and >= </li></ul></ul><ul><li>When they page up we will use the reverse cursor to find the starting point for the new page </li></ul><ul><ul><li>It will begin with the row prior to first row on the current page (the last row on the prior page) </li></ul></ul><ul><li>We will use this value as the key for the page down cursor to actually build the page </li></ul>
  47. 47. Difficult CICS Paging The WHERE clause - Up <ul><li>WHERE CUSTOMER_ACCT = :WS-CUST-ACCT </li></ul><ul><li>AND CUSTOMER_LOC <= :WS-CUST-LOC </li></ul><ul><li>AND CUSTOMER_LOC|| </li></ul><ul><li>DIGITS(DAYS(‘9999-12-31’) – DAYS(PAID_DATE))|| </li></ul><ul><li>CHAR(DATE('0001-01-01') + </li></ul><ul><li>((DAYS(ENTRY_DATE) - </li></ul><ul><li>DAYS(DATE('0001-01-01'))) * </li></ul><ul><li>:WS-SCREEN-MODE-NUM) DAYS)|| </li></ul><ul><li>INVOICE_NBR < :WS-PAGING-KEY </li></ul><ul><li>AND (:WS-SCREEN-MODE = 'A' OR </li></ul><ul><li>DATE_PAID = '0001-01-01') </li></ul>
  48. 48. Difficult CICS Paging Backing up <ul><li>We are on page 3 </li></ul><ul><ul><li>We feed Page 3, line 1 values into page up cursor </li></ul></ul><ul><ul><ul><li>We saved it in Linkage when page 3 was built last program cycle </li></ul></ul></ul><ul><ul><ul><li>The very first time in we used low-values </li></ul></ul></ul><ul><ul><li>The page up cursor finds us Page 1, line 16 value </li></ul></ul><ul><ul><ul><li>Remember there is not a Line 16 on the screen, this line is for the paging process and is saved in Linkage </li></ul></ul></ul><ul><ul><li>We feed that into page down cursor and build page 2 </li></ul></ul><ul><ul><ul><li>This ensures that pages are always full and changes to the database are reflected as they happen </li></ul></ul></ul><ul><ul><ul><li>Prevents pages with missing top lines </li></ul></ul></ul><ul><ul><ul><li>Makes first/last pages reliable </li></ul></ul></ul>
  49. 49. Difficult CICS Paging In Conclusion <ul><li>Remember performance, make sure the first variable item in the compound key is also stated as a >= (or <=) by itself </li></ul><ul><li>Only put variable items in the compound key, if it is always the same value (ex: Customer_Acct) put it in as an = </li></ul><ul><li>Do an EXPLAIN!, check your performance </li></ul><ul><li>Check your indexes, make sure there is an index for at least one = and also the first variable item </li></ul>
  50. 50. DB2 V7 and Beyond: Ideas to make use of UNION
  51. 51. Making Use of UNIONs <ul><li>A UNION can be used to replace multiple “OR” statements </li></ul><ul><ul><li>Remember that “OR”s are real index killers </li></ul></ul><ul><ul><ul><li>Avoiding them where possible is important </li></ul></ul></ul><ul><ul><li>We talked about RIDLISTs before but the RIDLIST can’t help every SQL </li></ul></ul><ul><ul><ul><li>UNIONS may be able to help some of these </li></ul></ul></ul>
  52. 52. Making Use of UNIONs <ul><li>Assume we have a large Customer Table </li></ul><ul><ul><li>It has an index on CUST_NBR </li></ul></ul><ul><ul><li>It is a large table 1.5 million rows </li></ul></ul><ul><ul><li>We need to extract all customers with Current Bills and certain Past Due Bills </li></ul></ul><ul><ul><ul><li>We only want to list a customer once </li></ul></ul></ul>
  53. 53. Making Use of UNIONs <ul><li>SELECT * FROM TBL_CUSTOMER </li></ul><ul><li>WHERE CUST_NBR IN </li></ul><ul><ul><li>( SELECT CUST_NBR </li></ul></ul><ul><ul><li>FROM TBL_CURR_BILLS) </li></ul></ul><ul><ul><li>OR CUST_NBR IN </li></ul></ul><ul><ul><li>(SELECT CUST_NBR </li></ul></ul><ul><ul><li>FROM TBL_PAST_DUE </li></ul></ul><ul><ul><li>WHERE PAST_DUE_DAYS < 30) </li></ul></ul><ul><ul><li>The performance of this could be hideous </li></ul></ul><ul><ul><ul><li>Even assuming the TBL_CURR_BILLS and TBL_PAST_DUE are small </li></ul></ul></ul><ul><ul><ul><li>The presence of the OR really prevents any effective indexing </li></ul></ul></ul>
  54. 54. Making Use of UNIONs <ul><li>It can be rewritten as a Union: </li></ul><ul><li>SELECT CUST_NBR FROM TBL_CUSTOMER </li></ul><ul><li>WHERE CUST_NBR IN </li></ul><ul><ul><li>( SELECT CUST_NBR </li></ul></ul><ul><ul><li>FROM TBL_CURR_BILLS) </li></ul></ul><ul><li>UNION </li></ul><ul><li>SELECT CUST_NBR FROM TBL_CUSTOMER </li></ul><ul><li>WHERE CUST_NBR IN </li></ul><ul><ul><li>(SELECT CUST_NBR </li></ul></ul><ul><ul><li>FROM TBL_PAST_DUE </li></ul></ul><ul><ul><li> WHERE PAST_DUE_DAYS < 30) </li></ul></ul>
  55. 55. Making Use of UNIONs <ul><li>The UNION will run each one as a separate SQL and can then index CUST_NBR </li></ul><ul><li>Then it will SORT/SUM them into one result </li></ul><ul><ul><li>UNION ALL would eliminate the SORT/SUM but would possibly return duplicates if the customer had both a current and a <30 past due bill </li></ul></ul>
  56. 56. Making Use of UNIONs <ul><li>Places to consider a UNION </li></ul><ul><ul><li>SQL that you have to use a lot of “OR”s </li></ul></ul><ul><ul><li>SQL where you are trying to get the same result set from conflicting criteria </li></ul></ul><ul><ul><li>Places where RIDLIST processing is being invoked but is performing poorly </li></ul></ul><ul><ul><li>UNION doesn’t have to be used against different tables, both Selects can be the same table </li></ul></ul>
  57. 57. DB2 V7 and Beyond: Questions ?