Industries Training & Education Partnership Developer Center Lines of Business University Alliances Events & Webinars Innovation Log On Join Us Hi, Guest Search the Community Activity Communications Actions Browse ABAP Testing and Troubleshooting Previous post Next post 0 Tweet 0 One of the most important considerations when writing a select statement against a large table is the effective use of an index. However this is sometimes more easily said than done. Have you ever found that your WHERE clause is missing just one field of an index and that field is not at the end of the index? There are some situations where you can make more effective use of the entire index even if you are missing a field. Here is a simple trick to help you do just that. If the field you are missing cannot contain too many entries, then if you create a range table with all possible entries and add that range table to your WHERE clause, you can dramatically speed up a SELECT statement. Even when you take into account the extra time needed to retrieve the key fields, the results are worth it. This may seem a bit counter-intuitive, but the example code shows what I'm doing (but be careful if you run this code in a QA environment, it may take a while): REPORT ztest_indexed_selects.PARAMETERS: p_bukrs LIKE bkpf-bukrs MEMORY ID buk OBLIGATORY, p_belnr LIKE bkpf-belnr MEMORY ID bln OBLIGATORY, p_gjahr LIKE bkpf-gjahr MEMORY ID gjr OBLIGATORY.TYPES: BEGIN OF bkpf_fields, bukrs LIKE bkpf- bukrs, belnr LIKE bkpf-belnr, gjahr LIKE bkpf- gjahr, blart LIKE bkpf-blart, budat LIKE bkpf-budat, END OF bkpf_fields.DATA: bkpf TYPE bkpf, dd07l TYPE dd07l.DATA: bkpf_int TYPE TABLE OF bkpf_fields, bkpf_wa TYPE bkpf_fields.DATA: start TYPE i, end TYPE i, dif TYPE i.START-OF-SELECTION. PERFORM get_one_document. PERFORM unindexed_select_bkpf. PERFORM indexed_select_bkpf. PERFORM unindexed_select_bkpf_2. PERFORM indexed_select_bkpf_2.*&-------------- -------------------------------------------------------**& Form get_one_document*&-------------------------------------------------------------- -------** text*----------------------------------------------------------- -----------*FORM get_one_document.* First we get a single document using a select statement that is* fully qualified on the primary key. Because buffering may be an issue,* the first select will be disregarded in this test. However, in real* life, this would be the important time.* Initial select SELECT bukrs belnr gjahr blart budat FROM bkpf INTO TABLE bkpf_int WHERE bukrs = p_bukrs AND belnr = p_belnr AND gjahr = p_gjahr. IF sy-subrc <> 0. MESSAGE ID '00' TYPE 'E' NUMBER '001' WITH 'Document does not exist'. ENDIF.* Next we get the same document using the same fully qualified select* statement. We will use the time for this in comparisons. REFRESH bkpf_int. GET RUN TIME FIELD start. SELECT bukrs belnr gjahr blart budat FROM bkpf INTO TABLE bkpf_int WHERE bukrs = p_bukrs AND belnr = p_belnr AND gjahr = p_gjahr. GET RUN TIME FIELD end. dif = end - start. WRITE: /001 'Time for first (fully qualified) select', 067 ':', dif, 'microseconds'. SKIP 1.* So we can use these fields later on READ TABLE bkpf_int INTO bkpf_wa INDEX 1.ENDFORM. " get_one_document*&-- -------------------------------------------------------------------**& Form unindexed_select_bkpf*&--------------------------------------------------- ------------------** text*------------------------------------------------ ----------------------*FORM unindexed_select_bkpf.* Now we select a group of documents using a select statement that is* missing the company code from the primary key. This may return a* different set of documents from the first select, but we are just* interested in how long it takes.* Initial select REFRESH bkpf_int. SELECT bukrs belnr gjahr blart budat FROM bkpf INTO TABLE bkpf_int WHERE belnr = p_belnr AND gjahr = p_gjahr. REFRESH bkpf_int. GET RUN TIME FIELD start.* Use this select in comparisons SELECT bukrs belnr gjahr blart budat FROM bkpf INTO TABLE bkpf_int WHERE belnr = p_belnr AND gjahr = p_gjahr. GET RUN TIME FIELD end. dif = end - start. WRITE: /001 'Time for second (unindexed) select', 067 ':', dif, 'microseconds'.ENDFORM. " unindexed_select_bkpf*&------- --------------------------------------------------------------**& Form indexed_select_bkpf*&----------------------------------------------------------- ----------** text*-------------------------------------------------------- --------------*FORM indexed_select_bkpf.* Now we're going to use the first trick. Go to table T001 (company* codes) and retrieve all the company codes and I ran the above code in QA instances with a DB2 environment in both 4.6C and 4.7. There are more indexes on BKPF in 4.7, but I tried to use one that is in both versions. I also ran a similar program in Oracle with comparable results. But I really dont know if it will work with other databases please let me know! I ran this many times in both active and quiet systems. Here are some typical results: Time for first (fully qualified) select : 148 microseconds Time for second (unindexed) select : 1,873,906 microseconds Time for third select (indexed by selecting from the check table) : 455 microseconds Time for fourth (partially indexed) select : 816,253 microseconds Time for fifth select (indexed by hardcoding the domain values) : 43,259 microseconds Time for sixth select (indexed by selecting the domain values) : 43,332 microseconds Some things to note: In the above times, the first select shows what happens in the ideal world. We are comparing select 2 against select 3 and select 4 against selects 5 and 6. But selects 2 and 3 should return the same results as should selects 4, 5 and 6. Using an Index When You Don't Have all of the Fields Posted by Rob Burbank in ABAP Testing and Troubleshooting on Sep 13, 2006 2:28:16 AM Share 0 Like Average User Rating (0 ratings) 0 Tweet 0 But the point is that even though start out knowing nothing about (and presumably not caring about) the company code in selects 2 and 3 and the document status in selects 4, 5 and 6, if you put all possible values of these fields into the select statement, the results are dramatic. If you try to combine both tricks, you will probably find that they dont work very well together. Once seems to be enough. 1106 Views Topics: abap Tags: analytics, select, index, bseg Share 0 Like 12 Comments Like (0) sunil ojha Sep 14, 2006 8:42 AM hi Rob , Its really fine way which generally we ignore but it will work more better if you use SELECT DISTINCT when you are filling ranges table insted of select.....endselect. Let me know if i am correct. Regards, Sunil Like (0) Rob Burbank Sep 14, 2006 9:34 AM (in response to sunil ojha) I'm actually not sure if you're right or not. But I wasn't really worrying about that portion. I'll give it a try and let you know. Thanks very much for your comments. Rob Like (0) Rob Burbank Sep 14, 2006 10:22 AM (in response to Rob Burbank) OK - I ran a small test. It turned out that SELECT DISTINCT took about five times longer to run than just SELECT. Rob Like (0) Rashid Javed Sep 20, 2006 12:06 AM Hi Nice information. But i think buffering at database and application server level affect the runtimes of select statements. Like once some data is read from BKPF, it will reduce the subsequent select times. To overcome this buffering effect, i think you can change the order of 'performs' in your program and than average out the runtimes for each version of select. Just a thought.... cheers! Community User Sep 20, 2006 2:16 AM (in response to Rashid Javed) Hi, Simply changing the order of performs should not affect runtime as long as database/app server buffer is large enough to hold results. One will have to make sure that the resultset is substantial enough to displace previously buffered entries. On the app server, you can easily bypass the buffer by explicitly issuing respective select statement, the database side of buffer is trickier to bypass unless you have administrative account and your own playbox to reset its buffers. Regards Like (0) Like (0) Rob Burbank Sep 20, 2006 2:41 PM (in response to Rashid Javed) I agree with Shehryar that the order shouldn't affect performance. But buffering was a big concern of mine when I was developing the program. That's why for each select for which I measure a run time, I do a "preliminary" select to try to eliminate any of these effects. Another way (which I also tried but didn't mention in the blog) is to run each select on subsequent nights. The run times were longer, in all cases, but the idea of getting the key fields from the database worked in all cases. Rob Like (0) Rashid Javed Sep 21, 2006 5:36 AM (in response to Rob Burbank) Actually the main of my post was >>change the order<< of 'performs' in your program and than >>average out the runtimes<< for each version of select. Of course order will affect the runtime; if the data is already buffered, subsequent selects of simmilar data will return less runtime than actual. To overcome this I was suggesting to make multiple runs of the program with different 'select' order. That is if in a program we have two selects(lets call it select A and select B) on same tables, than run the program first time without taking any time measurement so that it can account for buffering. After that make two runs of the program with different select orders taking runtime mesurements for both and calculate the average runtime for each select afterwards. But again it was just a thought, as you have already mentioned two points in your reply 1: "That's why for each select for which I measure a run time, I do a "preliminary" select to try to eliminate any of these effects." 2: "run each select on subsequent nights" there can be many ways to address the same problem. Cheers! Like (0) Rob Burbank Sep 22, 2006 2:43 PM (in response to Rashid Javed) In my testing I created a job and executed this program a number of times. So this, in a way, altered the select order, but within a job, not the same program. But the point really is - make sure you use an index, even if you have to go out of your way to do it. Rob Like (0) T4 Yang Jul 22, 2008 10:23 PM Hi Bob, Thank you for provide such useful information. I wonder weather it will be faster by adding a condition of BELNR (between '0' and 'z'). Another question is while getting BSEG from BKPF, whitch is better? database join (BSIS/BSAS/BSID/BSAD/BSIK/BSAK) or FOR ALL ENTRIES (BSEG)? It seems very slow by using FOR ALL ENTRIES when IT_BKPF(internal table) contains huge records. regards, T4 Rob Burbank Jul 28, 2008 3:11 PM (in response to T4 Yang) Thanks for the input T4. I'm not exactly sure what you're asking. But I think the answer is "It depends on what fields you have". If you have the customer, vendor or GL account number, then start with the appropriate secondary index table; otherwise, see if you can force an index by using other key fields. My experience is that JOINs are faster than FOR ALL ENTRIES (I've written another blog on just that subject), but it's not always true. Sometimes, FOR ALL ENTRIES is quicker. And of course, BSEG is a cluster table and cannot be used in JOINs anyway. But the purpose of this blog isn't about JOINs or FOR ALL ENTRIES. It's about how to go about getting a SELECT to use an index effectively. Follow SCN Site Index Contact Us SAP Help Portal Privacy Terms of Use Legal Disclosure Copyright Like (0) Rob Like (1) T4 Yang Jul 29, 2008 11:25 PM (in response to Rob Burbank) I've found your blog about JOIN and FOR ALL ENTRIES. Furthermore, the blog of "Quickly Retrieving FI document Data from BSEG" is excellent. Thanks for sharing. Best regards, T4 Like (1) Rob Burbank Jul 30, 2008 5:56 AM (in response to T4 Yang) Thank's T4 - it's always nice to get positive feedback. Rob