You are on page 1of 4

Getting Started Newsletters Store

Products Services & Support About SCN Downloads


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