ABAP基础知识 用SQL处理内表

前言

ABAP升级新语法,很多新语法带来了更简洁的代码及更容易理解的代码形式.

下图是截至S4 1909版本的ABAP语法升级信息(该截图来自ABAP中的帮助)

在项目中也越来越多的使用新语法来处理逻辑, 内表是ABAP中最关注处理性能的部分,新语法允许把内表引入到SQL语句中执行.因此带来了使用SQL语句处理内表的新方式

本文通过示例比较内表常用的几种处理的新旧语法的性能比较

示例程序的数据样本

来自ACDOCA表,该系统中记录数30,129

SQL处理内表

用@标记引入的内表且必须给出别名,into子句要放到最后

@内表 as a

获取一条记录

分组小计

内表关联物理表(推荐使用,取代for all entries in )

通过关键字访问内表

新语法可以把内表当作一个表引入到select 语句中, 但限制了每次只能引入一个内表

因此可以通过SELECT 语句读取内表的单条记录

但是通过示例程序对二分法,哈希法及SQL法的比较

对标准内表的SELECT SINGLE 访问性能很差.耗时远远高于哈希,二分法读取

通过关键字访问不同类型的内表

基于上一个结论联想到内表的三种不同类型(标准,排序,哈希)在正常read读取的差异.这个差异应该也会存在于SELECT SINGLE 读取内表. 因此比较了三种不同内表的SELECT SINGLE差异.

从下图中可以看出SELECT SINGLE 读取不同类型的内表,耗时是不同的. 读取哈希内表耗时最低, 但耗时也比 read table 读取哈希表多很多( 117 VS 12 )

内表小计的比较

之前写过文章介绍了几种内表小计的方式

详见链接

无峰,公众号:ABAP 技巧与实战ABAP基础知识 内表汇总数据的方式

为了和select 语法比较性能.示例程序中给出了几种常用方法

  • COLLECT语句

  • 循环用哈希表统计

  • 用LOOP GROUP统计

  • 用SELECT统计

比较结果出乎意料: SELECT 统计内表(标准内表)耗时低于其它方式. 看来使用SQL语句统计内表可以作为首选方式了.

还有个更吃惊的结论: 直接从数据库统计性能远高于从内表统计( 9478 VS 35754 ),这还不包括从数据库读取数据到内表的耗时.

看来对于统计类的数据处理逻辑下沉到数据库层级是非常有必要的

程序源代码

properties 复制代码
*&---------------------------------------------------------------------*
*& Report ZTS_ITAB_PROC
*&---------------------------------------------------------------------*
*&通过程序比较内表的关键字访问,统计的性能差异
*&P_1  比较读取内表的单条记录
*&P_2  通过select single 访问内表(三种内表类型比较)
*&P_3  内表几种小计方式比较
*&---------------------------------------------------------------------*
REPORT zts_itab_proc.


PARAMETERS:
  p_1 AS CHECKBOX,
  p_2 AS CHECKBOX,
  p_3 AS CHECKBOX.


DATA: lt_hs TYPE HASHED TABLE OF  acdoca WITH UNIQUE KEY rclnt rldnr rbukrs gjahr belnr docln, "哈希内表
      lt_st TYPE TABLE OF acdoca,  "通常内表
      lt_ss TYPE SORTED TABLE OF acdoca WITH NON-UNIQUE KEY rclnt rldnr rbukrs gjahr belnr docln.
IF p_1 = 'X'.
  cl_demo_output=>next_section('内表关键字访问(微秒)').
  cl_demo_output=>next_section('-内表关键字访问-哈希').
  PERFORM frm_access_with_key_hs.
  cl_demo_output=>next_section('-内表关键字访问-二分').
  PERFORM frm_access_with_key_st.
  cl_demo_output=>next_section('-内表关键字访问-select').
  PERFORM frm_access_with_key_select.
  cl_demo_output=>write('结论:哈希会优化性能,二分法性能也可以,内表作为表的select 语句没有性能优化').
ENDIF.


IF p_2 = 'X'.
  cl_demo_output=>next_section('select single 内表(微秒)').
  cl_demo_output=>write('结论:通过SELECT single 语句访问内表性能差于通过read table 访问内表,内表类型对SELECT SINGLE 访问存在优化').
  PERFORM frm_access_by_select_itab.
ENDIF.


IF p_3 = 'X'.
  PERFORM frm_compare_sum.
ENDIF.


 perform frm_inner_join.


