ABAP 中 SQL 语句的性能优化

原文链接:[Performance optimization of SQL statements in ABAP](Performance optimization of SQL statements in ABAP)

作者:community.sap.com/t5/user/vie...

SQL 语句语法是否合理,将直接影响程序的执行效率。

在本文中,我解释了数据库优化器的概念,以及它如何解析 SQL 查询以创建评估路径。我举例说明了如何通过更改 WHERE 子句来提高 ABAP 中 SQL 语句的性能。我假设读者熟悉交易 SE30ST05

注: 以下 SQL 追踪来自 DB2 数据库,其他数据库的结果可能有所不同。

SELECT 语句

先来考虑简单的 SELECT 语句:

SQL 复制代码
SELECT * from MARA where MATNR = 'ACFBAS2' 。

这里数据库有 3 种可能的搜索策略来查找记录,选择哪种路径由优化器决定:

  1. 搜索整个表格
  2. 使用主索引进行搜索
  3. 使用搜索的二级索引

优化器:优化器是对 SQL 语句进行解析,并考虑每个访问路径(全量扫描、主索引、二级索引)并制定 SQL 语句执行计划的数据库程序。有 2 种类型的优化器:

  • RBO(Rule-Based Optimization) 基于规则的优化器:根据可用的索引和 WHERE 子句中使用的字段,RBO 创建其执行计划。WHERE 子句中的大多数导入条件是使用 EQUALS 条件指定并出现在索引中的字段。
  • CBO(Cost-Based Optimization) 基于成本的优化器:为了创建 SQL 语句的执行计划,CBO 考虑与 RBO 相同的标准
  1. 表大小:对于小表,CBO 决定避免索引,转而使用更有效的全表扫描。大型表更有可能通过索引进行访问。

  2. 索引字段的选择性:给定索引字段的选择性是当 SQL 语句在该字段中搜索特定非重复值时读取的表部分的平均大小。例如:

    • 如果 VBAK 表中有 200 个订单类型和 10,000 个实际订单,则字段文档类型的选择性为 10,000 除以 200,即 10,000 的 50 或 2%。字段中非重复值的数量越大,优化程序使用基于该字段的索引的可能性就越大。
  3. 物理存储:优化器考虑必须在硬盘驱动器上物理读取多少索引数据块或页面。需要读取的物理内存越多,使用索引的可能性就越小。

示例 1: 创建数据库表 ZKUM_TEST ,包含字段从 F1、F2、...一直到 F10。并且 F1、F2、F3、F4 是表的关键字段,即主键。现在考虑以下 select 查询语句:

  1. SELECT * FROM ZKUM_TEST INTO TABLE ITAB WHERE F2 = '1' and F3 = '3':这里优化器不会使用主索引进行搜索操作,因为 WHERE 子句中缺少 F1 字段。因此,将执行全表扫描。
  2. SELECT * FROM ZKUM_TEST INTO TABLE ITAB WHERE F1 = '2' and F2 = '3' : 如果字段 F1 和 F2 上没有二级索引,则优化器将选择主索引,因为 4 个字段中的两个字段是匹配的。
  3. SELECT * FROM ZKUM_TEST INTO TABLE ITAB WHERE F1 <> '2' and F2 = '3' and F3 = '3':此处优化器不会使用主索引,因为运算符 NOT 用于比较列 F1。如果列 F2、F3 上有任何二级索引,则优化器将使用二级索引。

为了对最佳访问做出正确的决策,CBO 需要有关表和索引大小的统计信息。必须定期生成统计信息以使其保持最新状态。标准 SAP 报告可用于生成这些统计信息(事务 DB13)。CBO 的优点是它的灵活性,因为它考虑了指定字段的选择性。

例如:

SQL 复制代码
SELECT * FROM MARA WHERE MANDT= '020' and BISMT = 'a1'. 

在这里,CBO 知道 MANDT 只包含一个不同的值将决定全表扫描,而 RBO 将选择效率较低的索引范围扫描。

