SELECT * FROM dd_view_name INTO TABLE lt_data WHERE ...
💡数据库视图封装 JOIN 逻辑。使用它们简化 ABAP 代码中重复的 JOIN。
INNER JOIN(内连接)
仅返回两表中匹配的行
最常见的 JOIN 类型
1 2 3 4 5 6 7
SELECT s~carrid, s~connid, p~cityfrom, p~cityto INTO TABLE lt_result FROM sflight AS s INNER JOIN spfli AS p ON s~carrid = p~carrid AND s~connid = p~connid WHERE s~carrid = 'LH'.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
表 A(左) 表 B(右) +----+------+ +----+-------+ | K1 | A1 | | K1 | B1 | +----+------+ +----+-------+ | 01 | X | | 01 | Y | | 02 | Z | | 03 | W | +----+------+ +----+-------+
SELECT s~carrid, s~connid, p~cityfrom INTO TABLE lt_result FROM sflight AS s LEFT OUTER JOIN spfli AS p ON s~carrid = p~carrid AND s~connid = p~connid.
1 2 3 4 5 6 7
LEFT OUTER JOIN 结果: +----+------+----+-------+ | K1 | A1 | K1 | B1 | +----+------+----+-------+ | 01 | X | 01 | Y | ← 找到匹配 | 02 | Z | -- | NULL | ← 无匹配,右侧为 NULL +----+------+----+-------+
FROM sflight AS s INNER JOIN spfli AS p ON s~carrid = p~carrid " alias~column 表示法
表示法
含义
alias~column
限定来自特定表的列
~column
快捷方式(如果不歧义)
裸 column
如果在多个表中则歧义
子查询
标量子查询
在 WHERE 子句中返回单个值:
1 2 3
SELECT * FROM sflight INTO TABLE lt_flights WHERE price > ( SELECT AVG(price) FROM sflight ).
IN 子查询
返回一组值:
1 2 3
SELECT * FROM sflight INTO TABLE lt_result WHERE carrid IN ( SELECT carrid FROM scarr WHERE currcode = 'EUR' ).
子查询类型
返回值
配合使用
标量
单个值
=、<、> 等
集合
多个值
IN、NOT IN
📌子查询绕过 SAP 表缓冲(被缓冲视为 Native SQL 路径)。
FOR ALL ENTRIES
将数据库表与内表连接:
1 2 3 4 5 6 7 8 9
" 关键:先检查驱动表! IF lt_keys IS NOT INITIAL. SELECT carrid, connid, price INTO TABLE lt_result FROM sflight FOR ALL ENTRIES IN lt_keys WHERE carrid = lt_keys-carrid AND connid = lt_keys-connid. ENDIF.
FOR ALL ENTRIES 规则
规则
详情
检查 IS NOT INITIAL
空驱动表读取整个数据库表
自动去重
结果自动删除重复行
不适用于大数据量
驱动表很大时性能下降
不能使用 LIKE、NOT IN
WHERE 中受限的运算符
⛔如果驱动表(内表)为空,FOR ALL ENTRIES 的行为如同没有 WHERE 子句,读取整个数据库表。始终先检查 itab IS NOT INITIAL!
性能提示
1 2 3 4 5 6 7 8 9 10 11 12
" 不好:驱动表未排序、有重复 SELECT * FROM sflight FOR ALL ENTRIES IN lt_dirty WHERE carrid = lt_dirty-carrid.
" 好:先精简驱动表 SORT lt_dirty BY carrid. DELETE ADJACENT DUPLICATES FROM lt_dirty COMPARING carrid.
SELECT * FROM sflight FOR ALL ENTRIES IN lt_dirty WHERE carrid = lt_dirty-carrid.
性能反模式:循环中的 SELECT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
" 反模式:循环中的 SELECT LOOP AT lt_flights INTO ls_flight. SELECT SINGLE cityfrom INTO ls_flight-cityfrom FROM spfli WHERE carrid = ls_flight-carrid AND connid = ls_flight-connid. MODIFY lt_flights FROM ls_flight. ENDLOOP.
" 正确:使用 JOIN 或预缓冲 SELECT s~carrid, s~connid, p~cityfrom INTO TABLE lt_result FROM sflight AS s INNER JOIN spfli AS p ON s~carrid = p~carrid AND s~connid = p~connid.
方式
数据库往返次数
性能
循环中的 SELECT
n + 1(每行一次)
很差
JOIN
1
优秀
FOR ALL ENTRIES
1(或少量)
小集合时良好
预缓冲 + READ
1 + 内存查找
良好
JOIN 比较
特性
INNER JOIN
LEFT OUTER JOIN
未匹配的左表行
丢弃
保留(右侧 = NULL)
右侧限制
无
仅一个表
绕过缓冲
是
是
ON 条件
必需
必需
默认 JOIN 类型
INNER(关键字可选)
必须指定 OUTER
考试/测试模式
关键词 / 短语
答案
“FOR ALL ENTRIES 空表”
读取整个数据库表 – 始终检查 IS NOT INITIAL
“LEFT OUTER JOIN 右侧限制”
右侧只允许一个表
“JOIN 和表缓冲”
JOIN 绕过 SAP 表缓冲
“别名列语法”
alias~column 消除歧义
“FOR ALL ENTRIES 去重处理”
结果集自动删除重复项
“循环中的 SELECT”
反模式;使用 JOIN 或预缓冲
“精简驱动表”
FOR ALL ENTRIES 前 SORT + DELETE ADJACENT DUPLICATES
CASE lo_descr->kind. WHEN cl_abap_typedescr=>kind_elem. " 基本类型 WHEN cl_abap_typedescr=>kind_struct. " 结构 WHEN cl_abap_typedescr=>kind_table. " 内表 ENDCASE.
CL_ABAP_ELEMDESCR
描述基本(内置)类型。
1 2 3 4 5 6 7 8
DATA: lo_elem TYPE REF TO cl_abap_elemdescr. lo_elem ?= cl_abap_typedescr=>describe_by_data( lv_string ).
" 计算行数 SELECT COUNT(*) INTO lv_count FROM sflight WHERE carrid = 'LH'.
" 多个聚合 SELECT carrid, COUNT(*) AS cnt, SUM(price) AS total, AVG(price) AS avg_price, MAX(price) AS highest INTO TABLE lt_summary FROM sflight GROUP BY carrid.
GROUP BY
将共享列值的行分组
SELECT 中每个非聚合列必须出现在 GROUP BY 中
在 SELECT 列表之前评估
1 2 3 4
SELECT carrid, connid, COUNT(*) AS cnt INTO TABLE lt_counts FROM sflight GROUP BY carrid, connid.
⚠️如果你选择了不在 GROUP BY 中且未聚合的列,结果未定义且取决于数据库。
HAVING
过滤组(不是单个行)
在 GROUP BY 之后、SELECT 之前应用
条件中可使用聚合函数
1 2 3 4 5
SELECT carrid, COUNT(*) AS cnt INTO TABLE lt_result FROM sflight GROUP BY carrid HAVING COUNT(*) > 10. " 仅行数 > 10 的组
子句
过滤对象
应用时机
WHERE
单个行
分组之前
HAVING
组
分组之后
ORDER BY
语法
行为
ORDER BY col1 col2
按指定列排序
ORDER BY 1 3
按列位置排序(从 1 开始)
ORDER BY col1 ASCENDING col2 DESCENDING
显式方向
1 2 3 4
SELECT carrid, price INTO TABLE lt_sorted FROM sflight ORDER BY carrid ASCENDING price DESCENDING.
💡ORDER BY 绕过 SAP 表缓冲。对于缓冲表的排序输出,改为在 ABAP 中对内表排序。
DISTINCT
从结果集中删除重复行
应用于整行,而非单个列
1 2 3 4
" 唯一城市对 SELECT DISTINCT cityfrom, cityto INTO TABLE lt_routes FROM spfli.
+----------------------------------+ | 主程序 | | INCLUDE zprog_top. | ← Top include(数据声明) | INCLUDE zprog_cls. | ← 类定义 | INCLUDE zprog_pbo. | <-- PBO 模块(对话程序) | INCLUDE zprog_pai. | <-- PAI 模块(对话程序) | INCLUDE zprog_f01. | <-- FORM 例程 | INCLUDE zprog_f02. | <-- FORM 例程(按功能分组) +----------------------------------+
Include
用途
Top include(_top)
全局数据声明、TYPES、CONSTANTS
类 include(_cls)
局部类定义和实现
Form include(_f01、_f02、…)
按功能分组的模块化 FORM 例程
💡Top include 模式集中了所有数据声明。这防止重复声明并提供程序数据的清晰概览。
模块化建议
建议
原因
优先使用方法而非 FORM
方法在类中:更好的封装、继承、接口
使用类(局部或全局)
面向对象设计更易维护
保持模块化单元简短
更容易测试和理解
每个单元单一职责
每个方法/FORM 应做一件事
避免过程中使用全局变量
显式传递参数
错误处理
级别
方法
数据库操作
立即检查 sy-subrc
基于类的异常
TRY ... CATCH ... ENDTRY 配合异常类
断言
ASSERT 用于开发者不变量
函数模块
通过 EXCEPTIONS 或基于类的异常处理
返回值
使用返回参数,而不仅是 sy-subrc
1 2 3 4 5 6 7 8 9 10 11 12
" 好:立即检查 sy-subrc SELECT SINGLE * FROM sflight INTO ls_flight WHERE carrid = 'LH'. IF sy-subrc <> 0. " 处理错误 ENDIF.
" 好:基于类的异常处理 TRY. lo_handler->process_order( ls_order ). CATCH cx_static_check INTO lx_error. MESSAGE lx_error->get_text( ) TYPE 'S' DISPLAY LIKE 'E'. ENDTRY.