cl_demo_output=>display( ).
*&---------------------------------------------------------------------*
*& Form frm_access_with_key
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& -->  p1        text
*& <--  p2        text
*&---------------------------------------------------------------------*
FORM frm_access_with_key_hs .
  "耗时考虑,哈希表的读取,及访问
  GET RUN TIME FIELD DATA(t1).
  SELECT * FROM acdoca INTO TABLE lt_hs.
  GET RUN TIME FIELD DATA(t2).
  READ TABLE lt_hs INTO DATA(ls_hs)
     WITH TABLE KEY rclnt = '800' rldnr = '0L' rbukrs = '2450' gjahr = '2021' belnr = '0100000239' docln = '000003' .
  GET RUN TIME FIELD DATA(t3).


  cl_demo_output=>write( |取数耗时:  { t2 - t1 }| ).
  cl_demo_output=>write( |读取耗时:  { t3 - t2 }| ).
  cl_demo_output=>write( |总耗时  :  { t3 - t1 }| ).


ENDFORM.


FORM frm_access_with_key_st .
  "耗时考虑,哈希表的读取,及访问
  GET RUN TIME FIELD DATA(t1).
  SELECT * FROM acdoca INTO TABLE lt_st ORDER BY rldnr rbukrs gjahr belnr docln.
  GET RUN TIME FIELD DATA(t2).
  READ TABLE lt_st INTO DATA(ls_st)
   WITH KEY rldnr = '0L' rbukrs = '2450' gjahr = '2021' belnr = '0100000239' docln = '000003' BINARY SEARCH.
  GET RUN TIME FIELD DATA(t3).


  cl_demo_output=>write( |取数耗时:  { t2 - t1 }| ).
  cl_demo_output=>write( |读取耗时:  { t3 - t2 }| ).
  cl_demo_output=>write( |总耗时  :  { t3 - t1 }| ).


ENDFORM.


FORM frm_access_with_key_select.
  DATA: ls_st TYPE acdoca.
  "耗时考虑,哈希表的读取,及访问
  GET RUN TIME FIELD DATA(t1).
  SELECT * FROM acdoca INTO TABLE lt_st . " ORDER BY rldnr rbukrs gjahr belnr docln.
  GET RUN TIME FIELD DATA(t2).
  SELECT SINGLE * FROM @lt_st AS a
    WHERE rldnr = '0L' AND  rbukrs = '2450' AND  gjahr = '2021' AND  belnr = '0100000239' AND docln = '000003'
     INTO @ls_st.
  GET RUN TIME FIELD DATA(t3).
  READ TABLE lt_st INTO ls_st
   WITH KEY rldnr = '0L' rbukrs = '2450' gjahr = '2021' belnr = '0100000239' docln = '000003'.
    GET RUN TIME FIELD DATA(t4).
  READ TABLE lt_st INTO ls_st
   WITH KEY rldnr = '0L' rbukrs = '2450' gjahr = '2024' belnr = '0100001960' docln = '000001'.
  GET RUN TIME FIELD DATA(t5).


  cl_demo_output=>write( |取数耗时          :  { t2 - t1 }| ).
  cl_demo_output=>write( |读取耗时          :  { t3 - t2 }| ).
  cl_demo_output=>write( |READ耗时(前面的行):  { t4 - t3 }| ).
  cl_demo_output=>write( |READ耗时(后面的行):  { t5 - t4 }| ).
  cl_demo_output=>write( |SELECT总耗时      :  { t3 - t1 }| ).




ENDFORM.


