使用排序表和散列表

学习目标

  • 处理排序表和散列表。

试用:代码示例准备

  1. 选择 Ctrl + Shift + N 创建新的 ABAP 资源库对象。

  2. 在过滤器字段中键入单词 database ,标记搜索结果 数据库表 ,然后选择 继续 。

  3. 将新对象分配到正在使用的包并输入名称 ZS4D401_FLIGHTS 。输入表的描述,然后选择 下一步 。

  4. 将对象分配到传输请求并选择 完成 。

  5. 在大括号之间输入以下代码:

key client : abap.clnt not null;
key carrier_id : /dmo/carrier_id not null;
key connection_id : /dmo/connection_id not null;
key flight_date : /dmo/flight_date not null;
@Semantics.amount.currencyCode : 'zs4d401_flights.currency_code'
price : /dmo/flight_price;
currency_code : /dmo/currency_code;
plane_type_id : /dmo/plane_type_id;
seats_max : /dmo/plane_seats_max;
seats_occupied : /dmo/plane_seats_occupied;
  1. 选择 Ctrl + F3 激活表。

  2. 创建名为 ZCL_FILL_ZS4D401_FLIGHTS 的新 ABAP 类。

  3. 在 main 方法的实施中输入以下代码:

DATA flights TYPE TABLE OF /dmo/flight.
DATA insert_tab TYPE TABLE OF /dmo/flight.
DELETE FROM ZS4D401_flights.
SELECT FROM /dmo/flight FIELDS * ORDER BY carrier_Id, connection_id INTO TABLE @flights .
LOOP AT flights INTO DATA(first_date).
* Original table flights has 2 flights per connection. Only process the first
IF sy-tabix MOD 2 = 0. CONTINUE. ENDIF.
*Extend flight dates by 2000 days
DO 2000 TIMES.
APPEND first_date TO insert_tab.
first_date-flight_date += 1.
ENDDO.
* ENDIF.
ENDLOOP.
* Read highest connection number for each flight
SELECT FROM /dmo/flight AS main FIELDS carrier_Id, connection_id, flight_date, price, currency_code, plane_type_id
WHERE connection_id = ( SELECT MAX( connection_id ) FROM /dmo/flight WHERE carrier_id = main~carrier_id )
AND flight_Date = ( SELECT MIN( flight_date ) FROM /dmo/flight WHERE carrier_id = main~carrier_id AND connection_id = main~connection_id )
GROUP BY carrier_id, connection_Id, flight_date, price, currency_code, plane_type_id
ORDER BY carrier_id, connection_id
INTO TABLE @DATA(max).
*Add 50 new connection numbers and 2000 days of flights for each
LOOP AT max INTO DATA(line).
DO 50 TIMES.
line-connection_id += 1.
line-plane_type_id = SWITCH #( CONV i( line-connection_id ) MOD 2 WHEN 0 THEN 'A330' WHEN 1 THEN 'A350' ).
first_date = CORRESPONDING #( line ).
DATA(repetitions) = COND i( WHEN line-carrier_id = 'LH' AND line-connection_id = '0405' THEN 4000 ELSE 2000 ).
DO repetitions TIMES.
first_date-seats_max = 220.
APPEND first_date TO insert_tab.
first_date-flight_date += 1.
ENDDO.
ENDDO.
ENDLOOP.
SORT insert_tab BY carrier_Id connection_id flight_date.
DELETE ADJACENT DUPLICATES FROM insert_tab COMPARING carrier_id connection_id flight_date.
INSERT ZS4D401_flights FROM TABLE @insert_tab.
out->write( |Generated { sy-dbcnt } rows in table ZS4D401_flights| ).
  1. 选择 Ctrl + F3 激活类。

  2. 选择 F9 以运行类。 main 方法将在接下来的两个部分中填充代码片段所需的额外数据库表。

排序表和散列表

到目前为止,我们已使用标准内部表。标准表中的数据不按特定排序顺序保存。因此,当您从表中读取数据时,系统必须按顺序搜索表,直到找到您需要的行。根据表的大小,这可能需要一些时间。

可以使用排序表或散列表优化内部表的读取访问性能。

在排序表中,表的内容始终根据关键字段按升序排序。向表中插入新记录时,系统会确保将其放置在正确的位置。由于数据始终进行排序,因此系统可以比从标准表中更有效地检索记录(只要您遵循特定规则)。

哈希表使用特殊的哈希算法进行管理。这可确保即使表非常大,系统也可以非常快速地检索记录。但是,这种性能提升仅在非常特殊的情况下有效。

访问排序表

在本视频中,您将了解如何访问排序表。

视频:本节含 SAP 官方视频(视频 ID 1_8nwlfj07),需在 learning.sap.com 在线观看。

声明散列表时,必须使用唯一键定义它 – 不允许重复。

观看视频,了解如何访问散列表。

视频:本节含 SAP 官方视频(视频 ID 1_uw3pasov),需在 learning.sap.com 在线观看。

何时使用标准表、排序表和散列表

如果填充内部表并随后对整个表实施循环,则可以使用标准表。排序表和散列表的性能优化无关紧要,因为循环中没有过滤器。

