After you complete this chapter, you will be able to:
Quite often in ABAP/4, the data you put into an internal table comes from one or more database tables. This section presents the best ways to do this.
The ways of filling an internal table from the database fall into two categories:
To select multiple rows directly into the body of an internal table, use the into table addition of the select statement. It takes the selected rows and places them into the body of an internal table in a single operation known as an array operation. No work areas are used or needed.
An array operation is any statement that performs an operation on multiple rows of an internal table, instead of a single row at a time. Array operations are always more efficient than single row operations.
TIP |
select into table is the most efficient way to fill an internal table from the database. |
The following is the syntax for the into table addition of the select statement.
(a) select * (b) select f1 f2 . . . from dbtab into [corresponding fields of] table it.
where:
The following points apply:
The select into table statement places all selected rows directly into the body of it. Existing internal table contents are first discarded. Listing 13.1 contains a sample program using select into table.
Listing 13.1 This Program Reads Rows from a Database
Table Directly into an Internal Table
1 report ztx1301. 2 tables ztxlfa1. 3 data it like ztxlfa1 occurs 23 with header line. 4 5 select * from ztxlfa1 into table it. "don't code an endselect 6 loop at it. 7 write: / it-lifnr, it-name1. 8 endloop. 9 10 select * from ztxlfa1 into table it 11 where lifnr between 'V2' and 'V5'. 12 skip. 13 loop at it. 14 write: / it-lifnr, it-name1. 15 endloop. 16 free it.
The code in Listing 13.1 produces this output:
V9 Code Now, Specs Later Ltd. 1000 Parts Unlimited 1010 Industrial Pumps Inc. 1020 Chemical Nation Ltd. 1030 ChickenFeed Ltd. 1050 The Bit Bucket 1060 Memory Lane Ltd. 1070 Flip My Switch Inc. V10 Duncan's Mouse Inc. V6 Anna Banana Ltd. V8 Smile When You Say That Ltd. V11 Weiner Schnittsel Inc. V12 Saurkrouten 1040 Motherboards Inc. 1080 Silicon Sandwich Ltd. 1090 Consume Inc. 2000 Monitors and More Ltd. V1 Quantity First Ltd. V2 OverPriced Goods Inc. V3 Fluffy Bunnies Ltd. V4 Moo like a Cow Inc. V5 Wolfman Sport Accessories Inc. V7 The Breakfast Club Inc. V2 OverPriced Goods Inc. V3 Fluffy Bunnies Ltd. V4 Moo like a Cow Inc. V5 Wolfman Sport Accessories Inc.
NOTE |
Don't use an endselect with select into table. A syntax error will occur. |
Suppose your program already reads data into an internal table. If that data needs to be sorted, use the sort statement. Don't use order by on select even if those fields are supported by an index. On a quiet, standalone system, measurements reveal that the sort statement is a little faster. Even if they were at a par, sort would still be the preferred method because it offloads cycles from the database server to the application server.
NOTE |
Don't use order by with select into table. Use sort on the internal table instead. |
Imagine this scenario:
Each row from dbtab is moved byte-by-byte into a new row of it, which is exactly like assigning one field string to another. There is only one difference between this and a field string assignment: dbtab can be shorter (in bytes) than it, but it cannot be longer than it. If it is longer, a short dump (error SAPSQL_SELECT_TAB_TOO_SMALL) occurs. The data types and lengths of each sending field in dbtab should match the receiving field in it. Any remaining fields in it are filled with initial values (blanks or zeros). Figures 13.1 through 13.4 illustrate this point.
Table 13.1 summarizes the rules for using select with into table. Selected fields must fit into the internal table. This table describes the restrictions you will face.
|
Selected? | |
Select * | All fields in the database table | The internal table must contain at least as many fields as the database table, and they must be like the database table fields. |
Select f1 | f1 from the database table | The internal table must begin with a field like f1, or have only one field and it must be like f1. |
Listings 13.2 and 13.3 illustrate this concept.
Listing 13.2 What Happens if Table Structures Differ
When Using the Select-into Statement
1 report ztx1302. 2 tables ztxlfa1. 3 data begin of it occurs 2. 4 include structure ztxlfa1. 5 data: invoice_amt type p, 6 end of it. 7 8 select * from ztxlfa1 into table it where land1 = 'DE'. 9 loop at it. 10 write: / it-lifnr, it-land1, it-regio, it-invoice_amt. 11 endloop. 12 13 skip. 14 select lifnr land1 regio from ztxlfa1 into table it where land1 = 'DE'. 15 loop at it. 16 write: / it-mandt, it-lifnr, it-land1, it-regio. 17 endloop. 18 free it.
The code in Listing 13.2 produces this output:
V11 DE 07 0 V12 DE 14 0 V8 DE 03 0 V11 DE 07 V12 DE 14 V8 DE 03
Listing 13.3 Your Program Will Produce a Short Dump
If You Try to Put More Fields into an Internal Table than Exist
in That Table
1 report ztx1303. 2 tables ztxlfa1. 3 data: begin of it occurs 23, 4 lifnr like ztxlfa1-lifnr, "this is a char 10 field 5 land1 like ztxlfa1-land1, "this is a char 3 field 6 end of it. 7 8 *The next line causes a short dump. The internal table is too narrow. 9 select * from ztxlfa1 into table it.
The code in Listing 13.3 produces the output shown in Figure 13.5.
If the components of the internal table aren't in the same order as those from the database, or if they don't have the same data type and length, you can use the corresponding fields addition. This addition has the same effect as the move corresponding statement does on a field string: It moves fields from the database table into fields of the same name in the internal table body.
TIP |
corresponding fields performs one assignment per field instead of a single assignment for the entire row, so it adds overhead to the select statement. You should use it only when necessary. |
The following points apply:
Listing 13.4 shows efficient and inefficient uses for this addition.
Listing 13.4 Using the corresponding fields Addition
to Fill Internal Tables from the Database
1 report ztx1304. 2 tables ztxlfa1. 3 data: begin of it1 occurs 23, 4 lifnr like ztxlfa1-lifnr, 5 lifnr_ext like ztxlfa1-lifnr, 6 land1 like ztxlfa1-land1, 7 end of it1, 8 begin of it2 occurs 23, 9 lifnr like ztxlfa1-lifnr, 10 land1 like ztxlfa1-land1, 11 end of it2. 12 13 * This is efficient usage: 14 select lifnr land1 from ztxlfa1 15 into corresponding fields of table it1 16 where lifnr between 'V10' and 'V12'. 17 loop at it1. 18 write: / it1-lifnr, it1-land1. 19 endloop. 20 21 * This is inefficient: 22 select * from ztxlfa1 23 into corresponding fields of table it2 24 where lifnr between 'V10' and 'V12'. 25 skip. 26 loop at it1. 27 write: / it1-lifnr, it1-land1. 28 endloop. 29 30 * Instead, write: 31 select lifnr land1 from ztxlfa1 into table it2 32 where lifnr between 'V10' and 'V12'. 33 skip. 34 loop at it2. 35 write: / it2-lifnr, it2-land1. 36 endloop. 37 free: it1, it2.
The code in Listing 13.4 produces this output:
V10 CC V11 DE V12 DE V10 CC V11 DE V12 DE V10 CC V11 DE V12 DE
Using select to add rows one at a time requires the use of a work area and a second statement such as append, insert, or collect. Omitting the table addition causes the row to be assigned to a work area. It is common to use the header line of an internal table as an explicit work area. Alternatively, you can use the default table work area (defined using tables).
Listing 13.5 shows some examples of using select to add rows one at a time to an internal table.
Listing 13.5 Using select with the append Statement
to Fill Internal Tables
1 report ztx1305. 2 tables ztxlfa1. 3 data it like ztxlfa1 occurs 2 with header line. 4 5 *Do it this way 6 select * from ztxlfa1 into it "notice 'table' is omitted so the 7 where land1 = 'DE'. "row goes into the header line of it 8 append it. 9 endselect. 10 11 loop at it. 12 write: / it-lifnr, it-land1, it-regio. 13 endloop. 14 refresh it. 15 16 *Or this way 17 select * from ztxlfa1 "no 'into' so the row goes into the 18 where land1 = 'DE'. "default table work area 19 append ztxlfa1 to it. "and then is appended to it 20 endselect. 21 22 skip. 23 loop at it. 24 write: / it-lifnr, it-land1, it-regio. 25 endloop. 26 refresh it. 27 28 *Not this way 29 select * from ztxlfa1 "row goes into default table work area 30 where land1 = 'DE'. 31 it = ztxlfa1. "then is assigned to the header line 32 append it. 33 endselect. 34 35 skip. 36 loop at it. 37 write: / it-lifnr, it-land1, it-regio. 38 endloop. 39 free it.
The code in Listing 13.5 produces this output:
V11 DE 07 V12 DE 14 V8 DE 03 V11 DE 07 V12 DE 14 V8 DE 03 V11 DE 07 V12 DE 14 V8 DE 03
into corresponding fields can also be used to place data into a work area instead of into the body of the internal table. The effect is similar to that of the move-corresponding statement and is more efficient. Listing 13.6 shows how.
Listing 13.6 How the corresponding fields Addition
to the select Statement Produces the Same Effect as the move-corresponding
Statement
1 report ztx1306. 2 tables ztxlfa1. 3 data: begin of it occurs 2, 4 lifnr like ztxlfa1-lifnr, 5 row_id like sy-index, 6 land1 like ztxlfa1-land1, 7 end of it. 8 9 *Do it this way: 10 select lifnr land1 from ztxlfa1 11 into corresponding fields of it "notice 'table' is omitted 12 where land1 = 'DE'. 13 append it. 14 endselect. 15 16 loop at it. 17 write: / it-lifnr, it-row_id, it-land1. 18 endloop. 19 refresh it. 20 21 *Not this way: 22 select * from ztxlfa1 23 where land1 = 'DE'. 24 move-corresponding ztxlfa1 to it. 25 append it. 26 endselect. 27 28 skip. 29 loop at it. 30 write: / it-lifnr, it-row_id, it-land1. 31 endloop. 32 free it.
The code in Listing 13.6 produces this output:
V11 0 DE V12 0 DE V8 0 DE V11 0 DE V12 0 DE V8 0 DE
Line 22 moves rows into work area ztxlfa1. Line 24 uses an additional statement-move-corresponding-to transfer the work area to the header line it. Line 25 then appends the header line to the body of it. This accomplishes the same result as the first select, but it involves an additional statement and so is less efficient.
Table 13.2 contains a list of the various forms of select
as it is used with internal tables and their relative efficiency.
They are in descending order of most-to-least efficient.
select into table it | Body |
select into corresponding fields of table it | Body |
select into it | Header line |
select into corresponding fields of it | Header line |
Before discussing the next topic, it will be helpful to become more acquainted with the sample tables ztxlfa1, ztxlfb1, ztxlfc1, and ztxlfc3; they will be frequently used in the examples and exercises that follow. These tables are based on the R/3 tables lfa1, lfb1, lfc1, and lfc3. Please review the structures of these tables within the DDIC as you read the descriptions below.
SAP designed R/3 to be usable by a conglomerate that consists of multiple companies. During initial R/3 configuration, a company code is assigned to each company in the conglomerate. The company code forms part of the primary key in many master data tables, which enables the conglomerate to keep the information for all of its companies in a single database. Management can run reports for an individual company code as well as consolidated reports spanning multiple companies.
Table lfa1 contains vendor master information that is consistent across all companies. Ignoring mandt, its primary key consists only of lifnr, the vendor number. The fields of lfa1, for example, are vendor name and address, telephone numbers, spoken language, and industry key (defined by the type of product the vendor produces, such as chemical, agricultural, and so on).
Table lfb1 contains vendor master information that is specific to a company. Its primary key consists of lifnr and bukrs: the company code field (see Figure 13.6). Stored within lfb1 are the company's account number with the vendor, reconciliation account number, withholding tax information, an interest calculation indicator, and so on.
Figure 13.6 : The relationship between the primary keys.
Figure 13.6 is the relationship between the primary keys of tables lfa1 through lfc3. They all begin with lifnr, and lfa1 uses it as the entire primary key. lfb1 uses the vendor number and company code. lfc1 uses vendor number, company code, and fiscal year. lfc3 uses those same fields plus a special G/L indicator.
Table lfc1 contains G/L (general ledger) transaction figures. Each row holds one fiscal year's worth of transaction figures for a vendor within a company. The primary key consists of lifnr, bukrs, and gjahr, the fiscal year.
The set of fields named umNNs, umNNh, and umNNu is repeated 16 times within an lfc1 row. Each of the first 12 sets contains the debit postings, credit postings, and sales for one posting period (usually a month) of the fiscal year. The last four sets are additional closing periods used to contain year-end accounting adjustments.
NOTE |
There can be either 12 or 13 posting periods in a fiscal year. This is deter-mined when the system is initially configured. If there are 13, then there are only three additional closing periods. |
Table lfc3 contains special G/L transaction figures. Special G/Ls use an alternative reconciliation account in the general ledger. Each row of lfc3 holds a summary of the special G/Ls for an entire fiscal year in three fields: saldv, solll, and habnl. These contain the balance carried forward and the total debit and credit posting for the fiscal year. The primary key is the same as lfc1, plus a special G/L indicator: shbkz. The value in this field indicates which alternative reconciliation account is used.
Most master data tables use a similar model for their primary key structures.
In this book, simplified versions of these tables are used: ztxlfa1, ztxlfb1, ztxlfc1, and ztxlfc3. These were created and populated by the setup routine.
After you fill an internal table with data, you often need to write the data out. This output will frequently contain summary information (such as totals) at the top or bottom of the report. There might also be interim summaries (such as subtotals) within the body of the report.
For example, suppose you need to write the G/L figures from ztxlfc1 for each vendor, with subtotals by fiscal year and a grand total at the bottom of the report.
To do this, you can read the data into an internal table and then, within loop at, use the following statements:
The first statement of each of these statement pairs-except for sum-controls when the code that lies between them is executed. This type of control is called a control break. Their purpose is to execute the code between them whenever a specific condition in the data is detected during the processing of the loop.
Use the at first and at last statements to perform processing during the first or last loop pass of an internal table.
The following is the syntax for the at first and at last statements.
loop at it. --- at first. --- endat. --- at last. --- endat. --- endloop.
where:
The following points apply:
The first time through the loop, the lines of code between at first and endat are executed. The last time through the loop, the lines of code between at last and endat are executed. If there are multiple occurrences of at first, they are all executed. at last behaves in a similar fashion.
Use at first for:
Use at last for:
Listing 13.7 shows a sample program that uses these constructs.
Listing 13.7 Using at first to Write Headings and
at last to Underline the Last Line
1 report ztx1307. 2 tables ztxlfc3. 3 data it like ztxlfc3 occurs 25 with header line. 4 select * from ztxlfc3 into table it where shbkz = 'Z'. 5 loop at it. 6 at first. 7 write: / 'Vendor', 8 12 'Cpny', 9 17 'Year', 10 22 'Bal C/F'. 11 uline. 12 endat. 13 write: / it-lifnr, 14 12 it-bukrs, 15 17 it-gjahr, 16 22 it-saldv. 17 at last. 18 write: / '----------', 19 12 '----', 20 17 '----', 21 22 '-------------------'. 22 endat. 23 endloop. 24 free it.
The code in Listing 13.7 produces this output:
Vendor Cpny Year Bal C/F ---------------------------------------- 1000 1000 1995 0.00 1000 1000 1996 5,000.00 1000 1000 1998 4,000.00 1040 4000 1997 0.00 1070 2000 1997 1,000.00 1090 2000 1997 250.50 V1 1000 1992 1,000.00 V1 3000 1990 1,000.00 V1 3000 1994 1,000.00 V4 4000 1997 100.00 V6 2000 1997 1,000.00 V6 4000 1997 0.00 ---------- ---- ---- -------------------
Between the at first and endat, or between the at last and endat, the component values of the work area row will not contain any data. The default key fields are filled with * (asterisks) and the numeric fields are set to zeros. The endat restores the contents to the values they had prior to entering the at. Changes to the work area within at and endat are lost.
Listing 13.8 demonstrates that the components of the default key fields are filled with asterisks, and the non-key fields filled with zeros, inside an at or endat statement.
Listing 13.8 The Contents of the Internal Table
Fields Between at and endat
1 report ztx1308. 2 tables ztxlfc3. 3 data it like ztxlfc3 occurs 1 with header line. 4 select * up to 1 rows from ztxlfc3 into table it. 5 loop at it. 6 write: / 'Before ''at first'':', 7 / it-lifnr, it-bukrs, it-gjahr, it-shbkz, it-saldv, 8 it-solll. 9 at first. 10 write: / 'Inside ''at first'':', 11 / it-lifnr, it-bukrs, it-gjahr, it-shbkz, it-saldv, 12 it-solll. 13 it-lifnr = 'XXXX'. 14 endat. 15 write: / 'Between ''at first'' and ''at last'':', 16 / it-lifnr, it-bukrs, it-gjahr, it-shbkz, it-saldv, 17 it-solll. 18 at last. 19 write: / 'Inside ''at last'':', 20 / it-lifnr, it-bukrs, it-gjahr, it-shbkz, it-saldv, 21 it-solll. 22 it-lifnr = 'XXXX'. 23 endat. 24 write: / 'After ''at last'':', 25 / it-lifnr, it-bukrs, it-gjahr, it-shbkz, it-saldv, 26 it-solll. 27 endloop. 28 free it.
The code in Listing 13.8 produces this output:
Before 'at first': 1000 1000 1990 A 1,000.00 500.00 Inside 'at first': ********** **** **** * 0.00 0.00 Between 'at first' and 'at last': 1000 1000 1990 A 1,000.00 500.00 Inside 'at last': ********** **** **** * 0.00 0.00 After 'at last': 1000 1000 1990 A 1,000.00 500.00
Use the at new and at end of statements to detect a change in a column from one loop pass to the next. These statements enable you to execute code at the beginning and end of a group of records.
The following is the syntax for the at new and at end of statements.
sort by c. loop at it. --- at new c. --- endat. --- at end of c. --- endat. --- endloop.
where:
The following points apply:
Each time the value of c changes, the lines of code between at new and endat are executed. This block is also executed during the first loop pass or if any fields to the left of c change. Between at and endat, the numeric fields to the right of c are set to zero. The non-numeric fields are filled with asterisks (*). If there are multiple occurrences of at new, they are all executed. at end of behaves in a similar fashion.
A control level is the component named on a control break statement; it regulates the control break. For example, in the following code snippet, f2 is a control level because it appears on the at new statement.
loop at it. at new f2. "(some code here) endat. endloop.
It is said that a control break is triggered if the control level changes. This means that when the contents of the control level change, the code between the at and endat is executed.
A control break is also triggered if any of the fields prior to the control level in the structure change. Therefore, you should define the internal table structure to begin with the fields that form your control levels. You must also sort by all fields prior to and including c.
Between at and endat, numeric fields to the right of the control level will be zero and non-numeric fields will be filled with asterisks.
Figures 13.7 and 13.8 illustrate the use of at new.
This program provides a working example of Figures 13.7 and 13.8. It illustrates the point that a control break is triggered whenever the control level changes or any field prior to the control level changes.
Listing 13.9 The Code in Figures 13.7 and 13.8 Is
Reproduced in a Working Example
1 report ztx1309. 2 data: begin of it occurs 4, 3 f1, 4 f2, 5 end of it. 6 7 it = '1A'. append it. "Fill it with data 8 it = '3A'. append it. 9 it = '1B'. append it. 10 it = '2B'. append it. 11 12 sort it by f1. "it now looks like figure 13.7 13 loop at it. 14 at new f1. 15 write: / it-f1, it-f2. 16 endat. 17 endloop. 18 skip. 19 sort it by f2. "it now looks like figure 13.8 20 loop at it. 21 at new f2. 22 write: / it-f1, it-f2. 23 endat. 24 endloop. 25 skip. 26 sort it by f1 f2. "it now looks like figure 13.8 27 loop at it. 28 at new f1. 29 write: / it-f1. 30 endat. 31 at new f2. 32 write: /4 it-f2. 33 endat. 34 endloop. 35 free it.
The code in Listing 13.9 produces this output:
1 * 2 * 3 * 1 A 3 A 1 B 2 B 1 A B 2 B 3 A
The lines of code between at end of and endat are executed:
Listing 13.10 illustrates this statement.
Listing 13.10 How the at new and at end of Are Triggered
by the Field Changes and Control Level Changes
1 report ztx1310. 2 data: begin of it occurs 4, 3 f1, 4 f2, 5 end of it. 6 7 it = '1A'. append it. "Fill it with data 8 it = '3A'. append it. 9 it = '1B'. append it. 10 it = '2B'. append it. 11 12 sort it by f1. 13 loop at it. 14 at new f1. 15 write: / 'start of:', it-f1. 16 endat. 17 write: /4 it-f1. 18 at end of f1. 19 write: / 'end of:', it-f1. 20 endat. 21 endloop. 22 free it.
The code in Listing 13.10 produces this output:
start of: 1 1 1 end of: 1 start of: 2 2 end of: 2 start of: 3 3 end of: 3
CAUTION |
Do not use control break statements within a loop at it where . . . con-struct; the effects are unpredictable. |
Use the sum statement to calculate totals for the rows of a control level.
The following is the syntax for the sum statement.
at first/last/new/end of. --- sum. --- endat.
where:
Sum calculates a total for the current value of the control level that contains it. This is clearer if you imagine that it does the following:
Listing 13.11 illustrates this statement.
Listing 13.11 Using the sum Statement to Calculate
the Totals of a Control Level
1 report ztx1311. 2 data: begin of it occurs 4, 3 f1, 4 f2 type i, 5 f3 type i, 6 end of it. 7 8 it-f1 = 'A'. it-f2 = 1. it-f3 = 10. append it. 9 it-f1 = 'B'. it-f2 = 3. it-f3 = 30. append it. 10 it-f1 = 'A'. it-f2 = 2. it-f3 = 20. append it. 11 it-f1 = 'B'. it-f2 = 4. it-f3 = 40. append it. 12 13 sort it by f1. "it now looks like figure 13.9 14 loop at it. 15 at new f1. 16 sum. 17 write: / 'total:', it-f1, it-f2, it-f3. 18 endat. 19 write: /11 it-f2, it-f3. 20 endloop. 21 free it.
The code in Listing 13.11 produces this output:
total: A 3 30 1 10 2 20 total: B 7 70 3 30 4 40
TIP |
Summing can result in overflow because the total is placed into a field of the same length. Overflow causes a short dump with the error SUM_OVERFLOW. When using sum, avoid overflow by increasing the length of numeric fields before populating the internal table. |
Another statement you can use to perform control break processing is on change of. It behaves in a manner similar to at new.
The following is the syntax for the on change of statement.
on change of v1 [or v2 . . .]. --- [else. ---] endon.
where:
The following points apply:
on change of differs from at new in the following respects:
When a loop begins execution, the system creates a global auxiliary field for each field named in an on change of statement contained by the loop. On creation, these fields are given default initial values (blanks or zeros). They are freed when the loop ends.
Each time on change of is executed, the contents of its fields are compared with the contents of the global auxiliary fields. If they are different, the on change of is triggered and the auxiliary fields are updated with the new values. If they are the same, the code within on change of is not executed.
NOTE |
Because global auxiliary fields do not exist outside a loop, you cannot use on change of outside of a loop. |
This concept is graphically illustrated in Figures 13.11 through 13.16.
Figure 13.11: This is the first time though a loop using on change of.
Figure 13.12: When on change of is triggered, the auxiliary field is updated.
Figure 13.14: This is the third loop pass. on change of is triggered.
Figure 13.15: Here the auxiliary field is updated.
Figure 13.16: This is the fourth loop pass. on change of is not triggered.
Listing 13.12 illustrates the use of on change of.
Listing 13.12 Using on change of in Two Different
Ways: Inside of loop at and Inside of select
1 report ztx1312. 2 tables ztxlfa1. 3 data: begin of it occurs 4, 4 f1 type i, 5 f2, 6 f3 type i, 7 f4, 8 end of it. 9 10 it-f1 = 1. it-f2 = 'A'. it-f3 = 11. it-f4 = 'W'. append it. 11 it-f1 = 3. it-f2 = 'A'. it-f3 = 22. it-f4 = 'X'. append it. 12 it-f1 = 1. it-f2 = 'A'. it-f3 = 33. it-f4 = 'Y'. append it. 13 it-f1 = 2. it-f2 = 'A'. it-f3 = 44. it-f4 = 'Z'. append it. 14 15 loop at it. 16 on change of it-f2. 17 write: / it-f1, it-f2, it-f3, it-f4. 18 endon. 19 endloop. 20 write: / 'End of loop'. 21 22 * executing the same code again - the aux field still contains 'A' 23 loop at it. 24 at first. 25 write: / 'Looping without a reset...'. 26 endat. 27 on change of it-f2. 28 write: / it-f1, it-f2, it-f3, it-f4. 29 else. 30 write: / 'on change of not triggered for row', sy-tabix. 31 endon. 32 endloop. 33 write: / 'End of loop'. 34 35 *reset the aux field to blanks 36 clear it-f2. 37 on change of it-f2. 38 endon. 39 loop at it. 40 at first. 41 write: / 'Looping after reset...'. 42 endat. 43 on change of it-f2. 44 write: / it-f1, it-f2, it-f3, it-f4. 45 endon. 46 endloop. 47 write: / 'End of loop'. 48 free it. 49 50 select * from ztxlfa1 where land1 = 'US'. 51 on change of ztxlfa1-land1. 52 write: / 'land1=', ztxlfa1-land1. 53 endon. 54 endselect. 55 write: / 'End of select'. 56 57 *executing the same select again without a reset works find 58 select * from ztxlfa1 where land1 = 'US'. 59 on change of ztxlfa1-land1. 60 write: / 'land1=', ztxlfa1-land1. 61 endon. 62 endselect. 63 write: / 'End of select'.
In a 3.0F environment and above, the code in Listing 13.12 produces this output:
1 A 11 W End of loop Looping without a reset... on change of not triggered for row 2 on change of not triggered for row 3 on change of not triggered for row 4 End of loop Looping after reset... 1 A 11 W End of loop land1= US End of select land1= US End of select
The first time through a loop, the global auxiliary field is created with initial values (blanks or zeros). If the first value in a loop happens to be blank or zero, on change of will not be triggered. Listing 13.13 illustrates this problem and offers a solution.
Listing 13.13 The First Row of Loop #1 Does Not
Trigger on change of
1 report ztx1313. 2 data: begin of it occurs 4, 3 f1 type i, 4 end of it. 5 6 it-f1 = 0. append it. 7 it-f1 = 3. append it. 8 it-f1 = 1. append it. 9 it-f1 = 2. append it. 10 11 loop at it. "loop #1 12 write: / '==new row==', 12 it-f1. 13 on change of it-f1. 14 write: / 'f1 changed:', 12 it-f1. 15 endon. 16 endloop. 17 18 skip. 19 loop at it. "loop #2 20 write: / '==new row==', 12 it-f1. 21 on change of it-f1. 22 write: / 'f1 changed:', 12 it-f1. 23 else. 24 if sy-tabix = 1. 25 write: / 'f1 changed:', 12 it-f1. 26 endif. 27 endon. 28 endloop. 29 free it.
The code in Listing 13.13 produces this output:
==new row== 0 ==new row== 3 f1 changed: 3 ==new row== 1 f1 changed: 1 ==new row== 2 f1 changed: 2 ==new row== 0 f1 changed: 0 ==new row== 3 f1 changed: 3 ==new row== 1 f1 changed: 1 ==new row== 2 f1 changed: 2
NOTE |
In versions prior to 3.0F, only select and get statements automatically reset the global auxiliary fields. However, if you reset them yourself, you can use on change of in any type of loop. Use this code to perform the reset: clear f1. on change of f1. endon. clear moves initial values to f1. on change of compares f1 to the global auxiliary field. If they differ, the empty on change of overwrites it with initial values. Use this to fill the auxiliary fields with any value from f1. |
NOTE |
Don't use select and append. Whenever possible, use select into table instead. Don't use order by with select into table. Use sort on the internal table instead. |
The first part of this chapter seems to place a lot of emphasis on efficiency. I'm just learning and I find all of these methods confusing. Does this really make that much of a difference, or are you splitting hairs? | |
Experience has taught me that students who learn correctly the first time are far better off. ABAP/4 programs often process gargantuan amounts of data, so unfortunately, "easy code" often translates into "overhead overhead overhead." If you only learn easy methods first, they become habits and are difficult to discard. Poor code affects the performance of your programs and of the system. |
The Workshop provides two ways for you to affirm what you've learned in this chapter. The Quiz section poses questions to help you solidify your understanding of the material covered and the Exercise section provides you with experience in using what you have learned. You can find answers to the quiz questions and exercises in Appendix B, "Answers to Quiz Questions and Exercises."
Copy program ztx1110 to -e1301. Change it so that if a row is found and the contents of any fields in the found row differ from what is in the header line, overwrite the found row with the current contents of the header line. The output should look like this:
10 1 XX 1 20 2 XX 2 30 3 XX 3 40 4 XX 4 50 5 XX 5
Hint: Use the comparing addition on the read table statement.
The following program produces a short dump. Describe the problem, copy the program to -e1302 and fix it.
report zty1302 tables: ztxlfa1. data: begin of it occurs 10, lifnr like ztxlfa1-lifnr, land1 like ztxlfa1-land1, end of it. select * from ztxlfa1 into table it. loop at it. write: / it-lifnr, it-land1. endloop.