FORM frm_access_by_select_itab.
  DATA: ls_st TYPE acdoca.
  "耗时考虑,哈希表的读取,及访问
  GET RUN TIME FIELD DATA(t1).
  SELECT * FROM acdoca INTO TABLE lt_st ORDER BY rldnr rbukrs gjahr belnr docln.
  GET RUN TIME FIELD DATA(t2).
  lt_hs[] = lt_st[].
  GET RUN TIME FIELD DATA(t3).
  lt_ss[] = lt_st[].
  GET RUN TIME FIELD DATA(t4).


  SELECT SINGLE * FROM @lt_st AS a
    WHERE rclnt = '800' AND rldnr = '0L' AND  rbukrs = '2450' AND  gjahr = '2021' AND  belnr = '0100000239' AND docln = '000003'
     INTO @ls_st.
  GET RUN TIME FIELD DATA(t5).
  SELECT SINGLE * FROM @lt_ss AS a
    WHERE rclnt = '800' AND rldnr = '0L' AND  rbukrs = '2450' AND  gjahr = '2021' AND  belnr = '0100000239' AND docln = '000003'
     INTO @ls_st.
  GET RUN TIME FIELD DATA(t6).
  SELECT SINGLE * FROM @lt_hs AS a
    WHERE rclnt = '800' AND rldnr = '0L' AND  rbukrs = '2450' AND  gjahr = '2021' AND  belnr = '0100000239' AND docln = '000003'
     INTO @ls_st.
  GET RUN TIME FIELD DATA(t7).
  cl_demo_output=>write( |取数耗时      :  { t2 - t1 }| ).
  cl_demo_output=>write( |赋值哈希表耗时:  { t3 - t2 }| ).
  cl_demo_output=>write( |赋值排序表耗时:  { t4 - t3 }| ).
  cl_demo_output=>write( |标准表取数耗时:  { t5 - t4 }| ).
  cl_demo_output=>write( |排序表取数耗时:  { t6 - t5 }| ).
  cl_demo_output=>write( |哈希表取数耗时:  { t7 - t6 }| ).


ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_compare_sum
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& -->  p1        text
*& <--  p2        text
*&---------------------------------------------------------------------*
FORM frm_compare_sum .


  DATA: ls_st TYPE acdoca.
  "耗时考虑,哈希表的读取,及访问
  GET RUN TIME FIELD DATA(t1).
  SELECT * FROM acdoca INTO TABLE lt_st ORDER BY rldnr rbukrs gjahr belnr docln.
  GET RUN TIME FIELD DATA(t2).


  PERFORM sum_collect.


  GET RUN TIME FIELD DATA(t3).
  PERFORM sum_hash.


  GET RUN TIME FIELD DATA(t4).
  PERFORM sum_select.


  GET RUN TIME FIELD DATA(t5).


  PERFORM sum_loop_group.
  GET RUN TIME FIELD DATA(t6).


  PERFORM sum_select_table.
  GET RUN TIME FIELD DATA(t7).


  cl_demo_output=>write( |取数耗时       :  { t2 - t1 }| ).
  cl_demo_output=>write( |COLLECT统计耗时:  { t3 - t2 }| ).
  cl_demo_output=>write( |哈希表统计耗时 :   { t4 - t3 }| ).
  cl_demo_output=>write( |SELECT统计耗时 :   { t5 - t4 }| ).
  cl_demo_output=>write( |LOOP GROUP耗时 :   { t6 - t5 }| ).
  cl_demo_output=>write( |数据库统计耗时 :   { t7 - t6 }| ).


ENDFORM.
*&---------------------------------------------------------------------*
*& Form sum_collect
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& -->  p1        text
*& <--  p2        text
*&---------------------------------------------------------------------*
FORM sum_collect .
  DATA: BEGIN OF ls_sum,
          racct TYPE acdoca-racct,
          hsl   TYPE acdoca-hsl,
        END OF ls_sum.
  DATA: lt_sum LIKE TABLE OF ls_sum.
  DATA: lt_sum_hs LIKE HASHED TABLE OF ls_sum WITH UNIQUE KEY racct.


  LOOP AT lt_st INTO DATA(ls_st).
    MOVE-CORRESPONDING ls_st TO ls_sum.
    COLLECT ls_sum INTO lt_sum.
  ENDLOOP.
ENDFORM.


FORM sum_hash .
  DATA: BEGIN OF ls_sum,
          racct TYPE acdoca-racct,
          hsl   TYPE acdoca-hsl,
        END OF ls_sum.
  DATA: lt_sum LIKE TABLE OF ls_sum.
  DATA: lt_sum_hs LIKE HASHED TABLE OF ls_sum WITH UNIQUE KEY racct.


  LOOP AT lt_st INTO DATA(ls_st).
    READ TABLE lt_sum_hs ASSIGNING FIELD-SYMBOL(<ls_sum>) WITH TABLE KEY racct = ls_st-racct.
    IF sy-subrc <> 0.
      CLEAR ls_sum.
      ls_sum-racct = ls_st-racct.
      INSERT ls_sum INTO TABLE lt_sum_hs ASSIGNING <ls_sum>.
    ENDIF.
    <ls_sum>-hsl = <ls_sum>-hsl + ls_st-hsl.
  ENDLOOP.
ENDFORM.


