`
Jack Wu
  • 浏览: 894842 次
  • 来自: ...
社区版块
存档分类
最新评论

ABAP--在查询条件只包含部分索引字段时,如何使用索引

阅读更多
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 put them into a range
* table. We'll put the range table into the select. So long as the
* number of company codes is not too great, this will speed up the
* select on BKPF.

  RANGES: r_bukrs FOR bkpf-bukrs.

* Preliminary selects
  r_bukrs-option = 'EQ'.
  r_bukrs-sign   = 'I'.
  SELECT bukrs
    FROM t001
    INTO r_bukrs-low.
    APPEND r_bukrs.
  ENDSELECT.

  REFRESH bkpf_int.
  SELECT  bukrs belnr gjahr blart budat
    FROM  bkpf
    INTO  TABLE bkpf_int
    WHERE bukrs IN r_bukrs
    AND   belnr = p_belnr
    AND   gjahr = p_gjahr.

  REFRESH: bkpf_int,
           r_bukrs.
  GET RUN TIME FIELD start.

* Use these selects in comparison
  r_bukrs-option = 'EQ'.
  r_bukrs-sign   = 'I'.
  SELECT bukrs
    FROM t001
    INTO r_bukrs-low.
    APPEND r_bukrs.
  ENDSELECT.

  SELECT bukrs belnr gjahr blart budat
    FROM  bkpf
    INTO  TABLE bkpf_int
    WHERE bukrs IN r_bukrs
    AND   belnr = p_belnr
    AND   gjahr = p_gjahr.

  GET RUN TIME FIELD end.
  dif = end - start.
  WRITE: /001 'Time for third select',
              '(indexed by selecting from the check table)',
          067  ':', dif, 'microseconds'.
  SKIP 1.

ENDFORM.                    " indexed_select_bkpf

*&---------------------------------------------------------------------*
*&      Form  unindexed_select_bkpf_2
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM unindexed_select_bkpf_2.

* Now we'll get another group of records from BKPF. There is a
* secondary index on BKPF with fields BUKRS, BSTAT and BUDAT.
* We're going to leave BSTAT out of the select and use
* BUKRS and BUDAT from the first document we selected.

* Preliminary select - to be ignored.
  REFRESH bkpf_int.
  SELECT  bukrs belnr gjahr blart budat
    FROM  bkpf
    INTO  TABLE bkpf_int
    WHERE bukrs = p_bukrs
    AND   budat = bkpf_wa-budat.

  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   budat = bkpf_wa-budat.

  GET RUN TIME FIELD end.
  dif = end - start.
  WRITE: /001 'Time for fourth (partially indexed) select',
          067  ':', dif, 'microseconds'.

ENDFORM.                    " unindexed_select_bkpf_2

*&---------------------------------------------------------------------*
*&      Form  indexed_select_bkpf_2
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM indexed_select_bkpf_2.

* Finally, we will use the domain values of BSTAT in the select. If you
* are sure that you know all of the values, you can hardcode them. I am
* using all of the possible values of BSTAT so that all three selects
* return the same data. In practice, we would probably narrow it down.
* But since normal FI postings have BSTAT = SPACE, this won't help
* unless we are looking for 'not' normal documents.

  RANGES: r_bstat FOR bkpf-bstat.
  DATA  : d1 LIKE dd07l-domvalue_l,
          d2 LIKE dd07l-domvalue_h.

* Hardcoded values
* Preliminary select.
  REFRESH bkpf_int.
  SELECT  bukrs belnr gjahr blart budat
    FROM  bkpf
    INTO  TABLE bkpf_int
    WHERE bukrs EQ p_bukrs
    AND   bstat IN (' ', 'A', 'B', 'D', 'M', 'S', 'V', 'W', 'Z')
    AND   budat = bkpf_wa-budat.

  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 bukrs EQ p_bukrs
    AND   bstat IN (' ', 'A', 'B', 'D', 'M', 'S', 'V', 'W', 'Z')
    AND   budat = bkpf_wa-budat.

  GET RUN TIME FIELD end.
  dif = end - start.
  WRITE: /001 'Time for fifth select',
              '(indexed by hardcoding the domain values)',
          067  ':', dif, 'microseconds'.

* After an upgrade, the values in a domain may change. It's safer to
* retrieve all of the values from the data dictionary. There is a very
* slight increase in the time.

  r_bstat-sign   = 'I'.
  SELECT  domvalue_l domvalue_h
    FROM  dd07l
    INTO  (d1, d2)
    WHERE domname  = 'BSTAT'
    AND   as4local = 'A'.
    IF d2 IS INITIAL.
      r_bstat-option = 'EQ'.
      r_bstat-low    = d1.
      CLEAR r_bstat-high.
    ELSE.
      r_bstat-option = 'BT'.
      r_bstat-low    = d1.
      r_bstat-high   = d2.
    ENDIF.
    APPEND r_bstat.
  ENDSELECT.

  REFRESH bkpf_int.
  SELECT  bukrs belnr gjahr blart budat
    FROM  bkpf
    INTO  TABLE bkpf_int
    WHERE bukrs EQ p_bukrs
    AND   bstat IN r_bstat
    AND   budat = bkpf_wa-budat.

  REFRESH: bkpf_int,
           r_bstat.

* Use this select in comparisons.
  GET RUN TIME FIELD start.

  r_bstat-sign   = 'I'.
  SELECT  domvalue_l domvalue_h
    FROM  dd07l
    INTO  (d1, d2)
    WHERE domname  = 'BSTAT'
    AND   as4local = 'A'.
    IF d2 IS INITIAL.
      r_bstat-option = 'EQ'.
      r_bstat-low    = d1.
      CLEAR r_bstat-high.
    ELSE.
      r_bstat-option = 'BT'.
      r_bstat-low    = d1.
      r_bstat-high   = d2.
    ENDIF.
    APPEND r_bstat.
  ENDSELECT.

  REFRESH bkpf_int.
  SELECT  bukrs belnr gjahr blart budat
    FROM  bkpf
    INTO  TABLE bkpf_int
    WHERE bukrs EQ p_bukrs
    AND   bstat IN r_bstat
    AND   budat = bkpf_wa-budat.

  GET RUN TIME FIELD end.
  dif = end - start.
  WRITE: /001 'Time for sixth select',
              '(indexed by selecting the domain values)',
          067  ':', dif, 'microseconds'.

ENDFORM.                    " indexed_select_bkpf_2
    

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 don’t 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.

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 don’t work very well together. Once seems to be enough.

https://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/wlg/4370

分享到:
评论

相关推荐

    ABAP-EXCEL 编程的统一接口的 使用方法

    在本文中,我们将探讨如何使用ABAP-Excel编程的统一接口来满足三种常见的Excel输出需求:在特定位置输出单字段、在指定行下方插入多行以及删除原有模板的某些行。 首先,我们需要理解基本概念。在ABAP中,要操作...

    SAP ABAP开发系统字段

    这些字段由ABAP运行时环境自动填充,主要用于查询和监控系统状态,大多数情况下是只读的,以确保数据的完整性。下面我们将深入探讨一些重要的系统字段及其用途。 1. **sy-repid**:这是一个特殊的系统字段,用于...

    SAP ABAP 屏幕字段输入帮助实现

    通过以上介绍可以看出,SAP ABAP提供了多种方法来为屏幕字段设置输入帮助,包括利用数据字典中的默认设置、手动分配已有的搜索帮助,以及编写自定义代码。合理利用这些方法可以极大地提高应用程序的可用性和用户体验...

    SAP 索引 index

    - 在表的列中,如果“KEY”字段下有“X”,则表示该列为索引的一部分。 - 主索引通常出现在表的初始列中,它主要由表的主键字段组成。 - 示例:表MARA的主索引。 - **次级索引(Secondary Index):** - 次级索引...

    ABAP调优-代码优化.docx

    1. **优化Open SQL**:在编写Open SQL时,应尽量利用索引来提升查询性能。对于标准内表,可以使用`READ TABLE ... WITH TABLE KEY`配合二分查找,提高查询效率。 2. **内表类型选择**:理解不同内表类型的时间...

    ABAP SQL使用技巧

    在需要选择特定字段而非全部字段时,应使用 **SELECT ... INTO**,而不是 **SELECT * INTO**。前者只提取所需字段,从而减少数据传输量: ```abap SELECT DOMNAME FROM DD01L INTO DD01L_WA-DOMNAME WHERE DOMNAME ...

    初学ABAP的教程

    定义筛选条件,允许用户在执行程序时输入过滤参数,格式如下: ```abap SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-000. PARAMETERS: p1 type string. SELECTION-SCREEN END OF BLOCK b1. ``` - `...

    ABAP SQL 语句 动态 操作

    在ABAP编程中,SQL语句的动态操作是高级编程技术的一个重要方面,它允许根据程序运行时的条件和用户输入来构建和执行SQL查询。本程序“ZSQL_EXEC_RICOO”展示了如何使用ABAP实现动态SQL操作,包括SELECT、UPDATE、...

    SAP数据表大全ABAP数据表大全

    在SAP系统中,ABAP(Advanced Business Application Programming)是一种编程语言,用于开发和定制企业级应用程序。ABAP数据表是SAP系统的核心组成部分,它们存储了系统的业务数据和配置信息。本资料集“SAP数据表...

    abap 维护表视图

    例如,选择性地包含字段、使用合适的索引、避免全表扫描等。 - 使用`OpenSQL`(SAP的SQL语法扩展)可以在ABAP程序中高效地操作表视图。 6. **源码和工具** - 标签中提到的"源码"可能是指ABAP程序中操作表视图的...

    ABAP报表性能优化注意事项

    - **利用索引**:在连接表时,优先使用具有索引的关键字段,例如`vttp~vbeln = lips~vbeln`,这样可以提高查询速度。 2. **构建RANGE再执行SQL**: - 当查询条件与实际过滤字段不匹配时,可以通过先构建RANGE来...

    ABAP程序性能优化

    - 索引中的条件最好只包含`=`或`IN`操作符,避免使用其他逻辑条件。 2. **缓冲利用**: - 多数SAP表具有缓冲机制,可以显著提高性能。通过查询表元数据确定是否支持缓冲。 - 避免编写导致无法利用缓冲的SQL语句...

    ABAP 常用语句 数据读取 删除 修改 等语句 常用语句ABAP

    根据提供的标题、描述、标签及部分内容,我们可以提炼出与ABAP语言相关的多个核心知识点,包括数据读取、删除、修改等基本操作。下面将逐一详细展开这些知识点。 ### ABAP 数据读取 #### 1. 表的初始化与赋值 - **...

    abap性能优化

    4. **使用索引**:为频繁查询的字段创建索引,可以极大地提高数据检索速度,但同时要考虑维护索引所需的额外开销。 5. **程序结构优化**:良好的程序结构有助于性能提升。避免过深的嵌套循环,合理使用模块化和面向...

    ABAP 个人笔记-我的SAP学习

    T-CODE、ABAP Editor 的使用、处理块类型、应用程序类型、语句链、数据内部定义、结构体赋值、ABAP 数据表达式、用户主记录设置、内表管理、动态数据对象、子程序定义、package 命名规则、数据字典、select 查询、...

    SAP-ABAP语法详解教材

    5. **数据库通信**:掌握使用OPEN SQL和SELECT语句进行数据库查询,以及使用数据库视图、索引和事务代码来优化数据访问。 6. **程序结构与调试**:理解模块化编程,包括函数组、类和接口。同时,学习如何使用SAP的...

    SAP ABAP ALV分页显示

    此函数接收多个参数,包括字段目录(`IT_FIELDCAT`)、回调程序ID(`GD_REPID`)以及输出表(`T_OUTTAB`)等。 4. **分页控制**:使用`SET_PF_STATUS1`和`USER_COMMAND`两种事件处理器来控制分页行为。当用户点击“上一页...

    ABAP动态内表使用的例子

    由于动态内表没有固定的数据结构,因此不能像普通内表那样直接使用索引或字段符号来进行访问。为此,我们需要使用`FIELD-SYMBOLS`来引用动态内表中的行。 **代码示例:** ```abap FIELD-SYMBOLS &lt;fs_dyn_table&gt; ...

    ABAP代码性能指导

    - **减少特定条件使用**:在WHERE条件中尽量减少使用、&gt;、&lt;、&like%等条件。 ##### 6. 使用BINARY SEARCH - **优化循环中的表读取**:在循环中使用`READ TABLE... WITH KEY... BINARY SEARCH`进行表读取,但前提是...

    ABAP常见面试问题

    一个典型的 ABAP 程序结构包括以下几个部分: - **PROGRAM-ID**:定义程序标识符。 - **PARAMETERS**:定义输入参数。 - **TYPES**:定义数据类型。 - **DATA**:定义变量。 - **SELECTION-SCREEN**:定义屏幕选择。...

Global site tag (gtag.js) - Google Analytics