此示例显示何时应使用排序表和散列表。它包含使用 where 子句中的第一个关键字段的表连接循环。这是使用排序表的情况,因为即使未完全指定键值,系统也可以使用二进制搜索技术。

程序在循环中查找表 航空公司 中的条目。因此,使用完整键值对表进行重复访问。这是使用散列表的理想情况。

标准表、排序表和散列表的比较运行时

此图比较了标准表、排序表和散列表的运行时。

填充排序表和散列表

使用标准表时,可以使用 APPEND 语句将工作区的内容添加到表的末尾。这不适用于散列表,并会导致语法错误。

从技术上讲,可以使用 APPEND 语句填充排序表。但是,如果根据排序顺序,您尝试附加的行不属于表末尾,则可能会造成运行时错误。在我们的示例中,会发生这种情况。

填充散列表和安全填充排序表的方法是使用 INSERT 语句。INSERT 确保在表中的正确位置插入新行。

试用:排序和散列表

  1. 创建实施接口 IF_OO_ADT_CLASSRUN 的新类。

  2. 将以下代码片段复制到方法 if_oo_adt_classrun~main() 的实施部分:

* Run this class using the ABAP Profiler to measure relative access times for standard, sorted, and hashed tables
data(flights) = new lcl_flights( ).
flights->access_standard( ).
flights->access_sorted( ).
flights->access_hashed( ).
out->write( |Done| ).
  1. 切换到 局部类型 标签并将以下代码片段复制到编辑器中:

CLASS lcl_flights DEFINITION.
PUBLIC SECTION.
METHODS constructor.
METHODS access_standard.
METHODS access_sorted.
METHODS access_hashed.
PRIVATE SECTION.
DATA standard_table TYPE STANDARD TABLE OF zs4d401_flights WITH NON-UNIQUE KEY carrier_id connection_id flight_date.
DATA sorted_table TYPE SORTED TABLE OF zs4d401_flights WITH NON-UNIQUE KEY carrier_id connection_id flight_date.
DATA hashed_table TYPE HASHED TABLE OF zs4d401_flights WITH UNIQUE KEY carrier_id connection_id flight_date.
DATA key_carrier_id TYPE /dmo/carrier_id.
DATA key_connection_id TYPE /dmo/connection_id.
DATA key_date TYPE /dmo/flight_date.
METHODS set_line_to_read.
ENDCLASS.
CLASS lcl_flights IMPLEMENTATION.
METHOD access_hashed.
DATA(result) = hashed_table[ carrier_Id = me->key_carrier_id connection_Id = me->key_connection_id flight_date = me->key_date ].
ENDMETHOD.
METHOD access_sorted.
DATA(result) = sorted_table[ carrier_Id = me->key_carrier_id connection_Id = me->key_connection_id flight_date = me->key_date ].
ENDMETHOD.
METHOD constructor.
SELECT FROM zs4d401_flights FIELDS * INTO TABLE @standard_table.
SELECT FROM zs4d401_flights FIELDS * INTO TABLE @sorted_table.
SELECT FROM zs4d401_flights FIELDS * INTO TABLE @hashed_table.
set_line_to_read( ).
ENDMETHOD.
METHOD access_standard.
DATA(result) = standard_table[ carrier_Id = me->key_carrier_id connection_Id = me->key_connection_id flight_date = me->key_date ].
ENDMETHOD.
METHOD set_line_to_read.
DATA(line) = standard_table[ CONV i( lines( standard_table ) * '0.65' ) ].
me->key_carrier_id = line-carrier_Id.
me->key_connection_Id = line-connection_id.
me->key_date = line-flight_date.
ENDMETHOD.
ENDCLASS.
  1. 选择 Ctrl + F3 激活类。

  2. 右键单击编辑器,然后选择 参数文件作为... → ABAP 应用程序(控制台) 。

  3. 在详细信息 中,取消选中复选框 SQL 数据库访问 并选择 完成 。

  4. 切换到 ABAP 分析器 透视图并双击跟踪项目(可能需要先刷新显示)。

  5. 选择 Hit List 。

  6. 比较方法 access_standard( ) 、 access_sorted( ) 和 access_hashed( ) 的运行时。

使用排序表和散列表

您发现代码包含表类型为 标准表 的大型内部表。在代码中分析访问这些表的方式,发现它们只能通过键访问。为提高性能,将内部表的定义更改为排序表或哈希表,具体取决于其访问方式。

模板:

  • /LRN/CL_S4D401_ITS_FSYM(全局类)

解决方案:

  • /LRN/CL_S4D401_ITS_TABLE_KIND(全局类)

任务 1: 复制模板(可选)

复制模板类 /LRN/CL_S4D401_ITS_FSYM。如果已完成上一练习,则可以跳过此任务并继续编辑类 ZCL_##_SOLUTION