FORM sum_select .
  DATA: BEGIN OF ls_sum,
          racct TYPE acdoca-racct,
          hsl   TYPE acdoca-hsl,
        END OF ls_sum.
  DATA: lt_sum LIKE TABLE OF ls_sum.
  DATA: lt_sum_hs LIKE HASHED TABLE OF ls_sum WITH UNIQUE KEY racct.


  SELECT a~racct,SUM( a~hsl ) AS hsl
    FROM @lt_st AS a
    GROUP BY a~racct
    INTO CORRESPONDING FIELDS OF TABLE @lt_sum.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form sum_loop_group
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& -->  p1        text
*& <--  p2        text
*&---------------------------------------------------------------------*
FORM sum_loop_group .
  DATA: BEGIN OF ls_sum,
          racct TYPE acdoca-racct,
          hsl   TYPE acdoca-hsl,
        END OF ls_sum.
  DATA: lt_sum LIKE TABLE OF ls_sum.
  DATA: lt_sum_hs LIKE HASHED TABLE OF ls_sum WITH UNIQUE KEY racct.


  LOOP AT lt_st INTO DATA(ls_gp) GROUP BY ( racct = ls_gp-racct ).
    CLEAR ls_sum.
    ls_sum-racct = ls_gp-racct.
    LOOP AT GROUP ls_gp INTO DATA(ls_st).
      ls_sum-hsl = ls_sum-hsl + ls_st-hsl.
    ENDLOOP.
    APPEND ls_sum TO lt_sum.
  ENDLOOP.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form sum_SELECT_TABLE
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& -->  p1        text
*& <--  p2        text
*&---------------------------------------------------------------------*
FORM sum_select_table .
  DATA: BEGIN OF ls_sum,
          racct TYPE acdoca-racct,
          hsl   TYPE acdoca-hsl,
        END OF ls_sum.
  DATA: lt_sum LIKE TABLE OF ls_sum.
  DATA: lt_sum_hs LIKE HASHED TABLE OF ls_sum WITH UNIQUE KEY racct.


  SELECT a~racct,SUM( a~hsl ) AS hsl
    FROM acdoca AS a
    GROUP BY a~racct
    INTO CORRESPONDING FIELDS OF TABLE @lt_sum.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_inner_join
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& -->  p1        text
*& <--  p2        text
*&---------------------------------------------------------------------*
FORM frm_inner_join .
  data: lt_bkpf like table of bkpf.
  select distinct b~* from @lt_st as a inner join bkpf as b on b~belnr = a~belnr and b~gjahr = a~gjahr and b~bukrs = a~rbukrs
    into table @lt_bkpf.
ENDFORM.

总结

本文只是简单比较了一下内表的常规语句与SELECT 语句操作内表的性能差异,这次比较的结论会作用于后续项目中的程序性能优化

单条读取内表优选方式:

  • READ 哈希

  • READ 排序 或 READ 标准 binary search

当然 SELECT SINGLE 读取内表可以使用WHERE 条件添加一些特殊条件. 这个远比read 语句可以使用条件更丰富. 在一些性能要求不高的程序中, 可以用它来实现一些特殊逻辑

尽量从数据库表中获取聚合的数据(汇总),如果一定要在内表基础上小计或总计, 直接使用SQL语句处理内表也是一个优选.

相关推荐
Mr.1312 分钟前
数据库的三范式是什么?
数据库
Cachel wood18 分钟前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Python之栈26 分钟前
【无标题】
数据库·python·mysql
风_流沙38 分钟前
java 对ElasticSearch数据库操作封装工具类(对你是否适用嘞)
java·数据库·elasticsearch
亽仒凣凣1 小时前
Windows安装Redis图文教程
数据库·windows·redis
亦世凡华、1 小时前
MySQL--》如何在MySQL中打造高效优化索引
数据库·经验分享·mysql·索引·性能分析
YashanDB1 小时前
【YashanDB知识库】Mybatis-Plus调用YashanDB怎么设置分页
数据库·yashandb·崖山数据库
ProtonBase1 小时前
如何从 0 到 1 ,打造全新一代分布式数据架构
java·网络·数据库·数据仓库·分布式·云原生·架构
云和数据.ChenGuang6 小时前
Django 应用安装脚本 – 如何将应用添加到 INSTALLED_APPS 设置中 原创
数据库·django·sqlite
woshilys7 小时前
sql server 查询对象的修改时间
运维·数据库·sqlserver