标题 处理方法 索引字段 运行时间/微秒
无表格访问统计数据/无基于字段 BISMT 的二级索引 索引范围扫描 MANDT,MATNR 3,500,000
有表格访问统计数据/无基于字段 BISMT 的二级索引 全表扫描 N/A 500,000
使用表格访问统计/基于字段 BISMT 的二级索引 索引范围扫描 BISMT 3000

高效 SQL 编程的规则:

  1. SQL 语句必须具有仅传输最少量数据的 WHERE 子句。避免在 WHERE 子句中使用 NOT 条件。这些不能通过索引进行处理。避免使用相同的选择,您可以在 ST05 中识别这一点
  2. 使用 SELECT column1 column2 ....,而不是 SELCT *
  3. WHERE 条件必须简单;否则,优化程序可能会决定错误的索引或根本不使用索引。如果 WHERE 子句使用 ANDEQUALS 条件指定索引的每个字段,则该 WHERE 子句处理起来就很简单
  4. LOOP 中使用 FOR ALL ENTRIES 而不是 SELECT。 使用 FAE 时,请确保内表不为空,并且不得包含重复项 。根据数据库系统的不同,数据库接口将 FAE 转换为各种 SQL 语句,它可以使用 INUNION 运算符转换为 SQL 语句

多表查询大量数据,建立数据库视图

如果要从多个表中提取大量数据,则最好创建数据库视图,而不是使用 FOR ALL ENTRIES

来看示例 2

运行以下 select 语句的 SQL trace:

  1. 直接查询整个表
SQL 复制代码
SELECT mandt vbeln posnr FROM vbap INTO TABLE itab WHERE erdat in s_erdat.
  1. FOR ALL ENTRIES IN
ABAP 复制代码
IF itab[] IS NOT INITIAL.
  select mandt vbeln posnr from vbap into table itab
      FOR ALL ENTRIES IN itab
    where vbeln = itab-vbeln and posnr = itab-posnr.
ENDIF.

第一种 SQL

对于第一个 SELECT,将扫描整个表,因为找不到匹配的索引(因为 WHERE 子句中提到的列既不是主键的一部分,也不是第二个键的一部分)。要了解数据库的命中数,请单击菜单路径 "Trace List" 中的 "Summarize Trace by SQL statements" ,这将对相同的选择进行分组。

对于第一次选择仅一次命中到 DB,而对于第二次选择 3210 次命中到 DB,有关详细信息,请参见下文。"Executions" 列为您提供了对数据库的命中数。

单击"解释"按钮,首先选择以了解访问路径,即使用哪个索引。这里没有找到匹配的索引,所以整个表将被扫描。

在下面的屏幕截图中,您只能看到一列,即 MANDT 与原始索引的 3 列匹配。所以整个表都被扫描了。

第二种 SQL

第二选择: 内部表 ITAB[] 有大约 32K 条记录。

在 SQL 跟踪中,单击第二个 SQL 的显示,您会注意到 FOR ALL ENTRIES IN TABLE 构造已更改为 " UNION ALL",并且 DB 优化器会进行子查询。

DB 的点击数是 3210

UNION : UNION 命令用于从两个表中选择相关信息,与 JOIN 命令非常相似。 UNION ALL 命令等于 UNION 命令,只不过 UNION ALL 选择所有值。

在上面的示例中,单个 SELECT 语句被优化的数据库拆分为多个选择,并带有子查询(即 UNION ALL)并传递到数据库。

在此示例中,ITAB[] 大约有 32K 条记录,每次选择都会传递 10 条记录,从而向数据库发送 3210 次命中。

第一个 SELECT 的总持续时间:2,167,664 微秒

第二个 SELECT 的总持续时间:6,739,983 微秒。

现在更改第二种 SQL 方式,这样优化器就不会使用带有 UNION ALL 构造的子查询。POSNR 被注释掉。

修改后的代码:

sql 复制代码
SORT ITAB BY VBELN.
DELETE ADJACENT DUPLICATES FROM ITAB COMPARING VBELN.  

VBELNITAB[] 进行排序。然后从 ITAB 中删除相邻的重复项,比较 VBELN