步骤

  1. 将类 /LRN/CL_S4D401_ITS_FSYM 复制到您自己的包中的类(建议名称:ZCL_##_SOLUTION,其中 ## 代表组编号)。

  2. 在项目浏览器中,右键单击类 /LRN/CL_S4D401_ITS_FSYM 以打开上下文菜单。

  3. 从上下文菜单中,选择复制....

  4. 在包字段中输入的名称。在名称字段中,输入 ZCL_##_SOLUTION,其中 ## 代表组编号。

  5. 选择下一步

  6. 确认传输请求并选择完成

  7. 激活复制。

  8. Ctrl + F3 激活类。

任务 2: 调整 CONNECTIONS_BUFFER

使用本地类 LCL_PASSENGER_FLIGHT 的静态属性 **connections_buffer**的使用位置列表。更改 connections_buffer 的定义,以便最适合其访问方式。

步骤

  1. 导航到本地类 LCL_PASSENGER_FLIGHT 中静态属性 connections_buffer 的定义。

  2. 例如,在 大纲 视图中展开 LCL_PASSENGER_FLIGHT 节点并选择 CONNECTIONS_BUFFER

  3. connections_buffer 属性创建使用位置列表

  4. 在 CLASS-DATA 语句中,右键单击 connections_buffer 并选择获取调用清单。或者,您可以将光标放在 connections_buffer 上并按 Ctrl + Shift + G

  5. 在编辑器下方的搜索视图中分析列表。将光标置于查找结果上以显示完整的语句。

  6. 调整 connections_buffer 属性的定义。选择最适合读取访问的表类型和键值定义。

  7. 将表类型更改为 HASHED TABLE

  8. 将键值定义更改为以 carrier_idconnection_id 作为键值字段的唯一键值。

  9. 如下调整代码:

*  CLASS-DATA connections_buffer TYPE TABLE OF st_connections_buffer.
CLASS-DATA connections_buffer
TYPE HASHED TABLE OF st_connections_buffer
WITH UNIQUE KEY carrier_id connection_id.

任务 3: 调整 FLIGHTS_BUFFER

使用本地类 LCL_PASSENGER_FLIGHT 的静态属性 **flights_buffer**的使用位置列表。更改 flights_buffer 的定义,以便最适合访问 属性的方式。

步骤

  1. 导航到本地类 LCL_PASSENGER_FLIGHT 中静态属性 flights_buffer 的定义。

  2. 例如,展开概要视图中的节点 LCL_PASSENGER_FLIGHT,然后选择 FLIGHTS_BUFFER

  3. flights_buffer 属性创建使用位置清单

  4. 在 CLASS-DATA 语句中,右键单击 flights_buffer 并选择获取调用清单。或者,您可以将光标放在 flights_buffer 上并按 Ctrl + Shift + G

  5. 在编辑器下方的搜索视图中分析列表。将光标置于查找结果上以显示完整的语句。

  6. 调整属性 flights_buffer 的定义。选择最适合读取访问的表类型和键值定义。

  7. 将表类型更改为 SORTED TABLE

  8. 将键定义更改为以 carrier_idconnection_idflight_date 作为键字段的非唯一键。

  9. 如下调整代码:

*  CLASS-DATA: flights_buffer TYPE TABLE OF st_flights_buffer.
CLASS-DATA: flights_buffer
TYPE SORTED TABLE OF st_flights_buffer
WITH NON-UNIQUE KEY carrier_id connection_id flight_date.
  1. 问题 视图中分析语法错误。

  2. 分析编辑器下方的 问题 视图。

  3. 移除或注释冗余 SORT 语句。

  4. 如下调整代码:

*  SORT flights_buffer BY carrier_id connection_id flight_date.
  1. 以控制台应用形式激活并测试全局类。

  2. Ctrl + F3

  3. F9

任务 4: 量化改进

使用 ABAP 概要分析量化通过调整两个内部表的类型实现的性能改进。

步骤

  1. 在项目浏览器中找到模板类 /LRN/CL_S4D401_ITS_FSYM

  2. 将模板类 /LRN/CL_S4D401_ITS_FSYM 作为 ABAP 控制台应用进行剖析。

  3. 右键单击项目浏览器中的模板类 /LRN/CL_S4D401_ITS_FSYM,然后选择参数文件作为ABAP 应用程序(控制台)

  4. 选择完成

  5. 等待控制台视图出现或更新。

  6. 返回到您自己的类(ZCL_##_SOLUTION,其中 ## 是您的组编号)并以相同方式对其进行剖析。

  7. 在项目浏览器中找到您自己的类。

  8. 右键单击项目浏览器中的类,然后选择参数文件为ABAP 应用程序(控制台)

  9. 等待控制台视图出现或更新。

  10. 切换到 ABAP 概要分析 透视图并比较两个 ABAP 跟踪。

  11. 要打开 ABAP 概要分析透视图,请选择 窗口透视图打开透视图其他....从 eclipse 菜单。然后选择 ABAP 概要分析打开。(或者,如果可用,您可以选择 eclipse 工具栏右上角的 ABAP 概要分析 图标。)

  12. 在底部的 ABAP 跟踪视图中,展开与 ABAP 云项目相对应的节点。

  13. F5 键刷新跟踪结果清单。前两个跟踪结果应像您自己的 ABAP 类和模板类一样命名。

  14. 比较运行时 (s) 列中的值。

  15. 分析后,返回到 ABAP 透视图。

  16. 像以前一样切换透视图。