SQL 复制代码
SELECT mandt vbeln posnr FROM vbap INTO TABLE itab
      for ALL ENTRIES IN itab
    where vbeln = itab-vbeln ." and posnr = itab-posnr.

DB 的命中次数下降到 189 次,总持续时间下降到 453,742 微秒。

单击"解释"按钮以了解访问路径。在本例中,数据库优化器将 FOR ALL ENTRIES IN TABLE 构造更改为 'IN' 子句。

通过减少对数据库的命中数,在每个选择中传递一堆值(大约 30 个合约编号)。优化器仍然使用主键索引作为 WHERE 子句中提到的两列 (MANDTVEBLN) 与表的主键匹配。

跟踪中显示的运行时不仅包括数据库提供请求的数据所需的时间,还包括在数据库和应用程序服务器之间传输数据所需的时间。如果网络通信中出现性能问题,则 SQL 语句的运行时间会增加。因此,在得出任何结论之前,请进行 2-3 次跟踪。

如上所述,如果 SELECT 语句存在任何性能问题,首先需要检查的是 WHERE caluse,修改 WHERE 子句并查看优化器是否使用任何可用索引。如果没有使用可用索引,则最好创建二级索引以提高性能。

请注意:当您从多个表中提取数据时,最好使用 INNER JOIN 联接这些表,因为 INNER JOIN 的性能优于 FOR ALL ENTRIES

示例3:

考虑以下选择:这里我正在拉取合同行项 VC 特征详细信息。VBAP-CUOBJ 是行项 VC 配置编号

SQL 复制代码
SELECT a~instance  b~in_recno b~symbol_id c~atinn  c~atwrt  c~atflv
    FROM ( ( ibin AS a INNER JOIN ibinvalues AS b ON a~in_recno = b~in_recno )
    INNER JOIN ibsymbol AS c  ON b~symbol_id = c~symbol_id )
    INTO TABLE gt_char_val
    FOR ALL ENTRIES IN l_cabn
    WHERE a~instance = it_vbap-cuobj AND c~atinn = l_cabn-atinn .

与其在 IBIN、IBINVALUES 和 IBSYMBOL 上编写 3 个单独的 select 语句,不如使用 INNER JOIN

SQL 跟踪显示每个表都使用可用索引,即表 IBIN 索引"C",表 IBINVALUES 索引"SW0"使用,表 IBSYMBOL 索引"SEL"使用,因此查询性能良好。

因此,当您在多个表上使用 INNER JOIN 时,最好进行 SQL 跟踪并检查访问路径以查看是否使用了可用的索引。

索引:IBIN~C is on columns MANDT and INSTANCE

索引:SWO on table IBINVALUES is on columns MANDT, IN_RECNO, SYMBOL_ID, ATZIS and ASSTYP

索引: SEL on table IBSYMBOL is on columns MANDT, ATINN, ATWRT, ATFLV and SYMBOL_ID

创建二级索引

创建或更改二级索引可能会提高或降低 SQL 语句的性能,在创建二级索引之前,请检查是否可以以可以使用可用索引的方式重写 ABAP 程序。

切勿在没有 SAP 建议的情况下在 SAP 基础表上创建二级索引,例如 D010、D020、DD*。

创建二级索引的规则

  1. 在索引中仅包含选择性字段:ex customer number、Material、Contract number
  2. 在索引中包含少量字段
  3. 将选择性字段放置在索引的左侧
  4. 索引应该是不连续性的,避免创建两个或多个具有基本相同字段的索引
  5. 为每个表创建几个索引

如果通过特定索引字段进行搜索的 SQL 语句将导致读取整个索引的 5% 到 10% 以上,则基于成本的优化程序不会认为该索引有用,而是选择全表扫描作为最有效的访问方法。

示例 1 :考虑下面的 select 语句

sql 复制代码
SELECT MANDT VBELN posnr FROM VBAp into TABLE ITAB  WHERE VGBEL IN s_VGBEL.

VGBEL(参考文档编号)不是主键的一部分,也不存在二级主键。在 DB2 中选择上述 SQL 跟踪。

SQL 跟踪:

如果查看访问路径,则未找到匹配的索引。因此,我们需要检查在 MANDTVGBEL 上创建二级索引是否是一个好的候选项。

现在转到事务 DB05 并输入表名并指定要在其上创建索引的文件,然后单击执行:

如果表大小较大,请选中"在后台提交分析"复选框。

输出:

输出显示 VBAP 有 317,272 行,这 317K 条记录中的不同 VGBEL 值的数目为 5644。

在这 5644 个不同的值中

  • 4430 参考文档在 VBAP 表中的条目少于 10 个:表示如果您从 VBAP 中使用 count(*) 并输入这些 4430 参考文档编号中的任何一个,则行项目计数将在 1 到 10 之间。
  • 1099 参考文档的条目介于 10 到 100 之间
  • 101 参考文档的条目介于 101 到 1000 之间
  • 13 份参考文件的条目在 1001 到 10,000 之间
  • 只有 1 个参考文档在 VBAP 中的记录超过 100,000:表示如果您从 VBAP 执行 count(*) 并输入此 ref. doc 数字,您将获得行项目计数 >= 100,001

DB05 分析表明,大多数参考文档的记录计数在 1 到 1000 之间,这意味着这是创建辅助 INDEX 的良好候选项。

现在,在 SE11 事务中的 MANDTVGBEL 列的 VBAP 上创建一个辅助索引,然后再次运行相同的查询。

SQL 跟踪显示运行时间从 2,165,827 毫秒下降到 2,026 毫秒,差异巨大。

示例 2 :如果要在表的多个列上创建索引,那么根据 DB05 中的这些索引字段分析该表。考虑以下对 TCURR 表的分析。

我在 TCURR 表上运行 DB05 的字段 MANDTKURSTFCURRTCURRGDATU

解释:

  • KURST 行:TCURR 表中不同汇率类型的编号为 22 种,其中 2 种的记录计数在 1 到 10 之间。 三种速率类型的记录计数介于 11 到 100 之间。14 种汇率类型计数在 101 到 100 之间不等
  • FCURR 行:KURSTFCURR 组合的不同值数为 1014,其中 785 个组合的记录计数在 1 到 10 之间。KURSTFCURR 的 137 个组合的记录数量在 11 到 100 之间不等
  • TCURR 行:KURSTFCURRTCURR 组合的非重复值数为 1019,其中 790 个组合的记录计数在 1 到 10 之间不等
  • GDATU 行:KURSTFCURR & TCURRGDATU 组合的不同值数为 35K,即每个组合只有一条记录,因为它们是主要索引的一部分。

这样,DB05 分析将帮助您确定为创建索引而选择的列是否适合。如果您在 DB05 中看到更多记录的记录计数> 10,000,那么它就不是创建索引的良好候选者。

考虑下面对 T-code 列上的 BKPF 表的分析,这绝对不是一个好的候选项:

一些有用的 T-code 代码:

  • DB20
  • ST04, ST05
  • SE30
  • DB05
  • DB02:对于缺失的索引
  • SE14:重新创建索引
相关推荐
初晴~1 小时前
【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
java·数据库·redis·分布式·后端·spring·
盖世英雄酱581361 小时前
InnoDB 的页分裂和页合并
数据库·后端
小_太_阳1 小时前
Scala_【2】变量和数据类型
开发语言·后端·scala·intellij-idea
直裾2 小时前
scala借阅图书保存记录(三)
开发语言·后端·scala
星就前端叭2 小时前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
小林coding3 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
AI理性派思考者3 小时前
【保姆教程】手把手教你在Linux系统搭建早期alpha项目cysic的验证者&证明者
后端·github·gpu
从善若水4 小时前
【2024】Merry Christmas!一起用Rust绘制一颗圣诞树吧
开发语言·后端·rust
机器之心4 小时前
终于等来能塞进手机的文生图模型!十分之一体量,SnapGen实现百分百的效果
人工智能·后端
机器之心4 小时前
首次!大模型自动搜索人工生命,做出AI科学家的Sakana AI又放大招
人工智能·后端